diff --git a/encryption/LICENSE b/encryption/LICENSE new file mode 100644 index 000000000..d60c31a97 --- /dev/null +++ b/encryption/LICENSE @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + 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 + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/encryption/MANIFEST.in b/encryption/MANIFEST.in new file mode 100644 index 000000000..178716d12 --- /dev/null +++ b/encryption/MANIFEST.in @@ -0,0 +1,11 @@ +include LICENSE +include README +include Makefile +include deluge.desktop +include deluge.xpm +include msgfmt.py +recursive-include libtorrent/ * +recursive-include glade/ *.glade +recursive-include pixmaps/ *.png +recursive-include plugins/ * +recursive-include po/ * diff --git a/encryption/Makefile b/encryption/Makefile new file mode 100644 index 000000000..98f268653 --- /dev/null +++ b/encryption/Makefile @@ -0,0 +1,15 @@ +# +# Makefile for Deluge +# + +PREFIX = /usr + + +all: + python setup.py build + +install: + python setup.py install --prefix=$(PREFIX) + +clean: + python setup.py clean; rm -rf ./build diff --git a/encryption/README b/encryption/README new file mode 100644 index 000000000..2319ae9c8 --- /dev/null +++ b/encryption/README @@ -0,0 +1,57 @@ +Deluge BitTorrent Client + +Authors: + Zach Tibbitts, aka zachtib + Alon Zakai, aka kripkenstein + +Homepage: http://deluge-torrent.org + +========================== +Installation Instructions: +========================== + +First, make sure you have the proper bulid +dependencies installed. On a normal Debian +or Ubuntu system, those dependencies are: + +python-all-dev +python-all +python-support +libboost-dev +libboost-thread-dev +libboost-date-time-dev +libboost-filesystem-dev +libboost-serialization-dev +libboost-program-options-dev +libboost-regex-dev +zlib1g-dev + +But the names of the packages may vary +depending on your OS / distro. + +Once you have the needed libraries installed, +build Deluge by running: + + python setup.py build + +You shouldn't get any errors. Then run, as +root (or by using sudo): + + python setup.py install + +and Deluge will be installed. + +You can then run Deluge by executing: + + deluge + +Notes: + +1) On some distributions, boost libraries are + renamed to have "-mt" at the end (boost_thread_mt + instead of boost_thread, for example), the "mt" + indicating "multithreaded". In some cases it + appears the distros lack symlinks to connect + things. The solution is to either add symlinks + from the short names to those with "-mt", or to + alter setup.py to look for the "-mt" versions. diff --git a/encryption/deluge.desktop b/encryption/deluge.desktop new file mode 100644 index 000000000..30000b61b --- /dev/null +++ b/encryption/deluge.desktop @@ -0,0 +1,12 @@ +[Desktop Entry] +Version=1.0 +Encoding=UTF-8 +Name=Deluge BitTorrent Client +Comment=Bittorrent client written in Python/PyGTK +Exec=deluge +Icon=deluge.xpm +Terminal=false +Type=Application +Categories=Application;Network +StartupNotify=true +MimeType=application/x-bittorrent; diff --git a/encryption/deluge.xpm b/encryption/deluge.xpm new file mode 100644 index 000000000..5c3d1eae7 --- /dev/null +++ b/encryption/deluge.xpm @@ -0,0 +1,415 @@ +/* XPM */ +static char * deluge_torrent_xpm[] = { +"32 32 380 2", +" c None", +". c #8893A8", +"+ c #8994A8", +"@ c #B0B7C5", +"# c #919BAE", +"$ c #737F98", +"% c #A3ABBB", +"& c #8E98AD", +"* c #AEB5C4", +"= c #858FA5", +"- c #5E6D89", +"; c #929CB0", +"> c #8794A9", +", c #8592AA", +"' c #919FB5", +") c #A5B0C3", +"! c #7688A4", +"~ c #7687A1", +"{ c #8A99B1", +"] c #768AA7", +"^ c #7E92AE", +"/ c #8397B3", +"( c #92A4BD", +"_ c #96A8C0", +": c #5B7295", +"< c #8B9DB7", +"[ c #6A83A4", +"} c #728BAD", +"| c #7791B3", +"1 c #7B95B6", +"2 c #7C96B8", +"3 c #95ABC6", +"4 c #829CBC", +"5 c #45618C", +"6 c #7F96B3", +"7 c #6483A9", +"8 c #6384AC", +"9 c #6B8BB3", +"0 c #7091B8", +"a c #7495BC", +"b c #7596BD", +"c c #7496BD", +"d c #95AECC", +"e c #6F91B9", +"f c #6081A9", +"g c #3C6292", +"h c #365989", +"i c #4B6B97", +"j c #5676A1", +"k c #5F81AB", +"l c #6E93BF", +"m c #7097C2", +"n c #7297C3", +"o c #8FADCE", +"p c #618AB7", +"q c #476D9D", +"r c #5A779F", +"s c #1B4075", +"t c #1F4377", +"u c #365785", +"v c #4A6791", +"w c #4D6B94", +"x c #5D80AA", +"y c #6C93BF", +"z c #779DC6", +"A c #84A6CB", +"B c #5382B6", +"C c #3A659A", +"D c #5C789F", +"E c #224579", +"F c #1E4277", +"G c #274A7C", +"H c #2E4F7F", +"I c #335483", +"J c #44628D", +"K c #557197", +"L c #526E95", +"M c #506F99", +"N c #668FBD", +"O c #7FA3CA", +"P c #7299C4", +"Q c #36639A", +"R c #4F6D97", +"S c #345484", +"T c #305181", +"U c #42618A", +"V c #4E6992", +"W c #4A678F", +"X c #4B6992", +"Y c #4B6994", +"Z c #5F7A9E", +"` c #5E779C", +" . c #597398", +".. c #527099", +"+. c #618ABA", +"@. c #84A6CD", +"#. c #5D89BB", +"$. c #375A89", +"%. c #4C6992", +"&. c #3A5986", +"*. c #536E94", +"=. c #577197", +"-. c #6F92BB", +";. c #759ECA", +">. c #739CCA", +",. c #759DC9", +"'. c #6880A4", +"). c #5C7699", +"!. c #4F6F98", +"~. c #618DBD", +"{. c #81A4CA", +"]. c #4B7CB3", +"^. c #274F84", +"/. c #5A759B", +"(. c #375784", +"_. c #577196", +":. c #5A7498", +"<. c #6F89AB", +"[. c #8BADD1", +"}. c #7EA5D0", +"|. c #7FA6D0", +"1. c #7CA4CF", +"2. c #7CA4CE", +"3. c #789FC7", +"4. c #6984A7", +"5. c #5A7398", +"6. c #5076A5", +"7. c #6791C0", +"8. c #7198C3", +"9. c #2D5081", +"0. c #25477A", +"a. c #4C6890", +"b. c #627A9D", +"c. c #657DA1", +"d. c #A0BBD7", +"e. c #89AED6", +"f. c #8BAFD7", +"g. c #8BB0D7", +"h. c #8AAED6", +"i. c #86ACD4", +"j. c #82A8D2", +"k. c #7CA3CF", +"l. c #749DC8", +"m. c #6984A6", +"n. c #567298", +"o. c #5786BA", +"p. c #779CC6", +"q. c #5583B7", +"r. c #295086", +"s. c #2C4E7F", +"t. c #345483", +"u. c #5B7498", +"v. c #5D7699", +"w. c #93AAC4", +"x. c #9EBDDD", +"y. c #96B8DD", +"z. c #97B9DE", +"A. c #94B7DC", +"B. c #90B3DA", +"C. c #8AAFD6", +"D. c #83A9D2", +"E. c #7AA2CE", +"F. c #6F95BF", +"G. c #667E9F", +"H. c #577FAF", +"I. c #5585B8", +"J. c #759AC5", +"K. c #3F6FA7", +"L. c #254A80", +"M. c #5C769C", +"N. c #3C5B87", +"O. c #60789C", +"P. c #B0C3D8", +"Q. c #A1C0E3", +"R. c #A3C2E4", +"S. c #A3C3E5", +"T. c #A2C2E4", +"U. c #9EBFE2", +"V. c #98BADE", +"W. c #91B4DA", +"X. c #88AED6", +"Y. c #759ECB", +"Z. c #6885AD", +"`. c #5780B0", +" + c #5383B8", +".+ c #7299C3", +"++ c #4675AB", +"@+ c #395A89", +"#+ c #46648F", +"$+ c #3E5D88", +"%+ c #617A9C", +"&+ c #637B9D", +"*+ c #BFD1E4", +"=+ c #ADCBEA", +"-+ c #B0CDEB", +";+ c #AFCCEB", +">+ c #ACCAE9", +",+ c #A7C6E6", +"'+ c #9FC0E2", +")+ c #8DB1D8", +"!+ c #82A9D2", +"~+ c #77A0CC", +"{+ c #6A94C4", +"]+ c #5F8DBF", +"^+ c #5C89BA", +"/+ c #5C85B4", +"(+ c #3F5E8B", +"_+ c #3B5A86", +":+ c #647C9E", +"<+ c #61799D", +"[+ c #C1D2E4", +"}+ c #BBD5F1", +"|+ c #BCD6F2", +"1+ c #BAD5F1", +"2+ c #B5D1EE", +"3+ c #AECBEA", +"4+ c #A5C4E5", +"5+ c #9BBCE0", +"6+ c #84AAD3", +"7+ c #78A0CC", +"8+ c #6B96C6", +"9+ c #5F8CBF", +"0+ c #5282B8", +"a+ c #4F7FB4", +"b+ c #668CB7", +"c+ c #43618D", +"d+ c #325382", +"e+ c #5E779B", +"f+ c #5F789A", +"g+ c #BACADC", +"h+ c #C9E0F9", +"i+ c #C7DFF8", +"j+ c #C3DCF6", +"k+ c #BCD7F2", +"l+ c #B3CFED", +"m+ c #A8C7E7", +"n+ c #9DBEE1", +"o+ c #6A96C5", +"p+ c #5D8BBE", +"q+ c #5081B7", +"r+ c #497BB2", +"s+ c #698DB7", +"t+ c #45638E", +"u+ c #2E5080", +"v+ c #25487B", +"w+ c #506B93", +"x+ c #647B9E", +"y+ c #9DAFC7", +"z+ c #D6E9FD", +"A+ c #D1E8FE", +"B+ c #CAE2FA", +"C+ c #C1DAF5", +"D+ c #A9C8E8", +"E+ c #6894C4", +"F+ c #5A89BC", +"G+ c #4D7EB5", +"H+ c #4577AF", +"I+ c #6B8DB5", +"J+ c #3B6090", +"K+ c #3D5D8A", +"L+ c #3C5B89", +"M+ c #1D4276", +"N+ c #647C9C", +"O+ c #657D9E", +"P+ c #C2D4E6", +"Q+ c #D3E9FF", +"R+ c #CEE5FC", +"S+ c #C2DBF5", +"T+ c #80A7D1", +"U+ c #729CC9", +"V+ c #6491C2", +"W+ c #5686BA", +"X+ c #497BB3", +"Y+ c #5580B3", +"Z+ c #5B7EAA", +"`+ c #335686", +" @ c #4D6993", +".@ c #26497B", +"+@ c #4C6990", +"@@ c #6B82A2", +"#@ c #7D92B0", +"$@ c #C1D4E9", +"%@ c #C0DAF4", +"&@ c #B2CFED", +"*@ c #7BA3CE", +"=@ c #6F99C7", +"-@ c #6390C0", +";@ c #5886B9", +">@ c #4A7BB2", +",@ c #698CB7", +"'@ c #4C6F9C", +")@ c #284C7F", +"!@ c #577299", +"~@ c #2F517F", +"{@ c #536E93", +"]@ c #7086A5", +"^@ c #7B8FAC", +"/@ c #9FB3CD", +"(@ c #B8D2EE", +"_@ c #91B5DB", +":@ c #85ABD4", +"<@ c #7CA3CD", +"[@ c #729AC8", +"}@ c #6791C1", +"|@ c #5A87BA", +"1@ c #5280B4", +"2@ c #7A97BB", +"3@ c #416290", +"4@ c #2F5080", +"5@ c #4F6B91", +"6@ c #687F9F", +"7@ c #7B8FAB", +"8@ c #8396B0", +"9@ c #8AA1BF", +"0@ c #8CACCE", +"a@ c #8FB2D8", +"b@ c #86AAD2", +"c@ c #7DA3CC", +"d@ c #749BC7", +"e@ c #6993C1", +"f@ c #5B88B9", +"g@ c #7999C0", +"h@ c #587AA5", +"i@ c #21416E", +"j@ c #516D96", +"k@ c #415F8A", +"l@ c #567196", +"m@ c #637B9E", +"n@ c #6F84A3", +"o@ c #778BA7", +"p@ c #758BAA", +"q@ c #7490B3", +"r@ c #749BC5", +"s@ c #6992C0", +"t@ c #799DC4", +"u@ c #7291B6", +"v@ c #46658C", +"w@ c #264878", +"x@ c #4F6C94", +"y@ c #42608C", +"z@ c #294B7D", +"A@ c #4A6790", +"B@ c #567095", +"C@ c #597599", +"D@ c #587398", +"E@ c #536F96", +"F@ c #4F6D95", +"G@ c #7690B4", +"H@ c #7090B7", +"I@ c #4A6991", +"J@ c #2B4871", +"K@ c #41608C", +"L@ c #59749A", +"M@ c #234679", +"N@ c #214578", +"O@ c #3A5A87", +"P@ c #47648F", +"Q@ c #667EA2", +"R@ c #7188A9", +"S@ c #4B6892", +"T@ c #395985", +"U@ c #1C314F", +"V@ c #2E4F7D", +"W@ c #395986", +"X@ c #526E96", +"Y@ c #58739A", +"Z@ c #5B759B", +"`@ c #6A82A5", +" # c #627CA0", +".# c #365887", +"+# c #324B69", +"@# c #344B69", +"## c #34537D", +"$# c #294C7C", +"%# c #315383", +"&# c #40618D", +"*# c #40638F", +"=# c #3B5C87", +"-# c #36537A", +";# c #2E4156", +" ", +" . ", +" + @ # ", +" $ % & * = ", +" - ; > , ' ) ! ", +" ~ { ] ^ / ( _ ", +" : < [ } | 1 2 3 4 ", +" 5 6 7 8 9 0 a b c d e ", +" 7 f g h i j k l m n o p ", +" q r s s s t u v w x y z A B ", +" C D E s F G H I J K L M N O P ", +" Q R S s T U V W X Y Z ` ...+.@.#. ", +" $.%.t &.*.=.r -.;.>.,.0 '.).!.~.{.]. ", +" ^./.F (._.:.<.[.}.|.}.1.2.3.4.5.6.7.8. ", +" 9.v 0.a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q. ", +" r.v s.t.u.v.w.x.y.z.z.A.B.C.D.E.F.G.H.I.J.K. ", +" L.M.s N.b.O.P.Q.R.S.T.U.V.W.X.|.Y.Z.`. +.+++ ", +" @+#+s $+%+&+*+=+-+;+>+,+'+z.)+!+~+{+]+ +^+/+ ", +" (+S s _+:+<+[+}+|+1+2+3+4+5+B.6+7+8+9+0+a+b+ ", +" c+T s d+e+f+g+h+i+j+k+l+m+n+W.6+~+o+p+q+r+s+ ", +" t+u+s v+w+x+y+z+A+B+C+2+D+n+B.D.Y.E+F+G+H+I+J+ ", +" K+L+s M+N.N+O+P+Q+R+S+2+m+5+)+T+U+V+W+X+Y+Z+ ", +" `+ @s s .@+@@@#@$@R+%@&@4+z.e.*@=@-@;@>@,@'@ ", +" )@!@t s s ~@{@]@^@/@(@=+'+_@:@<@[@}@|@1@2@3@ ", +" S #+s s s 4@5@6@7@8@9@0@a@b@c@d@e@f@g@h@ ", +" i@j@u s s s G k@l@m@n@o@p@q@a r@s@t@u@v@ ", +" w@x@y@s s s s z@_+A@B@C@D@E@F@G@H@I@ ", +" J@K@L@y@M@s s N@z@S O@P@Q@R@S@T@ ", +" U@V@W@X@L@Y@Z@L@c.`@ #v .#+# ", +" @###$#%#&#*#=#-#;# ", +" ", +" "}; diff --git a/encryption/gettextize.sh b/encryption/gettextize.sh new file mode 100755 index 000000000..c592e2e7e --- /dev/null +++ b/encryption/gettextize.sh @@ -0,0 +1,2 @@ +#!/bin/sh +xgettext -f po/POTFILES.in -o po/deluge.pot diff --git a/encryption/glade/aboutdialog.glade b/encryption/glade/aboutdialog.glade new file mode 100644 index 000000000..21c46bd89 --- /dev/null +++ b/encryption/glade/aboutdialog.glade @@ -0,0 +1,37 @@ + + + + + + 5 + True + True + True + False + + + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 2 + + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + GTK_BUTTONBOX_END + + + False + GTK_PACK_END + + + + + + diff --git a/encryption/glade/delugegtk.glade b/encryption/glade/delugegtk.glade new file mode 100644 index 000000000..197083d5d --- /dev/null +++ b/encryption/glade/delugegtk.glade @@ -0,0 +1,1108 @@ + + + + + + True + Deluge + + + + + + True + 4 + 3 + + + True + + + True + GTK_POLICY_AUTOMATIC + GTK_POLICY_AUTOMATIC + + + True + True + True + True + False + + + + + + False + False + + + + + 300 + True + True + + + True + True + GTK_POLICY_AUTOMATIC + GTK_POLICY_AUTOMATIC + + + True + + + True + 10 + 12 + 4 + + + + + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 2 + 2 + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + + + 1 + 2 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 0 + <b>Progress:</b> + True + + + 1 + 2 + GTK_FILL + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 0 + <b>Name:</b> + True + + + GTK_FILL + + + + + True + + + 1 + 2 + 1 + 2 + + + + + + 4 + 2 + + + + + True + + + 3 + 4 + 11 + 12 + + + + + True + 0 + <b>Estimated Time Remaining:</b> + True + + + 2 + 3 + 11 + 12 + + + + + True + 0 + + + 3 + 4 + 5 + 6 + + + + + True + 0 + + + 1 + 2 + 5 + 6 + + + + + True + 0 + <b>Peers:</b> + True + + + 2 + 3 + 5 + 6 + + + + + True + 0 + <b>Seeders:</b> + True + + + 5 + 6 + + + + + True + 0 + <b>Total Size:</b> + True + + + 2 + 3 + + + + + True + 0 + <b>Total Downloaded:</b> + True + + + 3 + 4 + + + + + True + 0 + <b>Downloaded this session:</b> + True + + + 7 + 8 + + + + + True + 0 + + + 1 + 2 + 2 + 3 + + + + + True + 0 + + + 1 + 2 + 3 + 4 + + + + + True + 0 + + + 1 + 2 + 7 + 8 + + + + + True + 0 + <b>Tracker:</b> + True + + + 8 + 9 + + + + + True + 0 + <b>Tracker Response:</b> + True + + + 9 + 10 + + + + + True + 0 + <b>Tracker Status:</b> + True + + + 10 + 11 + + + + + True + 0 + + + 1 + 2 + 10 + 11 + + + + + True + 0 + <b>Next Announce:</b> + True + + + 2 + 3 + 10 + 11 + + + + + True + 0 + + + 3 + 4 + 10 + 11 + + + + + True + 0 + <b>Pieces:</b> + True + + + 2 + 3 + 2 + 3 + + + + + True + 0 + <b>Total Uploaded:</b> + True + + + 2 + 3 + 3 + 4 + + + + + True + 0 + + + 3 + 4 + 2 + 3 + + + + + True + 0 + + + 3 + 4 + 3 + 4 + + + + + True + 0 + <b>Share Ratio:</b> + True + + + 2 + 3 + 6 + 7 + + + + + True + 0 + <b>Uploaded This Session:</b> + True + + + 2 + 3 + 7 + 8 + + + + + True + 0 + + + 3 + 4 + 6 + 7 + + + + + True + 0 + + + 3 + 4 + 7 + 8 + + + + + True + 0 + + + 1 + 4 + 8 + 9 + + + + + True + 0 + + + 1 + 4 + 9 + 10 + + + + + True + 0 + <b>Use compact storage allocation:</b> + True + + + 11 + 12 + + + + + True + 0 + + + 1 + 2 + 11 + 12 + + + + + True + 0 + <b>Download Rate:</b> + True + + + 4 + 5 + + + + + True + 0 + + + 1 + 2 + 4 + 5 + + + + + True + 0 + <b>Upload Rate:</b> + True + + + 2 + 3 + 4 + 5 + + + + + True + 0 + + + 3 + 4 + 4 + 5 + + + + + + + + + False + + + + + True + Details + + + tab + False + False + + + + + True + GTK_POLICY_AUTOMATIC + GTK_POLICY_AUTOMATIC + + + True + + + + + 1 + False + + + + + True + Peers + + + tab + 1 + False + False + + + + + True + GTK_POLICY_AUTOMATIC + GTK_POLICY_AUTOMATIC + + + True + True + + + + + 2 + False + + + + + True + Files + + + tab + 2 + False + False + + + + + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + GTK_POLICY_AUTOMATIC + GTK_POLICY_AUTOMATIC + + + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + False + False + + + + + 3 + False + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + Log + + + tab + 3 + False + False + + + + + False + False + + + + + 3 + 2 + 3 + + + + + True + + + 3 + 3 + 4 + + + + + + True + + + True + _Torrent + True + + + + + True + Add Torrent + True + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + gtk-add + 1 + + + + + + + True + Add Torrent from URL + True + + + + + + True + Remove Torrent + True + + + + gtk-remove + 1 + + + + + + + True + Clear Completed + True + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + gtk-clear + 1 + + + + + + + True + + + + + True + gtk-quit + True + True + + + + + + + + + + True + _Edit + True + + + True + + + True + gtk-preferences + True + True + + + + + + True + Plugins + True + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + gtk-disconnect + 1 + + + + + + + + + + + True + _View + True + + + True + + + True + Toolbar + True + True + + + + + + True + Details + True + True + + + + + + True + Columns + True + + + True + + + True + Size + True + True + + + + + + True + Status + True + True + + + + + + True + Seeders + True + True + + + + + + True + Peers + True + True + + + + + + True + Download + True + True + + + + + + True + Upload + True + True + + + + + + True + Time Remaining + True + True + + + + + + True + Share Ratio + True + True + + + + + + + + + + + + + + True + _Help + True + + + + + True + gtk-about + True + True + + + + + + + + + + 3 + + + + + + True + GTK_TOOLBAR_BOTH_HORIZ + False + + + 2 + 3 + 1 + 2 + + GTK_FILL + + + + + True + GTK_TOOLBAR_BOTH_HORIZ + + + 1 + 2 + 1 + 2 + GTK_FILL + + + + + True + False + + + True + Add Torrent + Add Torrent + True + gtk-add + + + + False + + + + + True + Remove Torrent + Remove Torrent + True + gtk-remove + + + + False + + + + + True + Clear Finished Torrents + Clear Finished + True + gtk-clear + + + + False + + + + + True + + + False + False + + + + + True + Start / Pause + Start + True + gtk-media-play + + + + False + + + + + True + Queue Torrent Up + Move Up + True + gtk-go-up + + + + False + + + + + True + Queue Torrent Down + Move Down + True + gtk-go-down + + + + False + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + + + False + False + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + Preferences + gtk-preferences + + + + False + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + Plugins + gtk-disconnect + + + + False + + + + + 1 + 2 + + GTK_FILL + + + + + + diff --git a/encryption/glade/dgtkpopups.glade b/encryption/glade/dgtkpopups.glade new file mode 100644 index 000000000..2ac092090 --- /dev/null +++ b/encryption/glade/dgtkpopups.glade @@ -0,0 +1,249 @@ + + + + + + True + + + True + Size + True + True + + + + + + True + Status + True + True + + + + + + True + Seeders + True + True + + + + + + True + Peers + True + True + + + + + + True + Download Speed + True + True + + + + + + True + Upload Speed + True + True + + + + + + True + Time Remaining + True + True + + + + + + True + Share Ratio + True + True + + + + + + Remove Torrent + True + GDK_WINDOW_TYPE_HINT_DIALOG + True + True + False + + + True + + + True + + + True + 0 + <b>Are you sure you want to remove the selected torrent(s) from Deluge?</b> + True + + + 10 + + + + + True + Delete downloaded files + True + + + 1 + + + + + True + True + GTK_JUSTIFY_CENTER + + + 2 + + + + + False + False + 1 + + + + + True + GTK_BUTTONBOX_END + + + True + gtk-no + True + + + + + True + gtk-yes + True + 1 + + + 1 + + + + + False + GTK_PACK_END + + + + + + + True + + + True + Show/Hide + True + + + + + + True + Add a Torrent... + True + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + gtk-add + 1 + + + + + + + True + Clear Finished + True + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + gtk-clear + 1 + + + + + + + True + + + + + True + gtk-preferences + True + True + + + + + + True + Plugins + True + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + gtk-execute + 1 + + + + + + + True + + + + + True + gtk-quit + True + True + + + + + diff --git a/encryption/glade/plugin_dialog.glade b/encryption/glade/plugin_dialog.glade new file mode 100644 index 000000000..d9d680e30 --- /dev/null +++ b/encryption/glade/plugin_dialog.glade @@ -0,0 +1,114 @@ + + + + + + 480 + 5 + Plugin Manager + 583 + 431 + True + GDK_WINDOW_TYPE_HINT_DIALOG + True + True + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_ENTER_NOTIFY_MASK + 2 + + + True + False + + + True + True + + + True + + + + + True + + + True + False + GTK_WRAP_WORD + False + + + 10 + + + + + True + GTK_BUTTONBOX_SPREAD + + + True + False + gtk-preferences + True + + + + + + False + 1 + + + + + 10 + 1 + + + + + False + + + + + True + Plugins + + + tab + False + False + + + + + 1 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_ENTER_NOTIFY_MASK + GTK_BUTTONBOX_END + + + True + gtk-close + True + + + + + False + GTK_PACK_END + + + + + + diff --git a/encryption/glade/preferences_dialog.glade b/encryption/glade/preferences_dialog.glade new file mode 100644 index 000000000..dc9fde33a --- /dev/null +++ b/encryption/glade/preferences_dialog.glade @@ -0,0 +1,1431 @@ + + + + + + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 5 + Deluge Preferences + GTK_WIN_POS_CENTER_ON_PARENT + 500 + GDK_WINDOW_TYPE_HINT_DIALOG + True + True + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 2 + + + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + True + + + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + GTK_POLICY_NEVER + GTK_POLICY_AUTOMATIC + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + GTK_RESIZE_QUEUE + GTK_SHADOW_NONE + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 0 + GTK_SHADOW_NONE + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 12 + + + True + 2 + 2 + + + True + Ask me where to save each download + True + + + 2 + + + + + True + Save all downloads to: + True + radio_ask_save + + + 1 + 2 + + + + + True + GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER + + + 1 + 2 + 1 + 2 + + + + + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + <b>Download Location</b> + True + + + label_item + + + + + False + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 0 + GTK_SHADOW_NONE + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 12 + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 1 + 2 + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 0 + Maximum simultaneous active torrents: + + + + + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 1 + 0 0 100 1 10 10 + True + GTK_UPDATE_IF_VALID + + + 1 + 2 + + + + + + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + <b>Torrents</b> + True + + + label_item + + + + + False + False + 1 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 0 + GTK_SHADOW_NONE + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 12 + + + True + Use compact storage allocation + True + + + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + <b>Compact Allocation</b> + True + + + label_item + + + + + False + 2 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 0 + GTK_SHADOW_NONE + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 12 + + + True + 2 + 2 + + + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + Queue torrents to bottom when they begin seeding + True + + + 2 + + + + + True + Stop seeding torrents when their share ratio reaches: + True + + + 1 + 2 + + + + + True + True + 5 + 0.5 + 0 0 10 0.050000000000000003 10 9 + 2 + True + + + 1 + 2 + 1 + 2 + + + + + + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + <b>Seeding</b> + True + + + label_item + + + + + False + False + 3 + + + + + + + + + + + + False + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + Downloads + + + tab + False + False + + + + + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + GTK_POLICY_AUTOMATIC + GTK_POLICY_AUTOMATIC + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + GTK_RESIZE_QUEUE + GTK_SHADOW_NONE + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + <b>Warning - Changes to these settings will only be applied the next time Deluge is restarted</b> + True + GTK_JUSTIFY_CENTER + True + + + False + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 0 + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 12 + + + True + 2 + 4 + + + + + + True + Test Port + + + 3 + 4 + 1 + 2 + + + + + True + 0000 + + + 2 + 3 + 1 + 2 + + + + + True + Active port: + + + 1 + 2 + 1 + 2 + + + + + True + True + 0.5 + 0 0 65535 1 10 10 + + + 3 + 4 + + + + + True + True + 0.5 + 0 0 65535 1 10 10 + + + 1 + 2 + + + + + True + to: + + + 2 + 3 + + + + + True + Try from: + + + + + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + <b>TCP Port</b> + True + + + label_item + + + + + False + 1 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 0 + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 12 + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 2 + 2 + + + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 1 + 0 0 100 1 10 10 + + + 1 + 2 + 1 + 2 + GTK_FILL + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + Max number of DHT connections: + + + 1 + 2 + + + + + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + Enable Mainline DHT + True + + + 2 + + + + + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + <b>DHT</b> + True + + + label_item + + + + + False + 2 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 0 + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 12 + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + + + True + False + + UPnP + True + True + + + + + True + False + GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + NAT-PMP + True + True + + + 1 + + + + + True + False + GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + UT PeX + True + True + + + 2 + + + + + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + <b>Network extras</b> - Always on + True + + + label_item + + + + + + + False + False + 3 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 0 + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 12 + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 4 + 1 + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 0 + GTK_SHADOW_NONE + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 12 + + + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + Prefer to encrypt the entire stream + True + + + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + <b>Preference</b> + True + + + label_item + + + + + 3 + 4 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 0 + GTK_SHADOW_NONE + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 12 + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + + + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + Handshake + True + True + + + + + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + Either + True + chk_level_plaintext + + + 1 + + + + + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + Full Stream + True + chk_level_plaintext + + + 2 + + + + + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + <b>Level</b> + True + + + label_item + + + + + 2 + 3 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 0 + GTK_SHADOW_NONE + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 12 + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + + + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + Disabled + True + True + + + + + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + Enabled + True + chk_encout_disabled + + + 1 + + + + + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + Forced + True + chk_encout_disabled + + + 2 + + + + + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + <b>Outbound</b> + True + + + label_item + + + + + 1 + 2 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 0 + GTK_SHADOW_NONE + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 12 + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + + + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + Disabled + True + True + + + + + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + Enabled + True + chk_encin_disabled + + + 1 + + + + + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + Forced + True + chk_encin_disabled + + + 2 + + + + + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + <b>Inbound</b> + True + + + label_item + + + + + + + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + <b>Encryption</b> + True + + + label_item + + + + + False + False + 4 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + + + + + + + + + 5 + + + + + + + + + 1 + False + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + Network + + + tab + 1 + False + False + + + + + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + GTK_POLICY_AUTOMATIC + GTK_POLICY_AUTOMATIC + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + GTK_RESIZE_QUEUE + GTK_SHADOW_NONE + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 0 + GTK_SHADOW_NONE + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 12 + + + True + 5 + 3 + + + True + + + 2 + 3 + 2 + 3 + + + + + True + + + 2 + 3 + 1 + 2 + + + + + True + KB/s + + + 2 + 3 + 4 + 5 + + + + + + True + True + 10 + 1 + -1 -1 1024 1 10 10 + + + 1 + 2 + 4 + 5 + + + + + + True + 0 + Maximum Upload Rate: + + + 4 + 5 + + + + + True + True + 10 + 1 + -1 -1 100 1 10 10 + + + 1 + 2 + 1 + 2 + + + + + + True + 0 + Maximum Connections + + + 1 + 2 + + + + + True + 0 + Upload Slots + + + 2 + 3 + + + + + True + 0 + Maximum Download Rate: + + + 3 + 4 + + + + + True + True + 10 + 1 + -1 -1 100 1 10 10 + + + 1 + 2 + 2 + 3 + + + + + + True + True + 10 + 1 + -1 -1 2048 1 10 10 + + + 1 + 2 + 3 + 4 + + + + + + True + KB/s + + + 2 + 3 + 3 + 4 + + + + + + True + <i>(-1 is unlimited)</i> + True + + + 3 + + + + + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + <b>Bandwidth Usage</b> + True + + + label_item + + + + + False + + + + + + + + + + + + + + + 2 + False + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + Bandwidth + + + tab + 2 + False + False + + + + + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + GTK_POLICY_AUTOMATIC + GTK_POLICY_AUTOMATIC + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + GTK_RESIZE_QUEUE + GTK_SHADOW_NONE + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 0 + GTK_SHADOW_NONE + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 12 + + + True + 3 + 2 + + + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + False + + + 1 + 2 + 2 + 3 + + + + + True + Minimize to tray on close + True + + + 2 + 1 + 2 + 12 + + + + + True + Enable system tray icon + True + True + + + 2 + + + + + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + Password protect system tray + True + + + 2 + 3 + + + + + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + <b>System Tray</b> + True + + + label_item + + + + + False + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 0 + GTK_SHADOW_NONE + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 12 + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 1 + 3 + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 0 + GUI update interval + + + + + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + False + 0.5 0.5 5 0.5 0.5 1 + 1 + True + + + 1 + 2 + GTK_FILL + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + seconds + + + 2 + 3 + + + + + + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + <b>Performance</b> + True + + + label_item + + + + + False + False + 1 + + + + + + + + + + + + 3 + False + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + Other + + + tab + 3 + False + False + + + + + 1 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + GTK_BUTTONBOX_END + + + True + gtk-cancel + True + + + + + True + gtk-ok + True + 1 + + + 1 + + + + + False + GTK_PACK_END + + + + + + diff --git a/encryption/glade/torrent_menu.glade b/encryption/glade/torrent_menu.glade new file mode 100644 index 000000000..34204e06e --- /dev/null +++ b/encryption/glade/torrent_menu.glade @@ -0,0 +1,187 @@ + + + + + + True + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + StartPause + True + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + gtk-missing-image + 1 + + + + + + + True + Update Tracker + True + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + gtk-refresh + 1 + + + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + Queue + True + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + Top + True + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + gtk-goto-top + 1 + + + + + + + True + Up + True + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + gtk-go-up + 1 + + + + + + + True + Down + True + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + gtk-go-down + 1 + + + + + + + True + Bottom + True + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + gtk-goto-bottom + 1 + + + + + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + gtk-sort-ascending + 1 + + + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + Upload + True + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + menuitem9 + True + + + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + gtk-go-up + 1 + + + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + Download + True + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + menuitem11 + True + + + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + gtk-go-down + 1 + + + + + + diff --git a/encryption/libtorrent/include/libtorrent/alert.hpp b/encryption/libtorrent/include/libtorrent/alert.hpp new file mode 100755 index 000000000..b6b6711dc --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/alert.hpp @@ -0,0 +1,164 @@ +/* + +Copyright (c) 2003, Arvid Norberg, Daniel Wallin +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_ALERT_HPP_INCLUDED +#define TORRENT_ALERT_HPP_INCLUDED + +#include +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include + +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/time.hpp" +#include "libtorrent/config.hpp" + +#ifndef TORRENT_MAX_ALERT_TYPES +#define TORRENT_MAX_ALERT_TYPES 15 +#endif + +namespace libtorrent { + + class TORRENT_EXPORT alert + { + public: + enum severity_t { debug, info, warning, critical, fatal, none }; + + alert(severity_t severity, const std::string& msg); + virtual ~alert(); + + // a timestamp is automatically created in the constructor + ptime timestamp() const; + + std::string const& msg() const; + + severity_t severity() const; + + virtual std::auto_ptr clone() const = 0; + + private: + std::string m_msg; + severity_t m_severity; + ptime m_timestamp; + }; + + class TORRENT_EXPORT alert_manager + { + public: + alert_manager(); + ~alert_manager(); + + void post_alert(const alert& alert_); + bool pending() const; + std::auto_ptr get(); + + void set_severity(alert::severity_t severity); + bool should_post(alert::severity_t severity) const; + + private: + std::queue m_alerts; + alert::severity_t m_severity; + mutable boost::mutex m_mutex; + }; + + struct TORRENT_EXPORT unhandled_alert : std::exception + { + unhandled_alert() {} + }; + + namespace detail { + + struct void_; + + template + void handle_alert_dispatch( + const std::auto_ptr& alert_, const Handler& handler + , const std::type_info& typeid_ + , BOOST_PP_ENUM_BINARY_PARAMS(TORRENT_MAX_ALERT_TYPES, T, *p)) + { + if (typeid_ == typeid(T0)) + handler(*static_cast(alert_.get())); + else + handle_alert_dispatch(alert_, handler, typeid_ + , BOOST_PP_ENUM_SHIFTED_PARAMS( + TORRENT_MAX_ALERT_TYPES, p), (void_*)0); + } + + template + void handle_alert_dispatch( + const std::auto_ptr& alert_ + , const Handler& handler + , const std::type_info& typeid_ + , BOOST_PP_ENUM_PARAMS(TORRENT_MAX_ALERT_TYPES, void_* BOOST_PP_INTERCEPT)) + { + throw unhandled_alert(); + } + + } // namespace detail + + template + struct TORRENT_EXPORT handle_alert + { + template + handle_alert(const std::auto_ptr& alert_ + , const Handler& handler) + { + #define ALERT_POINTER_TYPE(z, n, text) (BOOST_PP_CAT(T, n)*)0 + + detail::handle_alert_dispatch(alert_, handler, typeid(*alert_) + , BOOST_PP_ENUM(TORRENT_MAX_ALERT_TYPES, ALERT_POINTER_TYPE, _)); + + #undef ALERT_POINTER_TYPE + } + }; + +} // namespace libtorrent + +#endif // TORRENT_ALERT_HPP_INCLUDED + diff --git a/encryption/libtorrent/include/libtorrent/alert_types.hpp b/encryption/libtorrent/include/libtorrent/alert_types.hpp new file mode 100755 index 000000000..e3e23ad05 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/alert_types.hpp @@ -0,0 +1,300 @@ +/* + +Copyright (c) 2003, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_ALERT_TYPES_HPP_INCLUDED +#define TORRENT_ALERT_TYPES_HPP_INCLUDED + +#include "libtorrent/alert.hpp" +#include "libtorrent/torrent_handle.hpp" +#include "libtorrent/socket.hpp" +#include "libtorrent/peer_connection.hpp" +#include "libtorrent/config.hpp" + +namespace libtorrent +{ + struct TORRENT_EXPORT torrent_alert: alert + { + torrent_alert(torrent_handle const& h, alert::severity_t s + , std::string const& msg) + : alert(s, msg) + , handle(h) + {} + + torrent_handle handle; + }; + + struct TORRENT_EXPORT tracker_alert: torrent_alert + { + tracker_alert(torrent_handle const& h + , int times + , int status + , std::string const& msg) + : torrent_alert(h, alert::warning, msg) + , times_in_row(times) + , status_code(status) + {} + + virtual std::auto_ptr clone() const + { return std::auto_ptr(new tracker_alert(*this)); } + + int times_in_row; + int status_code; + }; + + struct TORRENT_EXPORT tracker_warning_alert: torrent_alert + { + tracker_warning_alert(torrent_handle const& h + , std::string const& msg) + : torrent_alert(h, alert::warning, msg) + {} + + virtual std::auto_ptr clone() const + { return std::auto_ptr(new tracker_warning_alert(*this)); } + }; + + + + struct TORRENT_EXPORT tracker_reply_alert: torrent_alert + { + tracker_reply_alert(torrent_handle const& h + , int np + , std::string const& msg) + : torrent_alert(h, alert::info, msg) + , num_peers(np) + {} + + int num_peers; + + virtual std::auto_ptr clone() const + { return std::auto_ptr(new tracker_reply_alert(*this)); } + }; + + struct TORRENT_EXPORT tracker_announce_alert: torrent_alert + { + tracker_announce_alert(torrent_handle const& h, std::string const& msg) + : torrent_alert(h, alert::info, msg) + {} + + virtual std::auto_ptr clone() const + { return std::auto_ptr(new tracker_announce_alert(*this)); } + }; + + struct TORRENT_EXPORT hash_failed_alert: torrent_alert + { + hash_failed_alert( + torrent_handle const& h + , int index + , std::string const& msg) + : torrent_alert(h, alert::info, msg) + , piece_index(index) + { assert(index >= 0);} + + virtual std::auto_ptr clone() const + { return std::auto_ptr(new hash_failed_alert(*this)); } + + int piece_index; + }; + + struct TORRENT_EXPORT peer_ban_alert: torrent_alert + { + peer_ban_alert(tcp::endpoint const& pip, torrent_handle h, std::string const& msg) + : torrent_alert(h, alert::info, msg) + , ip(pip) + {} + + virtual std::auto_ptr clone() const + { return std::auto_ptr(new peer_ban_alert(*this)); } + + tcp::endpoint ip; + }; + + struct TORRENT_EXPORT peer_error_alert: alert + { + peer_error_alert(tcp::endpoint const& pip, peer_id const& pid_, std::string const& msg) + : alert(alert::debug, msg) + , ip(pip) + , pid(pid_) + {} + + virtual std::auto_ptr clone() const + { return std::auto_ptr(new peer_error_alert(*this)); } + + tcp::endpoint ip; + peer_id pid; + }; + + struct TORRENT_EXPORT invalid_request_alert: torrent_alert + { + invalid_request_alert( + peer_request const& r + , torrent_handle const& h + , tcp::endpoint const& sender + , peer_id const& pid_ + , std::string const& msg) + : torrent_alert(h, alert::debug, msg) + , ip(sender) + , request(r) + , pid(pid_) + {} + + virtual std::auto_ptr clone() const + { return std::auto_ptr(new invalid_request_alert(*this)); } + + tcp::endpoint ip; + peer_request request; + peer_id pid; + }; + + struct TORRENT_EXPORT torrent_finished_alert: torrent_alert + { + torrent_finished_alert( + const torrent_handle& h + , const std::string& msg) + : torrent_alert(h, alert::warning, msg) + {} + + virtual std::auto_ptr clone() const + { return std::auto_ptr(new torrent_finished_alert(*this)); } + }; + + struct TORRENT_EXPORT url_seed_alert: torrent_alert + { + url_seed_alert( + torrent_handle const& h + , const std::string& url_ + , const std::string& msg) + : torrent_alert(h, alert::warning, msg) + , url(url_) + {} + + virtual std::auto_ptr clone() const + { return std::auto_ptr(new url_seed_alert(*this)); } + + std::string url; + }; + + struct TORRENT_EXPORT file_error_alert: torrent_alert + { + file_error_alert( + const torrent_handle& h + , const std::string& msg) + : torrent_alert(h, alert::fatal, msg) + {} + + virtual std::auto_ptr clone() const + { return std::auto_ptr(new file_error_alert(*this)); } + }; + + struct TORRENT_EXPORT metadata_failed_alert: torrent_alert + { + metadata_failed_alert( + const torrent_handle& h + , const std::string& msg) + : torrent_alert(h, alert::info, msg) + {} + + virtual std::auto_ptr clone() const + { return std::auto_ptr(new metadata_failed_alert(*this)); } + }; + + struct TORRENT_EXPORT metadata_received_alert: torrent_alert + { + metadata_received_alert( + const torrent_handle& h + , const std::string& msg) + : torrent_alert(h, alert::info, msg) + {} + + virtual std::auto_ptr clone() const + { return std::auto_ptr(new metadata_received_alert(*this)); } + }; + + struct TORRENT_EXPORT listen_failed_alert: alert + { + listen_failed_alert( + const std::string& msg) + : alert(alert::fatal, msg) + {} + + virtual std::auto_ptr clone() const + { return std::auto_ptr(new listen_failed_alert(*this)); } + }; + + struct TORRENT_EXPORT portmap_error_alert: alert + { + portmap_error_alert(const std::string& msg) + : alert(alert::warning, msg) + {} + + virtual std::auto_ptr clone() const + { return std::auto_ptr(new portmap_error_alert(*this)); } + }; + + struct TORRENT_EXPORT portmap_alert: alert + { + portmap_alert(const std::string& msg) + : alert(alert::info, msg) + {} + + virtual std::auto_ptr clone() const + { return std::auto_ptr(new portmap_alert(*this)); } + }; + + struct TORRENT_EXPORT fastresume_rejected_alert: torrent_alert + { + fastresume_rejected_alert(torrent_handle const& h + , std::string const& msg) + : torrent_alert(h, alert::warning, msg) + {} + + virtual std::auto_ptr clone() const + { return std::auto_ptr(new fastresume_rejected_alert(*this)); } + }; + + struct TORRENT_EXPORT peer_blocked_alert: alert + { + peer_blocked_alert(address const& ip_ + , std::string const& msg) + : alert(alert::info, msg) + , ip(ip_) + {} + + address ip; + + virtual std::auto_ptr clone() const + { return std::auto_ptr(new peer_blocked_alert(*this)); } + }; + +} + + +#endif diff --git a/encryption/libtorrent/include/libtorrent/allocate_resources.hpp b/encryption/libtorrent/include/libtorrent/allocate_resources.hpp new file mode 100644 index 000000000..3d8237914 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/allocate_resources.hpp @@ -0,0 +1,78 @@ +/* + +Copyright (c) 2003, Magnus Jonsson +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_ALLOCATE_RESOURCES_HPP_INCLUDED +#define TORRENT_ALLOCATE_RESOURCES_HPP_INCLUDED + +#include +#include + +#include + +#include "libtorrent/resource_request.hpp" +#include "libtorrent/peer_id.hpp" +#include "libtorrent/socket.hpp" +#include "libtorrent/session.hpp" + +namespace libtorrent +{ + class peer_connection; + class torrent; + + int saturated_add(int a, int b); + + // Function to allocate a limited resource fairly among many consumers. + // It takes into account the current use, and the consumer's desired use. + // Should be invoked periodically to allow it adjust to the situation (make + // sure "used" is updated between calls!). + // If resources = std::numeric_limits::max() it means there is an infinite + // supply of resources (so everyone can get what they want). + + void allocate_resources( + int resources + , std::map >& torrents + , resource_request torrent::* res); + + void allocate_resources( + int resources + , std::map& connections + , resource_request peer_connection::* res); + + // Used for global limits. + void allocate_resources( + int resources + , std::vector& _sessions + , resource_request session::* res); +} + + +#endif diff --git a/encryption/libtorrent/include/libtorrent/asio.hpp b/encryption/libtorrent/include/libtorrent/asio.hpp new file mode 100644 index 000000000..dfad4a6d5 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio.hpp @@ -0,0 +1,73 @@ +// +// asio.hpp +// ~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_HPP +#define ASIO_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/basic_datagram_socket.hpp" +#include "asio/basic_deadline_timer.hpp" +#include "asio/basic_io_object.hpp" +#include "asio/basic_socket_acceptor.hpp" +#include "asio/basic_socket_iostream.hpp" +#include "asio/basic_socket_streambuf.hpp" +#include "asio/basic_stream_socket.hpp" +#include "asio/basic_streambuf.hpp" +#include "asio/buffer.hpp" +#include "asio/buffered_read_stream_fwd.hpp" +#include "asio/buffered_read_stream.hpp" +#include "asio/buffered_stream_fwd.hpp" +#include "asio/buffered_stream.hpp" +#include "asio/buffered_write_stream_fwd.hpp" +#include "asio/buffered_write_stream.hpp" +#include "asio/completion_condition.hpp" +#include "asio/datagram_socket_service.hpp" +#include "asio/deadline_timer_service.hpp" +#include "asio/deadline_timer.hpp" +#include "asio/error.hpp" +#include "asio/error_code.hpp" +#include "asio/handler_alloc_hook.hpp" +#include "asio/handler_invoke_hook.hpp" +#include "asio/io_service.hpp" +#include "asio/ip/address.hpp" +#include "asio/ip/address_v4.hpp" +#include "asio/ip/address_v6.hpp" +#include "asio/ip/basic_endpoint.hpp" +#include "asio/ip/basic_resolver.hpp" +#include "asio/ip/basic_resolver_entry.hpp" +#include "asio/ip/basic_resolver_iterator.hpp" +#include "asio/ip/basic_resolver_query.hpp" +#include "asio/ip/host_name.hpp" +#include "asio/ip/multicast.hpp" +#include "asio/ip/resolver_query_base.hpp" +#include "asio/ip/resolver_service.hpp" +#include "asio/ip/tcp.hpp" +#include "asio/ip/udp.hpp" +#include "asio/ip/unicast.hpp" +#include "asio/ip/v6_only.hpp" +#include "asio/is_read_buffered.hpp" +#include "asio/is_write_buffered.hpp" +#include "asio/placeholders.hpp" +#include "asio/read.hpp" +#include "asio/read_until.hpp" +#include "asio/socket_acceptor_service.hpp" +#include "asio/socket_base.hpp" +#include "asio/strand.hpp" +#include "asio/stream_socket_service.hpp" +#include "asio/streambuf.hpp" +#include "asio/system_error.hpp" +#include "asio/thread.hpp" +#include "asio/time_traits.hpp" +#include "asio/write.hpp" + +#endif // ASIO_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/basic_datagram_socket.hpp b/encryption/libtorrent/include/libtorrent/asio/basic_datagram_socket.hpp new file mode 100644 index 000000000..1a521628f --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/basic_datagram_socket.hpp @@ -0,0 +1,803 @@ +// +// basic_datagram_socket.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_BASIC_DATAGRAM_SOCKET_HPP +#define ASIO_BASIC_DATAGRAM_SOCKET_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/basic_socket.hpp" +#include "asio/datagram_socket_service.hpp" +#include "asio/error.hpp" +#include "asio/detail/throw_error.hpp" + +namespace asio { + +/// Provides datagram-oriented socket functionality. +/** + * The basic_datagram_socket class template provides asynchronous and blocking + * datagram-oriented socket functionality. + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + */ +template > +class basic_datagram_socket + : public basic_socket +{ +public: + /// The native representation of a socket. + typedef typename DatagramSocketService::native_type native_type; + + /// The protocol type. + typedef Protocol protocol_type; + + /// The endpoint type. + typedef typename Protocol::endpoint endpoint_type; + + /// Construct a basic_datagram_socket without opening it. + /** + * This constructor creates a datagram socket without opening it. The open() + * function must be called before data can be sent or received on the socket. + * + * @param io_service The io_service object that the datagram socket will use + * to dispatch handlers for any asynchronous operations performed on the + * socket. + */ + explicit basic_datagram_socket(asio::io_service& io_service) + : basic_socket(io_service) + { + } + + /// Construct and open a basic_datagram_socket. + /** + * This constructor creates and opens a datagram socket. + * + * @param io_service The io_service object that the datagram socket will use + * to dispatch handlers for any asynchronous operations performed on the + * socket. + * + * @param protocol An object specifying protocol parameters to be used. + * + * @throws asio::system_error Thrown on failure. + */ + basic_datagram_socket(asio::io_service& io_service, + const protocol_type& protocol) + : basic_socket(io_service, protocol) + { + } + + /// Construct a basic_datagram_socket, opening it and binding it to the given + /// local endpoint. + /** + * This constructor creates a datagram socket and automatically opens it bound + * to the specified endpoint on the local machine. The protocol used is the + * protocol associated with the given endpoint. + * + * @param io_service The io_service object that the datagram socket will use + * to dispatch handlers for any asynchronous operations performed on the + * socket. + * + * @param endpoint An endpoint on the local machine to which the datagram + * socket will be bound. + * + * @throws asio::system_error Thrown on failure. + */ + basic_datagram_socket(asio::io_service& io_service, + const endpoint_type& endpoint) + : basic_socket(io_service, endpoint) + { + } + + /// Construct a basic_datagram_socket on an existing native socket. + /** + * This constructor creates a datagram socket object to hold an existing + * native socket. + * + * @param io_service The io_service object that the datagram socket will use + * to dispatch handlers for any asynchronous operations performed on the + * socket. + * + * @param protocol An object specifying protocol parameters to be used. + * + * @param native_socket The new underlying socket implementation. + * + * @throws asio::system_error Thrown on failure. + */ + basic_datagram_socket(asio::io_service& io_service, + const protocol_type& protocol, const native_type& native_socket) + : basic_socket( + io_service, protocol, native_socket) + { + } + + /// Send some data on a connected socket. + /** + * This function is used to send data on the datagram socket. The function + * call will block until the data has been sent successfully or an error + * occurs. + * + * @param buffers One ore more data buffers to be sent on the socket. + * + * @returns The number of bytes sent. + * + * @throws asio::system_error Thrown on failure. + * + * @note The send operation can only be used with a connected socket. Use + * the send_to function to send data on an unconnected datagram socket. + * + * @par Example + * To send a single data buffer use the @ref buffer function as follows: + * @code socket.send(asio::buffer(data, size)); @endcode + * See the @ref buffer documentation for information on sending multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + std::size_t send(const ConstBufferSequence& buffers) + { + asio::error_code ec; + std::size_t s = this->service.send(this->implementation, buffers, 0, ec); + asio::detail::throw_error(ec); + return s; + } + + /// Send some data on a connected socket. + /** + * This function is used to send data on the datagram socket. The function + * call will block until the data has been sent successfully or an error + * occurs. + * + * @param buffers One ore more data buffers to be sent on the socket. + * + * @param flags Flags specifying how the send call is to be made. + * + * @returns The number of bytes sent. + * + * @throws asio::system_error Thrown on failure. + * + * @note The send operation can only be used with a connected socket. Use + * the send_to function to send data on an unconnected datagram socket. + */ + template + std::size_t send(const ConstBufferSequence& buffers, + socket_base::message_flags flags) + { + asio::error_code ec; + std::size_t s = this->service.send( + this->implementation, buffers, flags, ec); + asio::detail::throw_error(ec); + return s; + } + + /// Send some data on a connected socket. + /** + * This function is used to send data on the datagram socket. The function + * call will block until the data has been sent successfully or an error + * occurs. + * + * @param buffers One or more data buffers to be sent on the socket. + * + * @param flags Flags specifying how the send call is to be made. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes sent. + * + * @note The send operation can only be used with a connected socket. Use + * the send_to function to send data on an unconnected datagram socket. + */ + template + std::size_t send(const ConstBufferSequence& buffers, + socket_base::message_flags flags, asio::error_code& ec) + { + return this->service.send(this->implementation, buffers, flags, ec); + } + + /// Start an asynchronous send on a connected socket. + /** + * This function is used to send data on the datagram socket. The function + * call will block until the data has been sent successfully or an error + * occurs. + * + * @param buffers One or more data buffers to be sent on the socket. Although + * the buffers object may be copied as necessary, ownership of the underlying + * memory blocks is retained by the caller, which must guarantee that they + * remain valid until the handler is called. + * + * @param handler The handler to be called when the send operation completes. + * Copies will be made of the handler as required. The function signature of + * the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes sent. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_service::post(). + * + * @note The async_send operation can only be used with a connected socket. + * Use the async_send_to function to send data on an unconnected datagram + * socket. + * + * @par Example + * To send a single data buffer use the @ref buffer function as follows: + * @code + * socket.async_send(asio::buffer(data, size), handler); + * @endcode + * See the @ref buffer documentation for information on sending multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + void async_send(const ConstBufferSequence& buffers, WriteHandler handler) + { + this->service.async_send(this->implementation, buffers, 0, handler); + } + + /// Start an asynchronous send on a connected socket. + /** + * This function is used to send data on the datagram socket. The function + * call will block until the data has been sent successfully or an error + * occurs. + * + * @param buffers One or more data buffers to be sent on the socket. Although + * the buffers object may be copied as necessary, ownership of the underlying + * memory blocks is retained by the caller, which must guarantee that they + * remain valid until the handler is called. + * + * @param flags Flags specifying how the send call is to be made. + * + * @param handler The handler to be called when the send operation completes. + * Copies will be made of the handler as required. The function signature of + * the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes sent. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_service::post(). + * + * @note The async_send operation can only be used with a connected socket. + * Use the async_send_to function to send data on an unconnected datagram + * socket. + */ + template + void async_send(const ConstBufferSequence& buffers, + socket_base::message_flags flags, WriteHandler handler) + { + this->service.async_send(this->implementation, buffers, flags, handler); + } + + /// Send a datagram to the specified endpoint. + /** + * This function is used to send a datagram to the specified remote endpoint. + * The function call will block until the data has been sent successfully or + * an error occurs. + * + * @param buffers One or more data buffers to be sent to the remote endpoint. + * + * @param destination The remote endpoint to which the data will be sent. + * + * @returns The number of bytes sent. + * + * @throws asio::system_error Thrown on failure. + * + * @par Example + * To send a single data buffer use the @ref buffer function as follows: + * @code + * asio::ip::udp::endpoint destination( + * asio::ip::address::from_string("1.2.3.4"), 12345); + * socket.send_to(asio::buffer(data, size), destination); + * @endcode + * See the @ref buffer documentation for information on sending multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + std::size_t send_to(const ConstBufferSequence& buffers, + const endpoint_type& destination) + { + asio::error_code ec; + std::size_t s = this->service.send_to( + this->implementation, buffers, destination, 0, ec); + asio::detail::throw_error(ec); + return s; + } + + /// Send a datagram to the specified endpoint. + /** + * This function is used to send a datagram to the specified remote endpoint. + * The function call will block until the data has been sent successfully or + * an error occurs. + * + * @param buffers One or more data buffers to be sent to the remote endpoint. + * + * @param destination The remote endpoint to which the data will be sent. + * + * @param flags Flags specifying how the send call is to be made. + * + * @returns The number of bytes sent. + * + * @throws asio::system_error Thrown on failure. + */ + template + std::size_t send_to(const ConstBufferSequence& buffers, + const endpoint_type& destination, socket_base::message_flags flags) + { + asio::error_code ec; + std::size_t s = this->service.send_to( + this->implementation, buffers, destination, flags, ec); + asio::detail::throw_error(ec); + return s; + } + + /// Send a datagram to the specified endpoint. + /** + * This function is used to send a datagram to the specified remote endpoint. + * The function call will block until the data has been sent successfully or + * an error occurs. + * + * @param buffers One or more data buffers to be sent to the remote endpoint. + * + * @param destination The remote endpoint to which the data will be sent. + * + * @param flags Flags specifying how the send call is to be made. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes sent. + */ + template + std::size_t send_to(const ConstBufferSequence& buffers, + const endpoint_type& destination, socket_base::message_flags flags, + asio::error_code& ec) + { + return this->service.send_to(this->implementation, + buffers, destination, flags, ec); + } + + /// Start an asynchronous send. + /** + * This function is used to asynchronously send a datagram to the specified + * remote endpoint. The function call always returns immediately. + * + * @param buffers One or more data buffers to be sent to the remote endpoint. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param destination The remote endpoint to which the data will be sent. + * Copies will be made of the endpoint as required. + * + * @param handler The handler to be called when the send operation completes. + * Copies will be made of the handler as required. The function signature of + * the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes sent. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_service::post(). + * + * @par Example + * To send a single data buffer use the @ref buffer function as follows: + * @code + * asio::ip::udp::endpoint destination( + * asio::ip::address::from_string("1.2.3.4"), 12345); + * socket.async_send_to( + * asio::buffer(data, size), destination, handler); + * @endcode + * See the @ref buffer documentation for information on sending multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + void async_send_to(const ConstBufferSequence& buffers, + const endpoint_type& destination, WriteHandler handler) + { + this->service.async_send_to(this->implementation, buffers, destination, 0, + handler); + } + + /// Start an asynchronous send. + /** + * This function is used to asynchronously send a datagram to the specified + * remote endpoint. The function call always returns immediately. + * + * @param buffers One or more data buffers to be sent to the remote endpoint. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param flags Flags specifying how the send call is to be made. + * + * @param destination The remote endpoint to which the data will be sent. + * Copies will be made of the endpoint as required. + * + * @param handler The handler to be called when the send operation completes. + * Copies will be made of the handler as required. The function signature of + * the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes sent. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_service::post(). + */ + template + void async_send_to(const ConstBufferSequence& buffers, + const endpoint_type& destination, socket_base::message_flags flags, + WriteHandler handler) + { + this->service.async_send_to(this->implementation, buffers, destination, + flags, handler); + } + + /// Receive some data on a connected socket. + /** + * This function is used to receive data on the datagram socket. The function + * call will block until data has been received successfully or an error + * occurs. + * + * @param buffers One or more buffers into which the data will be received. + * + * @returns The number of bytes received. + * + * @throws asio::system_error Thrown on failure. + * + * @note The receive operation can only be used with a connected socket. Use + * the receive_from function to receive data on an unconnected datagram + * socket. + * + * @par Example + * To receive into a single data buffer use the @ref buffer function as + * follows: + * @code socket.receive(asio::buffer(data, size)); @endcode + * See the @ref buffer documentation for information on receiving into + * multiple buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + std::size_t receive(const MutableBufferSequence& buffers) + { + asio::error_code ec; + std::size_t s = this->service.receive( + this->implementation, buffers, 0, ec); + asio::detail::throw_error(ec); + return s; + } + + /// Receive some data on a connected socket. + /** + * This function is used to receive data on the datagram socket. The function + * call will block until data has been received successfully or an error + * occurs. + * + * @param buffers One or more buffers into which the data will be received. + * + * @param flags Flags specifying how the receive call is to be made. + * + * @returns The number of bytes received. + * + * @throws asio::system_error Thrown on failure. + * + * @note The receive operation can only be used with a connected socket. Use + * the receive_from function to receive data on an unconnected datagram + * socket. + */ + template + std::size_t receive(const MutableBufferSequence& buffers, + socket_base::message_flags flags) + { + asio::error_code ec; + std::size_t s = this->service.receive( + this->implementation, buffers, flags, ec); + asio::detail::throw_error(ec); + return s; + } + + /// Receive some data on a connected socket. + /** + * This function is used to receive data on the datagram socket. The function + * call will block until data has been received successfully or an error + * occurs. + * + * @param buffers One or more buffers into which the data will be received. + * + * @param flags Flags specifying how the receive call is to be made. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes received. + * + * @note The receive operation can only be used with a connected socket. Use + * the receive_from function to receive data on an unconnected datagram + * socket. + */ + template + std::size_t receive(const MutableBufferSequence& buffers, + socket_base::message_flags flags, asio::error_code& ec) + { + return this->service.receive(this->implementation, buffers, flags, ec); + } + + /// Start an asynchronous receive on a connected socket. + /** + * This function is used to asynchronously receive data from the datagram + * socket. The function call always returns immediately. + * + * @param buffers One or more buffers into which the data will be received. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param handler The handler to be called when the receive operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes received. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_service::post(). + * + * @note The async_receive operation can only be used with a connected socket. + * Use the async_receive_from function to receive data on an unconnected + * datagram socket. + * + * @par Example + * To receive into a single data buffer use the @ref buffer function as + * follows: + * @code + * socket.async_receive(asio::buffer(data, size), handler); + * @endcode + * See the @ref buffer documentation for information on receiving into + * multiple buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + void async_receive(const MutableBufferSequence& buffers, ReadHandler handler) + { + this->service.async_receive(this->implementation, buffers, 0, handler); + } + + /// Start an asynchronous receive on a connected socket. + /** + * This function is used to asynchronously receive data from the datagram + * socket. The function call always returns immediately. + * + * @param buffers One or more buffers into which the data will be received. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param flags Flags specifying how the receive call is to be made. + * + * @param handler The handler to be called when the receive operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes received. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_service::post(). + * + * @note The async_receive operation can only be used with a connected socket. + * Use the async_receive_from function to receive data on an unconnected + * datagram socket. + */ + template + void async_receive(const MutableBufferSequence& buffers, + socket_base::message_flags flags, ReadHandler handler) + { + this->service.async_receive(this->implementation, buffers, flags, handler); + } + + /// Receive a datagram with the endpoint of the sender. + /** + * This function is used to receive a datagram. The function call will block + * until data has been received successfully or an error occurs. + * + * @param buffers One or more buffers into which the data will be received. + * + * @param sender_endpoint An endpoint object that receives the endpoint of + * the remote sender of the datagram. + * + * @returns The number of bytes received. + * + * @throws asio::system_error Thrown on failure. + * + * @par Example + * To receive into a single data buffer use the @ref buffer function as + * follows: + * @code + * asio::ip::udp::endpoint sender_endpoint; + * socket.receive_from( + * asio::buffer(data, size), sender_endpoint); + * @endcode + * See the @ref buffer documentation for information on receiving into + * multiple buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + std::size_t receive_from(const MutableBufferSequence& buffers, + endpoint_type& sender_endpoint) + { + asio::error_code ec; + std::size_t s = this->service.receive_from( + this->implementation, buffers, sender_endpoint, 0, ec); + asio::detail::throw_error(ec); + return s; + } + + /// Receive a datagram with the endpoint of the sender. + /** + * This function is used to receive a datagram. The function call will block + * until data has been received successfully or an error occurs. + * + * @param buffers One or more buffers into which the data will be received. + * + * @param sender_endpoint An endpoint object that receives the endpoint of + * the remote sender of the datagram. + * + * @param flags Flags specifying how the receive call is to be made. + * + * @returns The number of bytes received. + * + * @throws asio::system_error Thrown on failure. + */ + template + std::size_t receive_from(const MutableBufferSequence& buffers, + endpoint_type& sender_endpoint, socket_base::message_flags flags) + { + asio::error_code ec; + std::size_t s = this->service.receive_from( + this->implementation, buffers, sender_endpoint, flags, ec); + asio::detail::throw_error(ec); + return s; + } + + /// Receive a datagram with the endpoint of the sender. + /** + * This function is used to receive a datagram. The function call will block + * until data has been received successfully or an error occurs. + * + * @param buffers One or more buffers into which the data will be received. + * + * @param sender_endpoint An endpoint object that receives the endpoint of + * the remote sender of the datagram. + * + * @param flags Flags specifying how the receive call is to be made. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes received. + */ + template + std::size_t receive_from(const MutableBufferSequence& buffers, + endpoint_type& sender_endpoint, socket_base::message_flags flags, + asio::error_code& ec) + { + return this->service.receive_from(this->implementation, buffers, + sender_endpoint, flags, ec); + } + + /// Start an asynchronous receive. + /** + * This function is used to asynchronously receive a datagram. The function + * call always returns immediately. + * + * @param buffers One or more buffers into which the data will be received. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param sender_endpoint An endpoint object that receives the endpoint of + * the remote sender of the datagram. Ownership of the sender_endpoint object + * is retained by the caller, which must guarantee that it is valid until the + * handler is called. + * + * @param handler The handler to be called when the receive operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * const asio::system_error& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes received. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_service::post(). + * + * @par Example + * To receive into a single data buffer use the @ref buffer function as + * follows: + * @code socket.async_receive_from( + * asio::buffer(data, size), 0, sender_endpoint, handler); @endcode + * See the @ref buffer documentation for information on receiving into + * multiple buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + void async_receive_from(const MutableBufferSequence& buffers, + endpoint_type& sender_endpoint, ReadHandler handler) + { + this->service.async_receive_from(this->implementation, buffers, + sender_endpoint, 0, handler); + } + + /// Start an asynchronous receive. + /** + * This function is used to asynchronously receive a datagram. The function + * call always returns immediately. + * + * @param buffers One or more buffers into which the data will be received. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param sender_endpoint An endpoint object that receives the endpoint of + * the remote sender of the datagram. Ownership of the sender_endpoint object + * is retained by the caller, which must guarantee that it is valid until the + * handler is called. + * + * @param flags Flags specifying how the receive call is to be made. + * + * @param handler The handler to be called when the receive operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes received. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_service::post(). + */ + template + void async_receive_from(const MutableBufferSequence& buffers, + endpoint_type& sender_endpoint, socket_base::message_flags flags, + ReadHandler handler) + { + this->service.async_receive_from(this->implementation, buffers, + sender_endpoint, flags, handler); + } +}; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_BASIC_DATAGRAM_SOCKET_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/basic_deadline_timer.hpp b/encryption/libtorrent/include/libtorrent/asio/basic_deadline_timer.hpp new file mode 100644 index 000000000..a630c67bc --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/basic_deadline_timer.hpp @@ -0,0 +1,399 @@ +// +// basic_deadline_timer.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_BASIC_DEADLINE_TIMER_HPP +#define ASIO_BASIC_DEADLINE_TIMER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/basic_io_object.hpp" +#include "asio/deadline_timer_service.hpp" +#include "asio/error.hpp" +#include "asio/detail/throw_error.hpp" + +namespace asio { + +/// Provides waitable timer functionality. +/** + * The basic_deadline_timer class template provides the ability to perform a + * blocking or asynchronous wait for a timer to expire. + * + * Most applications will use the asio::deadline_timer typedef. + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + * + * @sa @ref deadline_timer_reset + * + * @par Examples + * Performing a blocking wait: + * @code + * // Construct a timer without setting an expiry time. + * asio::deadline_timer timer(io_service); + * + * // Set an expiry time relative to now. + * timer.expires_from_now(boost::posix_time::seconds(5)); + * + * // Wait for the timer to expire. + * timer.wait(); + * @endcode + * + * @par + * Performing an asynchronous wait: + * @code + * void handler(const asio::error_code& error) + * { + * if (!error) + * { + * // Timer expired. + * } + * } + * + * ... + * + * // Construct a timer with an absolute expiry time. + * asio::deadline_timer timer(io_service, + * boost::posix_time::time_from_string("2005-12-07 23:59:59.000")); + * + * // Start an asynchronous wait. + * timer.async_wait(handler); + * @endcode + */ +template , + typename TimerService = deadline_timer_service > +class basic_deadline_timer + : public basic_io_object +{ +public: + /// The time traits type. + typedef TimeTraits traits_type; + + /// The time type. + typedef typename traits_type::time_type time_type; + + /// The duration type. + typedef typename traits_type::duration_type duration_type; + + /// Constructor. + /** + * This constructor creates a timer without setting an expiry time. The + * expires_at() or expires_from_now() functions must be called to set an + * expiry time before the timer can be waited on. + * + * @param io_service The io_service object that the timer will use to dispatch + * handlers for any asynchronous operations performed on the timer. + */ + explicit basic_deadline_timer(asio::io_service& io_service) + : basic_io_object(io_service) + { + } + + /// Constructor to set a particular expiry time as an absolute time. + /** + * This constructor creates a timer and sets the expiry time. + * + * @param io_service The io_service object that the timer will use to dispatch + * handlers for any asynchronous operations performed on the timer. + * + * @param expiry_time The expiry time to be used for the timer, expressed + * as an absolute time. + */ + basic_deadline_timer(asio::io_service& io_service, + const time_type& expiry_time) + : basic_io_object(io_service) + { + asio::error_code ec; + this->service.expires_at(this->implementation, expiry_time, ec); + asio::detail::throw_error(ec); + } + + /// Constructor to set a particular expiry time relative to now. + /** + * This constructor creates a timer and sets the expiry time. + * + * @param io_service The io_service object that the timer will use to dispatch + * handlers for any asynchronous operations performed on the timer. + * + * @param expiry_time The expiry time to be used for the timer, relative to + * now. + */ + basic_deadline_timer(asio::io_service& io_service, + const duration_type& expiry_time) + : basic_io_object(io_service) + { + asio::error_code ec; + this->service.expires_from_now(this->implementation, expiry_time, ec); + asio::detail::throw_error(ec); + } + + /// Cancel any asynchronous operations that are waiting on the timer. + /** + * This function forces the completion of any pending asynchronous wait + * operations against the timer. The handler for each cancelled operation will + * be invoked with the asio::error::operation_aborted error code. + * + * Cancelling the timer does not change the expiry time. + * + * @return The number of asynchronous operations that were cancelled. + * + * @throws asio::system_error Thrown on failure. + */ + std::size_t cancel() + { + asio::error_code ec; + std::size_t s = this->service.cancel(this->implementation, ec); + asio::detail::throw_error(ec); + return s; + } + + /// Cancel any asynchronous operations that are waiting on the timer. + /** + * This function forces the completion of any pending asynchronous wait + * operations against the timer. The handler for each cancelled operation will + * be invoked with the asio::error::operation_aborted error code. + * + * Cancelling the timer does not change the expiry time. + * + * @param ec Set to indicate what error occurred, if any. + * + * @return The number of asynchronous operations that were cancelled. + */ + std::size_t cancel(asio::error_code& ec) + { + return this->service.cancel(this->implementation, ec); + } + + /// Get the timer's expiry time as an absolute time. + /** + * This function may be used to obtain the timer's current expiry time. + * Whether the timer has expired or not does not affect this value. + */ + time_type expires_at() const + { + return this->service.expires_at(this->implementation); + } + + /// Set the timer's expiry time as an absolute time. + /** + * This function sets the expiry time. Any pending asynchronous wait + * operations will be cancelled. The handler for each cancelled operation will + * be invoked with the asio::error::operation_aborted error code. + * + * See @ref deadline_timer_reset for more information on altering the expiry + * time of an active timer. + * + * @param expiry_time The expiry time to be used for the timer. + * + * @return The number of asynchronous operations that were cancelled. + * + * @throws asio::system_error Thrown on failure. + */ + std::size_t expires_at(const time_type& expiry_time) + { + asio::error_code ec; + std::size_t s = this->service.expires_at( + this->implementation, expiry_time, ec); + asio::detail::throw_error(ec); + return s; + } + + /// Set the timer's expiry time as an absolute time. + /** + * This function sets the expiry time. Any pending asynchronous wait + * operations will be cancelled. The handler for each cancelled operation will + * be invoked with the asio::error::operation_aborted error code. + * + * See @ref deadline_timer_reset for more information on altering the expiry + * time of an active timer. + * + * @param expiry_time The expiry time to be used for the timer. + * + * @param ec Set to indicate what error occurred, if any. + * + * @return The number of asynchronous operations that were cancelled. + */ + std::size_t expires_at(const time_type& expiry_time, + asio::error_code& ec) + { + return this->service.expires_at(this->implementation, expiry_time, ec); + } + + /// Get the timer's expiry time relative to now. + /** + * This function may be used to obtain the timer's current expiry time. + * Whether the timer has expired or not does not affect this value. + */ + duration_type expires_from_now() const + { + return this->service.expires_from_now(this->implementation); + } + + /// Set the timer's expiry time relative to now. + /** + * This function sets the expiry time. Any pending asynchronous wait + * operations will be cancelled. The handler for each cancelled operation will + * be invoked with the asio::error::operation_aborted error code. + * + * See @ref deadline_timer_reset for more information on altering the expiry + * time of an active timer. + * + * @param expiry_time The expiry time to be used for the timer. + * + * @return The number of asynchronous operations that were cancelled. + * + * @throws asio::system_error Thrown on failure. + */ + std::size_t expires_from_now(const duration_type& expiry_time) + { + asio::error_code ec; + std::size_t s = this->service.expires_from_now( + this->implementation, expiry_time, ec); + asio::detail::throw_error(ec); + return s; + } + + /// Set the timer's expiry time relative to now. + /** + * This function sets the expiry time. Any pending asynchronous wait + * operations will be cancelled. The handler for each cancelled operation will + * be invoked with the asio::error::operation_aborted error code. + * + * See @ref deadline_timer_reset for more information on altering the expiry + * time of an active timer. + * + * @param expiry_time The expiry time to be used for the timer. + * + * @param ec Set to indicate what error occurred, if any. + * + * @return The number of asynchronous operations that were cancelled. + */ + std::size_t expires_from_now(const duration_type& expiry_time, + asio::error_code& ec) + { + return this->service.expires_from_now( + this->implementation, expiry_time, ec); + } + + /// Perform a blocking wait on the timer. + /** + * This function is used to wait for the timer to expire. This function + * blocks and does not return until the timer has expired. + * + * @throws asio::system_error Thrown on failure. + */ + void wait() + { + asio::error_code ec; + this->service.wait(this->implementation, ec); + asio::detail::throw_error(ec); + } + + /// Perform a blocking wait on the timer. + /** + * This function is used to wait for the timer to expire. This function + * blocks and does not return until the timer has expired. + * + * @param ec Set to indicate what error occurred, if any. + */ + void wait(asio::error_code& ec) + { + this->service.wait(this->implementation, ec); + } + + /// Start an asynchronous wait on the timer. + /** + * This function may be used to initiate an asynchronous wait against the + * timer. It always returns immediately. + * + * For each call to async_wait(), the supplied handler will be called exactly + * once. The handler will be called when: + * + * @li The timer has expired. + * + * @li The timer was cancelled, in which case the handler is passed the error + * code asio::error::operation_aborted. + * + * @param handler The handler to be called when the timer expires. Copies + * will be made of the handler as required. The function signature of the + * handler must be: + * @code void handler( + * const asio::error_code& error // Result of operation. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_service::post(). + */ + template + void async_wait(WaitHandler handler) + { + this->service.async_wait(this->implementation, handler); + } +}; + +/** + * @page deadline_timer_reset Changing an active deadline_timer's expiry time + * + * Changing the expiry time of a timer while there are pending asynchronous + * waits causes those wait operations to be cancelled. To ensure that the action + * associated with the timer is performed only once, use something like this: + * used: + * + * @code + * void on_some_event() + * { + * if (my_timer.expires_from_now(seconds(5)) > 0) + * { + * // We managed to cancel the timer. Start new asynchronous wait. + * my_timer.async_wait(on_timeout); + * } + * else + * { + * // Too late, timer has already expired! + * } + * } + * + * void on_timeout(const asio::error_code& e) + * { + * if (e != asio::error::operation_aborted) + * { + * // Timer was not cancelled, take necessary action. + * } + * } + * @endcode + * + * @li The asio::basic_deadline_timer::expires_from_now() function + * cancels any pending asynchronous waits, and returns the number of + * asynchronous waits that were cancelled. If it returns 0 then you were too + * late and the wait handler has already been executed, or will soon be + * executed. If it returns 1 then the wait handler was successfully cancelled. + * + * @li If a wait handler is cancelled, the asio::error_code passed to + * it contains the value asio::error::operation_aborted. + * + * @sa asio::basic_deadline_timer + */ + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_BASIC_DEADLINE_TIMER_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/basic_io_object.hpp b/encryption/libtorrent/include/libtorrent/asio/basic_io_object.hpp new file mode 100644 index 000000000..9291ff5da --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/basic_io_object.hpp @@ -0,0 +1,75 @@ +// +// basic_io_object.hpp +// ~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_BASIC_IO_OBJECT_HPP +#define ASIO_BASIC_IO_OBJECT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/io_service.hpp" +#include "asio/detail/noncopyable.hpp" + +namespace asio { + +/// Base class for all I/O objects. +template +class basic_io_object + : private noncopyable +{ +public: + /// The type of the service that will be used to provide I/O operations. + typedef IoObjectService service_type; + + /// The underlying implementation type of I/O object. + typedef typename service_type::implementation_type implementation_type; + + /// Get the io_service associated with the object. + /** + * This function may be used to obtain the io_service object that the I/O + * object uses to dispatch handlers for asynchronous operations. + * + * @return A reference to the io_service object that the I/O object will use + * to dispatch handlers. Ownership is not transferred to the caller. + */ + asio::io_service& io_service() + { + return service.io_service(); + } + +protected: + /// Construct a basic_io_object. + explicit basic_io_object(asio::io_service& io_service) + : service(asio::use_service(io_service)) + { + service.construct(implementation); + } + + /// Protected destructor to prevent deletion through this type. + ~basic_io_object() + { + service.destroy(implementation); + } + + // The backend service implementation. + service_type& service; + + // The underlying native implementation. + implementation_type implementation; +}; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_BASIC_IO_OBJECT_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/basic_resolver.hpp b/encryption/libtorrent/include/libtorrent/asio/basic_resolver.hpp new file mode 100644 index 000000000..5df89d545 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/basic_resolver.hpp @@ -0,0 +1,252 @@ +// +// basic_resolver.hpp +// ~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_BASIC_RESOLVER_HPP +#define ASIO_BASIC_RESOLVER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/basic_io_object.hpp" +#include "asio/error.hpp" +#include "asio/error_handler.hpp" +#include "asio/resolver_service.hpp" + +namespace asio { + +/// Provides endpoint resolution functionality. +/** + * The basic_resolver class template provides the ability to resolve a query + * to a list of endpoints. + * + * @par Thread Safety: + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + * + * @par Concepts: + * Async_Object, Error_Source. + */ +template > +class basic_resolver + : public basic_io_object +{ +public: + /// The protocol type. + typedef Protocol protocol_type; + + /// The endpoint type. + typedef typename Protocol::endpoint endpoint_type; + + /// The query type. + typedef typename Protocol::resolver_query query; + + /// The iterator type. + typedef typename Protocol::resolver_iterator iterator; + + /// The type used for reporting errors. + typedef asio::error error_type; + + /// Constructor. + /** + * This constructor creates a basic_resolver. + * + * @param io_service The io_service object that the resolver will use to + * dispatch handlers for any asynchronous operations performed on the timer. + */ + explicit basic_resolver(asio::io_service& io_service) + : basic_io_object(io_service) + { + } + + /// Cancel any asynchronous operations that are waiting on the resolver. + /** + * This function forces the completion of any pending asynchronous + * operations on the host resolver. The handler for each cancelled operation + * will be invoked with the asio::error::operation_aborted error code. + */ + void cancel() + { + return this->service.cancel(this->implementation); + } + + /// Resolve a query to a list of entries. + /** + * This function is used to resolve a query into a list of endpoint entries. + * + * @param q A query object that determines what endpoints will be returned. + * + * @returns A forward-only iterator that can be used to traverse the list + * of endpoint entries. + * + * @throws asio::error Thrown on failure. + * + * @note A default constructed iterator represents the end of the list. + * + * @note A successful call to this function is guaranteed to return at least + * one entry. + */ + iterator resolve(const query& q) + { + return this->service.resolve(this->implementation, q, throw_error()); + } + + /// Resolve a query to a list of entries. + /** + * This function is used to resolve a query into a list of endpoint entries. + * + * @param q A query object that determines what endpoints will be returned. + * + * @returns A forward-only iterator that can be used to traverse the list + * of endpoint entries. Returns a default constructed iterator if an error + * occurs. + * + * @param error_handler A handler to be called when the operation completes, + * to indicate whether or not an error has occurred. Copies will be made of + * the handler as required. The function signature of the handler must be: + * @code void error_handler( + * const asio::error& error // Result of operation. + * ); @endcode + * + * @note A default constructed iterator represents the end of the list. + * + * @note A successful call to this function is guaranteed to return at least + * one entry. + */ + template + iterator resolve(const query& q, Error_Handler error_handler) + { + return this->service.resolve(this->implementation, q, error_handler); + } + + /// Asynchronously resolve a query to a list of entries. + /** + * This function is used to asynchronously resolve a query into a list of + * endpoint entries. + * + * @param q A query object that determines what endpoints will be returned. + * + * @param handler The handler to be called when the resolve operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * const asio::error& error, // Result of operation. + * resolver::iterator iterator // Forward-only iterator that can be used to + * // traverse the list of endpoint entries. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_service::post(). + * + * @note A default constructed iterator represents the end of the list. + * + * @note A successful resolve operation is guaranteed to pass at least one + * entry to the handler. + */ + template + void async_resolve(const query& q, Handler handler) + { + return this->service.async_resolve(this->implementation, q, handler); + } + + /// Resolve an endpoint to a list of entries. + /** + * This function is used to resolve an endpoint into a list of endpoint + * entries. + * + * @param e An endpoint object that determines what endpoints will be + * returned. + * + * @returns A forward-only iterator that can be used to traverse the list + * of endpoint entries. + * + * @throws asio::error Thrown on failure. + * + * @note A default constructed iterator represents the end of the list. + * + * @note A successful call to this function is guaranteed to return at least + * one entry. + */ + iterator resolve(const endpoint_type& e) + { + return this->service.resolve(this->implementation, e, throw_error()); + } + + /// Resolve an endpoint to a list of entries. + /** + * This function is used to resolve an endpoint into a list of endpoint + * entries. + * + * @param e An endpoint object that determines what endpoints will be + * returned. + * + * @returns A forward-only iterator that can be used to traverse the list + * of endpoint entries. Returns a default constructed iterator if an error + * occurs. + * + * @param error_handler A handler to be called when the operation completes, + * to indicate whether or not an error has occurred. Copies will be made of + * the handler as required. The function signature of the handler must be: + * @code void error_handler( + * const asio::error& error // Result of operation. + * ); @endcode + * + * @note A default constructed iterator represents the end of the list. + * + * @note A successful call to this function is guaranteed to return at least + * one entry. + */ + template + iterator resolve(const endpoint_type& e, Error_Handler error_handler) + { + return this->service.resolve(this->implementation, e, error_handler); + } + + /// Asynchronously resolve an endpoint to a list of entries. + /** + * This function is used to asynchronously resolve an endpoint into a list of + * endpoint entries. + * + * @param e An endpoint object that determines what endpoints will be + * returned. + * + * @param handler The handler to be called when the resolve operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * const asio::error& error, // Result of operation. + * resolver::iterator iterator // Forward-only iterator that can be used to + * // traverse the list of endpoint entries. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_service::post(). + * + * @note A default constructed iterator represents the end of the list. + * + * @note A successful resolve operation is guaranteed to pass at least one + * entry to the handler. + */ + template + void async_resolve(const endpoint_type& e, Handler handler) + { + return this->service.async_resolve(this->implementation, e, handler); + } +}; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_BASIC_RESOLVER_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/basic_socket.hpp b/encryption/libtorrent/include/libtorrent/asio/basic_socket.hpp new file mode 100644 index 000000000..b0dc52e48 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/basic_socket.hpp @@ -0,0 +1,972 @@ +// +// basic_socket.hpp +// ~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_BASIC_SOCKET_HPP +#define ASIO_BASIC_SOCKET_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/basic_io_object.hpp" +#include "asio/error.hpp" +#include "asio/socket_base.hpp" +#include "asio/detail/throw_error.hpp" + +namespace asio { + +/// Provides socket functionality. +/** + * The basic_socket class template provides functionality that is common to both + * stream-oriented and datagram-oriented sockets. + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + */ +template +class basic_socket + : public basic_io_object, + public socket_base +{ +public: + /// The native representation of a socket. + typedef typename SocketService::native_type native_type; + + /// The protocol type. + typedef Protocol protocol_type; + + /// The endpoint type. + typedef typename Protocol::endpoint endpoint_type; + + /// A basic_socket is always the lowest layer. + typedef basic_socket lowest_layer_type; + + /// Construct a basic_socket without opening it. + /** + * This constructor creates a socket without opening it. + * + * @param io_service The io_service object that the socket will use to + * dispatch handlers for any asynchronous operations performed on the socket. + */ + explicit basic_socket(asio::io_service& io_service) + : basic_io_object(io_service) + { + } + + /// Construct and open a basic_socket. + /** + * This constructor creates and opens a socket. + * + * @param io_service The io_service object that the socket will use to + * dispatch handlers for any asynchronous operations performed on the socket. + * + * @param protocol An object specifying protocol parameters to be used. + * + * @throws asio::system_error Thrown on failure. + */ + basic_socket(asio::io_service& io_service, + const protocol_type& protocol) + : basic_io_object(io_service) + { + asio::error_code ec; + this->service.open(this->implementation, protocol, ec); + asio::detail::throw_error(ec); + } + + /// Construct a basic_socket, opening it and binding it to the given local + /// endpoint. + /** + * This constructor creates a socket and automatically opens it bound to the + * specified endpoint on the local machine. The protocol used is the protocol + * associated with the given endpoint. + * + * @param io_service The io_service object that the socket will use to + * dispatch handlers for any asynchronous operations performed on the socket. + * + * @param endpoint An endpoint on the local machine to which the socket will + * be bound. + * + * @throws asio::system_error Thrown on failure. + */ + basic_socket(asio::io_service& io_service, + const endpoint_type& endpoint) + : basic_io_object(io_service) + { + asio::error_code ec; + this->service.open(this->implementation, endpoint.protocol(), ec); + asio::detail::throw_error(ec); + this->service.bind(this->implementation, endpoint, ec); + asio::detail::throw_error(ec); + } + + /// Construct a basic_socket on an existing native socket. + /** + * This constructor creates a socket object to hold an existing native socket. + * + * @param io_service The io_service object that the socket will use to + * dispatch handlers for any asynchronous operations performed on the socket. + * + * @param protocol An object specifying protocol parameters to be used. + * + * @param native_socket A native socket. + * + * @throws asio::system_error Thrown on failure. + */ + basic_socket(asio::io_service& io_service, + const protocol_type& protocol, const native_type& native_socket) + : basic_io_object(io_service) + { + asio::error_code ec; + this->service.assign(this->implementation, protocol, native_socket, ec); + asio::detail::throw_error(ec); + } + + /// Get a reference to the lowest layer. + /** + * This function returns a reference to the lowest layer in a stack of + * layers. Since a basic_socket cannot contain any further layers, it simply + * returns a reference to itself. + * + * @return A reference to the lowest layer in the stack of layers. Ownership + * is not transferred to the caller. + */ + lowest_layer_type& lowest_layer() + { + return *this; + } + + /// Open the socket using the specified protocol. + /** + * This function opens the socket so that it will use the specified protocol. + * + * @param protocol An object specifying protocol parameters to be used. + * + * @throws asio::system_error Thrown on failure. + * + * @par Example + * @code + * asio::ip::tcp::socket socket(io_service); + * socket.open(asio::ip::tcp::v4()); + * @endcode + */ + void open(const protocol_type& protocol = protocol_type()) + { + asio::error_code ec; + this->service.open(this->implementation, protocol, ec); + asio::detail::throw_error(ec); + } + + /// Open the socket using the specified protocol. + /** + * This function opens the socket so that it will use the specified protocol. + * + * @param protocol An object specifying which protocol is to be used. + * + * @param ec Set to indicate what error occurred, if any. + * + * @par Example + * @code + * asio::ip::tcp::socket socket(io_service); + * asio::error_code ec; + * socket.open(asio::ip::tcp::v4(), ec); + * if (ec) + * { + * // An error occurred. + * } + * @endcode + */ + asio::error_code open(const protocol_type& protocol, + asio::error_code& ec) + { + return this->service.open(this->implementation, protocol, ec); + } + + /// Assign an existing native socket to the socket. + /* + * This function opens the socket to hold an existing native socket. + * + * @param protocol An object specifying which protocol is to be used. + * + * @param native_socket A native socket. + * + * @throws asio::system_error Thrown on failure. + */ + void assign(const protocol_type& protocol, const native_type& native_socket) + { + asio::error_code ec; + this->service.assign(this->implementation, protocol, native_socket, ec); + asio::detail::throw_error(ec); + } + + /// Assign an existing native socket to the socket. + /* + * This function opens the socket to hold an existing native socket. + * + * @param protocol An object specifying which protocol is to be used. + * + * @param native_socket A native socket. + * + * @param ec Set to indicate what error occurred, if any. + */ + asio::error_code assign(const protocol_type& protocol, + const native_type& native_socket, asio::error_code& ec) + { + return this->service.assign(this->implementation, + protocol, native_socket, ec); + } + + /// Determine whether the socket is open. + bool is_open() const + { + return this->service.is_open(this->implementation); + } + + /// Close the socket. + /** + * This function is used to close the socket. Any asynchronous send, receive + * or connect operations will be cancelled immediately, and will complete + * with the asio::error::operation_aborted error. + * + * @throws asio::system_error Thrown on failure. + */ + void close() + { + asio::error_code ec; + this->service.close(this->implementation, ec); + asio::detail::throw_error(ec); + } + + /// Close the socket. + /** + * This function is used to close the socket. Any asynchronous send, receive + * or connect operations will be cancelled immediately, and will complete + * with the asio::error::operation_aborted error. + * + * @param ec Set to indicate what error occurred, if any. + * + * @par Example + * @code + * asio::ip::tcp::socket socket(io_service); + * ... + * asio::error_code ec; + * socket.close(ec); + * if (ec) + * { + * // An error occurred. + * } + * @endcode + */ + asio::error_code close(asio::error_code& ec) + { + return this->service.close(this->implementation, ec); + } + + /// Get the native socket representation. + /** + * This function may be used to obtain the underlying representation of the + * socket. This is intended to allow access to native socket functionality + * that is not otherwise provided. + */ + native_type native() + { + return this->service.native(this->implementation); + } + + /// Cancel all asynchronous operations associated with the socket. + /** + * This function causes all outstanding asynchronous connect, send and receive + * operations to finish immediately, and the handlers for cancelled operations + * will be passed the asio::error::operation_aborted error. + * + * @throws asio::system_error Thrown on failure. + */ + void cancel() + { + asio::error_code ec; + this->service.cancel(this->implementation, ec); + asio::detail::throw_error(ec); + } + + /// Cancel all asynchronous operations associated with the socket. + /** + * This function causes all outstanding asynchronous connect, send and receive + * operations to finish immediately, and the handlers for cancelled operations + * will be passed the asio::error::operation_aborted error. + * + * @param ec Set to indicate what error occurred, if any. + */ + asio::error_code cancel(asio::error_code& ec) + { + return this->service.cancel(this->implementation, ec); + } + + /// Determine whether the socket is at the out-of-band data mark. + /** + * This function is used to check whether the socket input is currently + * positioned at the out-of-band data mark. + * + * @return A bool indicating whether the socket is at the out-of-band data + * mark. + * + * @throws asio::system_error Thrown on failure. + */ + bool at_mark() const + { + asio::error_code ec; + bool b = this->service.at_mark(this->implementation, ec); + asio::detail::throw_error(ec); + return b; + } + + /// Determine whether the socket is at the out-of-band data mark. + /** + * This function is used to check whether the socket input is currently + * positioned at the out-of-band data mark. + * + * @param ec Set to indicate what error occurred, if any. + * + * @return A bool indicating whether the socket is at the out-of-band data + * mark. + */ + bool at_mark(asio::error_code& ec) const + { + return this->service.at_mark(this->implementation, ec); + } + + /// Determine the number of bytes available for reading. + /** + * This function is used to determine the number of bytes that may be read + * without blocking. + * + * @return The number of bytes that may be read without blocking, or 0 if an + * error occurs. + * + * @throws asio::system_error Thrown on failure. + */ + std::size_t available() const + { + asio::error_code ec; + std::size_t s = this->service.available(this->implementation, ec); + asio::detail::throw_error(ec); + return s; + } + + /// Determine the number of bytes available for reading. + /** + * This function is used to determine the number of bytes that may be read + * without blocking. + * + * @param ec Set to indicate what error occurred, if any. + * + * @return The number of bytes that may be read without blocking, or 0 if an + * error occurs. + */ + std::size_t available(asio::error_code& ec) const + { + return this->service.available(this->implementation, ec); + } + + /// Bind the socket to the given local endpoint. + /** + * This function binds the socket to the specified endpoint on the local + * machine. + * + * @param endpoint An endpoint on the local machine to which the socket will + * be bound. + * + * @throws asio::system_error Thrown on failure. + * + * @par Example + * @code + * asio::ip::tcp::socket socket(io_service); + * socket.open(asio::ip::tcp::v4()); + * socket.bind(asio::ip::tcp::endpoint( + * asio::ip::tcp::v4(), 12345)); + * @endcode + */ + void bind(const endpoint_type& endpoint) + { + asio::error_code ec; + this->service.bind(this->implementation, endpoint, ec); + asio::detail::throw_error(ec); + } + + /// Bind the socket to the given local endpoint. + /** + * This function binds the socket to the specified endpoint on the local + * machine. + * + * @param endpoint An endpoint on the local machine to which the socket will + * be bound. + * + * @param ec Set to indicate what error occurred, if any. + * + * @par Example + * @code + * asio::ip::tcp::socket socket(io_service); + * socket.open(asio::ip::tcp::v4()); + * asio::error_code ec; + * socket.bind(asio::ip::tcp::endpoint( + * asio::ip::tcp::v4(), 12345), ec); + * if (ec) + * { + * // An error occurred. + * } + * @endcode + */ + asio::error_code bind(const endpoint_type& endpoint, + asio::error_code& ec) + { + return this->service.bind(this->implementation, endpoint, ec); + } + + /// Connect the socket to the specified endpoint. + /** + * This function is used to connect a socket to the specified remote endpoint. + * The function call will block until the connection is successfully made or + * an error occurs. + * + * The socket is automatically opened if it is not already open. If the + * connect fails, and the socket was automatically opened, the socket is + * returned to the closed state. + * + * @param peer_endpoint The remote endpoint to which the socket will be + * connected. + * + * @throws asio::system_error Thrown on failure. + * + * @par Example + * @code + * asio::ip::tcp::socket socket(io_service); + * asio::ip::tcp::endpoint endpoint( + * asio::ip::address::from_string("1.2.3.4"), 12345); + * socket.connect(endpoint); + * @endcode + */ + void connect(const endpoint_type& peer_endpoint) + { + asio::error_code ec; + if (!is_open()) + { + this->service.open(this->implementation, peer_endpoint.protocol(), ec); + asio::detail::throw_error(ec); + } + this->service.connect(this->implementation, peer_endpoint, ec); + asio::detail::throw_error(ec); + } + + /// Connect the socket to the specified endpoint. + /** + * This function is used to connect a socket to the specified remote endpoint. + * The function call will block until the connection is successfully made or + * an error occurs. + * + * The socket is automatically opened if it is not already open. If the + * connect fails, and the socket was automatically opened, the socket is + * returned to the closed state. + * + * @param peer_endpoint The remote endpoint to which the socket will be + * connected. + * + * @param ec Set to indicate what error occurred, if any. + * + * @par Example + * @code + * asio::ip::tcp::socket socket(io_service); + * asio::ip::tcp::endpoint endpoint( + * asio::ip::address::from_string("1.2.3.4"), 12345); + * asio::error_code ec; + * socket.connect(endpoint, ec); + * if (ec) + * { + * // An error occurred. + * } + * @endcode + */ + asio::error_code connect(const endpoint_type& peer_endpoint, + asio::error_code& ec) + { + if (!is_open()) + { + if (this->service.open(this->implementation, + peer_endpoint.protocol(), ec)) + { + return ec; + } + } + + return this->service.connect(this->implementation, peer_endpoint, ec); + } + + /// Start an asynchronous connect. + /** + * This function is used to asynchronously connect a socket to the specified + * remote endpoint. The function call always returns immediately. + * + * The socket is automatically opened if it is not already open. If the + * connect fails, and the socket was automatically opened, the socket is + * returned to the closed state. + * + * @param peer_endpoint The remote endpoint to which the socket will be + * connected. Copies will be made of the endpoint object as required. + * + * @param handler The handler to be called when the connection operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * const asio::error_code& error // Result of operation + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_service::post(). + * + * @par Example + * @code + * void connect_handler(const asio::error_code& error) + * { + * if (!error) + * { + * // Connect succeeded. + * } + * } + * + * ... + * + * asio::ip::tcp::socket socket(io_service); + * asio::ip::tcp::endpoint endpoint( + * asio::ip::address::from_string("1.2.3.4"), 12345); + * socket.async_connect(endpoint, connect_handler); + * @endcode + */ + template + void async_connect(const endpoint_type& peer_endpoint, ConnectHandler handler) + { + if (!is_open()) + { + asio::error_code ec; + if (this->service.open(this->implementation, + peer_endpoint.protocol(), ec)) + { + this->io_service().post(asio::detail::bind_handler(handler, ec)); + return; + } + } + + this->service.async_connect(this->implementation, peer_endpoint, handler); + } + + /// Set an option on the socket. + /** + * This function is used to set an option on the socket. + * + * @param option The new option value to be set on the socket. + * + * @throws asio::system_error Thrown on failure. + * + * @sa SettableSocketOption @n + * asio::socket_base::broadcast @n + * asio::socket_base::do_not_route @n + * asio::socket_base::keep_alive @n + * asio::socket_base::linger @n + * asio::socket_base::receive_buffer_size @n + * asio::socket_base::receive_low_watermark @n + * asio::socket_base::reuse_address @n + * asio::socket_base::send_buffer_size @n + * asio::socket_base::send_low_watermark @n + * asio::ip::multicast::join_group @n + * asio::ip::multicast::leave_group @n + * asio::ip::multicast::enable_loopback @n + * asio::ip::multicast::outbound_interface @n + * asio::ip::multicast::hops @n + * asio::ip::tcp::no_delay + * + * @par Example + * Setting the IPPROTO_TCP/TCP_NODELAY option: + * @code + * asio::ip::tcp::socket socket(io_service); + * ... + * asio::ip::tcp::no_delay option(true); + * socket.set_option(option); + * @endcode + */ + template + void set_option(const SettableSocketOption& option) + { + asio::error_code ec; + this->service.set_option(this->implementation, option, ec); + asio::detail::throw_error(ec); + } + + /// Set an option on the socket. + /** + * This function is used to set an option on the socket. + * + * @param option The new option value to be set on the socket. + * + * @param ec Set to indicate what error occurred, if any. + * + * @sa SettableSocketOption @n + * asio::socket_base::broadcast @n + * asio::socket_base::do_not_route @n + * asio::socket_base::keep_alive @n + * asio::socket_base::linger @n + * asio::socket_base::receive_buffer_size @n + * asio::socket_base::receive_low_watermark @n + * asio::socket_base::reuse_address @n + * asio::socket_base::send_buffer_size @n + * asio::socket_base::send_low_watermark @n + * asio::ip::multicast::join_group @n + * asio::ip::multicast::leave_group @n + * asio::ip::multicast::enable_loopback @n + * asio::ip::multicast::outbound_interface @n + * asio::ip::multicast::hops @n + * asio::ip::tcp::no_delay + * + * @par Example + * Setting the IPPROTO_TCP/TCP_NODELAY option: + * @code + * asio::ip::tcp::socket socket(io_service); + * ... + * asio::ip::tcp::no_delay option(true); + * asio::error_code ec; + * socket.set_option(option, ec); + * if (ec) + * { + * // An error occurred. + * } + * @endcode + */ + template + asio::error_code set_option(const SettableSocketOption& option, + asio::error_code& ec) + { + return this->service.set_option(this->implementation, option, ec); + } + + /// Get an option from the socket. + /** + * This function is used to get the current value of an option on the socket. + * + * @param option The option value to be obtained from the socket. + * + * @throws asio::system_error Thrown on failure. + * + * @sa GettableSocketOption @n + * asio::socket_base::broadcast @n + * asio::socket_base::do_not_route @n + * asio::socket_base::keep_alive @n + * asio::socket_base::linger @n + * asio::socket_base::receive_buffer_size @n + * asio::socket_base::receive_low_watermark @n + * asio::socket_base::reuse_address @n + * asio::socket_base::send_buffer_size @n + * asio::socket_base::send_low_watermark @n + * asio::ip::multicast::join_group @n + * asio::ip::multicast::leave_group @n + * asio::ip::multicast::enable_loopback @n + * asio::ip::multicast::outbound_interface @n + * asio::ip::multicast::hops @n + * asio::ip::tcp::no_delay + * + * @par Example + * Getting the value of the SOL_SOCKET/SO_KEEPALIVE option: + * @code + * asio::ip::tcp::socket socket(io_service); + * ... + * asio::ip::tcp::socket::keep_alive option; + * socket.get_option(option); + * bool is_set = option.get(); + * @endcode + */ + template + void get_option(GettableSocketOption& option) const + { + asio::error_code ec; + this->service.get_option(this->implementation, option, ec); + asio::detail::throw_error(ec); + } + + /// Get an option from the socket. + /** + * This function is used to get the current value of an option on the socket. + * + * @param option The option value to be obtained from the socket. + * + * @param ec Set to indicate what error occurred, if any. + * + * @sa GettableSocketOption @n + * asio::socket_base::broadcast @n + * asio::socket_base::do_not_route @n + * asio::socket_base::keep_alive @n + * asio::socket_base::linger @n + * asio::socket_base::receive_buffer_size @n + * asio::socket_base::receive_low_watermark @n + * asio::socket_base::reuse_address @n + * asio::socket_base::send_buffer_size @n + * asio::socket_base::send_low_watermark @n + * asio::ip::multicast::join_group @n + * asio::ip::multicast::leave_group @n + * asio::ip::multicast::enable_loopback @n + * asio::ip::multicast::outbound_interface @n + * asio::ip::multicast::hops @n + * asio::ip::tcp::no_delay + * + * @par Example + * Getting the value of the SOL_SOCKET/SO_KEEPALIVE option: + * @code + * asio::ip::tcp::socket socket(io_service); + * ... + * asio::ip::tcp::socket::keep_alive option; + * asio::error_code ec; + * socket.get_option(option, ec); + * if (ec) + * { + * // An error occurred. + * } + * bool is_set = option.get(); + * @endcode + */ + template + asio::error_code get_option(GettableSocketOption& option, + asio::error_code& ec) const + { + return this->service.get_option(this->implementation, option, ec); + } + + /// Perform an IO control command on the socket. + /** + * This function is used to execute an IO control command on the socket. + * + * @param command The IO control command to be performed on the socket. + * + * @throws asio::system_error Thrown on failure. + * + * @sa IoControlCommand @n + * asio::socket_base::bytes_readable @n + * asio::socket_base::non_blocking_io + * + * @par Example + * Getting the number of bytes ready to read: + * @code + * asio::ip::tcp::socket socket(io_service); + * ... + * asio::ip::tcp::socket::bytes_readable command; + * socket.io_control(command); + * std::size_t bytes_readable = command.get(); + * @endcode + */ + template + void io_control(IoControlCommand& command) + { + asio::error_code ec; + this->service.io_control(this->implementation, command, ec); + asio::detail::throw_error(ec); + } + + /// Perform an IO control command on the socket. + /** + * This function is used to execute an IO control command on the socket. + * + * @param command The IO control command to be performed on the socket. + * + * @param ec Set to indicate what error occurred, if any. + * + * @sa IoControlCommand @n + * asio::socket_base::bytes_readable @n + * asio::socket_base::non_blocking_io + * + * @par Example + * Getting the number of bytes ready to read: + * @code + * asio::ip::tcp::socket socket(io_service); + * ... + * asio::ip::tcp::socket::bytes_readable command; + * asio::error_code ec; + * socket.io_control(command, ec); + * if (ec) + * { + * // An error occurred. + * } + * std::size_t bytes_readable = command.get(); + * @endcode + */ + template + asio::error_code io_control(IoControlCommand& command, + asio::error_code& ec) + { + return this->service.io_control(this->implementation, command, ec); + } + + /// Get the local endpoint of the socket. + /** + * This function is used to obtain the locally bound endpoint of the socket. + * + * @returns An object that represents the local endpoint of the socket. + * + * @throws asio::system_error Thrown on failure. + * + * @par Example + * @code + * asio::ip::tcp::socket socket(io_service); + * ... + * asio::ip::tcp::endpoint endpoint = socket.local_endpoint(); + * @endcode + */ + endpoint_type local_endpoint() const + { + asio::error_code ec; + endpoint_type ep = this->service.local_endpoint(this->implementation, ec); + asio::detail::throw_error(ec); + return ep; + } + + /// Get the local endpoint of the socket. + /** + * This function is used to obtain the locally bound endpoint of the socket. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns An object that represents the local endpoint of the socket. + * Returns a default-constructed endpoint object if an error occurred. + * + * @par Example + * @code + * asio::ip::tcp::socket socket(io_service); + * ... + * asio::error_code ec; + * asio::ip::tcp::endpoint endpoint = socket.local_endpoint(ec); + * if (ec) + * { + * // An error occurred. + * } + * @endcode + */ + endpoint_type local_endpoint(asio::error_code& ec) const + { + return this->service.local_endpoint(this->implementation, ec); + } + + /// Get the remote endpoint of the socket. + /** + * This function is used to obtain the remote endpoint of the socket. + * + * @returns An object that represents the remote endpoint of the socket. + * + * @throws asio::system_error Thrown on failure. + * + * @par Example + * @code + * asio::ip::tcp::socket socket(io_service); + * ... + * asio::ip::tcp::endpoint endpoint = socket.remote_endpoint(); + * @endcode + */ + endpoint_type remote_endpoint() const + { + asio::error_code ec; + endpoint_type ep = this->service.remote_endpoint(this->implementation, ec); + asio::detail::throw_error(ec); + return ep; + } + + /// Get the remote endpoint of the socket. + /** + * This function is used to obtain the remote endpoint of the socket. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns An object that represents the remote endpoint of the socket. + * Returns a default-constructed endpoint object if an error occurred. + * + * @par Example + * @code + * asio::ip::tcp::socket socket(io_service); + * ... + * asio::error_code ec; + * asio::ip::tcp::endpoint endpoint = socket.remote_endpoint(ec); + * if (ec) + * { + * // An error occurred. + * } + * @endcode + */ + endpoint_type remote_endpoint(asio::error_code& ec) const + { + return this->service.remote_endpoint(this->implementation, ec); + } + + /// Disable sends or receives on the socket. + /** + * This function is used to disable send operations, receive operations, or + * both. + * + * @param what Determines what types of operation will no longer be allowed. + * + * @throws asio::system_error Thrown on failure. + * + * @par Example + * Shutting down the send side of the socket: + * @code + * asio::ip::tcp::socket socket(io_service); + * ... + * socket.shutdown(asio::ip::tcp::socket::shutdown_send); + * @endcode + */ + void shutdown(shutdown_type what) + { + asio::error_code ec; + this->service.shutdown(this->implementation, what, ec); + asio::detail::throw_error(ec); + } + + /// Disable sends or receives on the socket. + /** + * This function is used to disable send operations, receive operations, or + * both. + * + * @param what Determines what types of operation will no longer be allowed. + * + * @param ec Set to indicate what error occurred, if any. + * + * @par Example + * Shutting down the send side of the socket: + * @code + * asio::ip::tcp::socket socket(io_service); + * ... + * asio::error_code ec; + * socket.shutdown(asio::ip::tcp::socket::shutdown_send, ec); + * if (ec) + * { + * // An error occurred. + * } + * @endcode + */ + asio::error_code shutdown(shutdown_type what, + asio::error_code& ec) + { + return this->service.shutdown(this->implementation, what, ec); + } + +protected: + /// Protected destructor to prevent deletion through this type. + ~basic_socket() + { + } +}; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_BASIC_SOCKET_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/basic_socket_acceptor.hpp b/encryption/libtorrent/include/libtorrent/asio/basic_socket_acceptor.hpp new file mode 100644 index 000000000..a2d6a0356 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/basic_socket_acceptor.hpp @@ -0,0 +1,824 @@ +// +// basic_socket_acceptor.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_BASIC_SOCKET_ACCEPTOR_HPP +#define ASIO_BASIC_SOCKET_ACCEPTOR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/basic_io_object.hpp" +#include "asio/basic_socket.hpp" +#include "asio/error.hpp" +#include "asio/socket_acceptor_service.hpp" +#include "asio/socket_base.hpp" +#include "asio/detail/throw_error.hpp" + +namespace asio { + +/// Provides the ability to accept new connections. +/** + * The basic_socket_acceptor class template is used for accepting new socket + * connections. + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + * + * @par Example + * Opening a socket acceptor with the SO_REUSEADDR option enabled: + * @code + * asio::ip::tcp::acceptor acceptor(io_service); + * asio::ip::tcp::endpoint endpoint(asio::ip::tcp::v4(), port); + * acceptor.open(endpoint.protocol()); + * acceptor.set_option(asio::ip::tcp::acceptor::reuse_address(true)); + * acceptor.bind(endpoint); + * acceptor.listen(); + * @endcode + */ +template > +class basic_socket_acceptor + : public basic_io_object, + public socket_base +{ +public: + /// The native representation of an acceptor. + typedef typename SocketAcceptorService::native_type native_type; + + /// The protocol type. + typedef Protocol protocol_type; + + /// The endpoint type. + typedef typename Protocol::endpoint endpoint_type; + + /// Construct an acceptor without opening it. + /** + * This constructor creates an acceptor without opening it to listen for new + * connections. The open() function must be called before the acceptor can + * accept new socket connections. + * + * @param io_service The io_service object that the acceptor will use to + * dispatch handlers for any asynchronous operations performed on the + * acceptor. + */ + explicit basic_socket_acceptor(asio::io_service& io_service) + : basic_io_object(io_service) + { + } + + /// Construct an open acceptor. + /** + * This constructor creates an acceptor and automatically opens it. + * + * @param io_service The io_service object that the acceptor will use to + * dispatch handlers for any asynchronous operations performed on the + * acceptor. + * + * @param protocol An object specifying protocol parameters to be used. + * + * @throws asio::system_error Thrown on failure. + */ + basic_socket_acceptor(asio::io_service& io_service, + const protocol_type& protocol) + : basic_io_object(io_service) + { + asio::error_code ec; + this->service.open(this->implementation, protocol, ec); + asio::detail::throw_error(ec); + } + + /// Construct an acceptor opened on the given endpoint. + /** + * This constructor creates an acceptor and automatically opens it to listen + * for new connections on the specified endpoint. + * + * @param io_service The io_service object that the acceptor will use to + * dispatch handlers for any asynchronous operations performed on the + * acceptor. + * + * @param endpoint An endpoint on the local machine on which the acceptor + * will listen for new connections. + * + * @param reuse_addr Whether the constructor should set the socket option + * socket_base::reuse_address. + * + * @throws asio::system_error Thrown on failure. + * + * @note This constructor is equivalent to the following code: + * @code + * basic_socket_acceptor acceptor(io_service); + * acceptor.open(endpoint.protocol()); + * if (reuse_addr) + * acceptor.set_option(socket_base::reuse_address(true)); + * acceptor.bind(endpoint); + * acceptor.listen(listen_backlog); + * @endcode + */ + basic_socket_acceptor(asio::io_service& io_service, + const endpoint_type& endpoint, bool reuse_addr = true) + : basic_io_object(io_service) + { + asio::error_code ec; + this->service.open(this->implementation, endpoint.protocol(), ec); + asio::detail::throw_error(ec); + if (reuse_addr) + { + this->service.set_option(this->implementation, + socket_base::reuse_address(true), ec); + asio::detail::throw_error(ec); + } + this->service.bind(this->implementation, endpoint, ec); + asio::detail::throw_error(ec); + this->service.listen(this->implementation, + socket_base::max_connections, ec); + asio::detail::throw_error(ec); + } + + /// Construct a basic_socket_acceptor on an existing native acceptor. + /** + * This constructor creates an acceptor object to hold an existing native + * acceptor. + * + * @param io_service The io_service object that the acceptor will use to + * dispatch handlers for any asynchronous operations performed on the + * acceptor. + * + * @param protocol An object specifying protocol parameters to be used. + * + * @param native_acceptor A native acceptor. + * + * @throws asio::system_error Thrown on failure. + */ + basic_socket_acceptor(asio::io_service& io_service, + const protocol_type& protocol, const native_type& native_acceptor) + : basic_io_object(io_service) + { + asio::error_code ec; + this->service.assign(this->implementation, protocol, native_acceptor, ec); + asio::detail::throw_error(ec); + } + + /// Open the acceptor using the specified protocol. + /** + * This function opens the socket acceptor so that it will use the specified + * protocol. + * + * @param protocol An object specifying which protocol is to be used. + * + * @throws asio::system_error Thrown on failure. + * + * @par Example + * @code + * asio::ip::tcp::acceptor acceptor(io_service); + * acceptor.open(asio::ip::tcp::v4()); + * @endcode + */ + void open(const protocol_type& protocol = protocol_type()) + { + asio::error_code ec; + this->service.open(this->implementation, protocol, ec); + asio::detail::throw_error(ec); + } + + /// Open the acceptor using the specified protocol. + /** + * This function opens the socket acceptor so that it will use the specified + * protocol. + * + * @param protocol An object specifying which protocol is to be used. + * + * @param ec Set to indicate what error occurred, if any. + * + * @par Example + * @code + * asio::ip::tcp::acceptor acceptor(io_service); + * asio::error_code ec; + * acceptor.open(asio::ip::tcp::v4(), ec); + * if (ec) + * { + * // An error occurred. + * } + * @endcode + */ + asio::error_code open(const protocol_type& protocol, + asio::error_code& ec) + { + return this->service.open(this->implementation, protocol, ec); + } + + /// Assigns an existing native acceptor to the acceptor. + /* + * This function opens the acceptor to hold an existing native acceptor. + * + * @param protocol An object specifying which protocol is to be used. + * + * @param native_acceptor A native acceptor. + * + * @throws asio::system_error Thrown on failure. + */ + void assign(const protocol_type& protocol, const native_type& native_acceptor) + { + asio::error_code ec; + this->service.assign(this->implementation, protocol, native_acceptor, ec); + asio::detail::throw_error(ec); + } + + /// Assigns an existing native acceptor to the acceptor. + /* + * This function opens the acceptor to hold an existing native acceptor. + * + * @param protocol An object specifying which protocol is to be used. + * + * @param native_acceptor A native acceptor. + * + * @param ec Set to indicate what error occurred, if any. + */ + asio::error_code assign(const protocol_type& protocol, + const native_type& native_acceptor, asio::error_code& ec) + { + return this->service.assign(this->implementation, + protocol, native_acceptor, ec); + } + + /// Determine whether the acceptor is open. + bool is_open() const + { + return this->service.is_open(this->implementation); + } + + /// Bind the acceptor to the given local endpoint. + /** + * This function binds the socket acceptor to the specified endpoint on the + * local machine. + * + * @param endpoint An endpoint on the local machine to which the socket + * acceptor will be bound. + * + * @throws asio::system_error Thrown on failure. + * + * @par Example + * @code + * asio::ip::tcp::acceptor acceptor(io_service); + * acceptor.open(asio::ip::tcp::v4()); + * acceptor.bind(asio::ip::tcp::endpoint(12345)); + * @endcode + */ + void bind(const endpoint_type& endpoint) + { + asio::error_code ec; + this->service.bind(this->implementation, endpoint, ec); + asio::detail::throw_error(ec); + } + + /// Bind the acceptor to the given local endpoint. + /** + * This function binds the socket acceptor to the specified endpoint on the + * local machine. + * + * @param endpoint An endpoint on the local machine to which the socket + * acceptor will be bound. + * + * @param ec Set to indicate what error occurred, if any. + * + * @par Example + * @code + * asio::ip::tcp::acceptor acceptor(io_service); + * acceptor.open(asio::ip::tcp::v4()); + * asio::error_code ec; + * acceptor.bind(asio::ip::tcp::endpoint(12345), ec); + * if (ec) + * { + * // An error occurred. + * } + * @endcode + */ + asio::error_code bind(const endpoint_type& endpoint, + asio::error_code& ec) + { + return this->service.bind(this->implementation, endpoint, ec); + } + + /// Place the acceptor into the state where it will listen for new + /// connections. + /** + * This function puts the socket acceptor into the state where it may accept + * new connections. + * + * @param backlog The maximum length of the queue of pending connections. + * + * @throws asio::system_error Thrown on failure. + */ + void listen(int backlog = socket_base::max_connections) + { + asio::error_code ec; + this->service.listen(this->implementation, backlog, ec); + asio::detail::throw_error(ec); + } + + /// Place the acceptor into the state where it will listen for new + /// connections. + /** + * This function puts the socket acceptor into the state where it may accept + * new connections. + * + * @param backlog The maximum length of the queue of pending connections. + * + * @param ec Set to indicate what error occurred, if any. + * + * @par Example + * @code + * asio::ip::tcp::acceptor acceptor(io_service); + * ... + * asio::error_code ec; + * acceptor.listen(asio::socket_base::max_connections, ec); + * if (ec) + * { + * // An error occurred. + * } + * @endcode + */ + asio::error_code listen(int backlog, asio::error_code& ec) + { + return this->service.listen(this->implementation, backlog, ec); + } + + /// Close the acceptor. + /** + * This function is used to close the acceptor. Any asynchronous accept + * operations will be cancelled immediately. + * + * A subsequent call to open() is required before the acceptor can again be + * used to again perform socket accept operations. + * + * @throws asio::system_error Thrown on failure. + */ + void close() + { + asio::error_code ec; + this->service.close(this->implementation, ec); + asio::detail::throw_error(ec); + } + + /// Close the acceptor. + /** + * This function is used to close the acceptor. Any asynchronous accept + * operations will be cancelled immediately. + * + * A subsequent call to open() is required before the acceptor can again be + * used to again perform socket accept operations. + * + * @param ec Set to indicate what error occurred, if any. + * + * @par Example + * @code + * asio::ip::tcp::acceptor acceptor(io_service); + * ... + * asio::error_code ec; + * acceptor.close(ec); + * if (ec) + * { + * // An error occurred. + * } + * @endcode + */ + asio::error_code close(asio::error_code& ec) + { + return this->service.close(this->implementation, ec); + } + + /// Get the native acceptor representation. + /** + * This function may be used to obtain the underlying representation of the + * acceptor. This is intended to allow access to native acceptor functionality + * that is not otherwise provided. + */ + native_type native() + { + return this->service.native(this->implementation); + } + + /// Cancel all asynchronous operations associated with the acceptor. + /** + * This function causes all outstanding asynchronous connect, send and receive + * operations to finish immediately, and the handlers for cancelled operations + * will be passed the asio::error::operation_aborted error. + * + * @throws asio::system_error Thrown on failure. + */ + void cancel() + { + asio::error_code ec; + this->service.cancel(this->implementation, ec); + asio::detail::throw_error(ec); + } + + /// Cancel all asynchronous operations associated with the acceptor. + /** + * This function causes all outstanding asynchronous connect, send and receive + * operations to finish immediately, and the handlers for cancelled operations + * will be passed the asio::error::operation_aborted error. + * + * @param ec Set to indicate what error occurred, if any. + */ + asio::error_code cancel(asio::error_code& ec) + { + return this->service.cancel(this->implementation, ec); + } + + /// Set an option on the acceptor. + /** + * This function is used to set an option on the acceptor. + * + * @param option The new option value to be set on the acceptor. + * + * @throws asio::system_error Thrown on failure. + * + * @sa SettableSocketOption @n + * asio::socket_base::reuse_address + * asio::socket_base::enable_connection_aborted + * + * @par Example + * Setting the SOL_SOCKET/SO_REUSEADDR option: + * @code + * asio::ip::tcp::acceptor acceptor(io_service); + * ... + * asio::ip::tcp::acceptor::reuse_address option(true); + * acceptor.set_option(option); + * @endcode + */ + template + void set_option(const SettableSocketOption& option) + { + asio::error_code ec; + this->service.set_option(this->implementation, option, ec); + asio::detail::throw_error(ec); + } + + /// Set an option on the acceptor. + /** + * This function is used to set an option on the acceptor. + * + * @param option The new option value to be set on the acceptor. + * + * @param ec Set to indicate what error occurred, if any. + * + * @sa SettableSocketOption @n + * asio::socket_base::reuse_address + * asio::socket_base::enable_connection_aborted + * + * @par Example + * Setting the SOL_SOCKET/SO_REUSEADDR option: + * @code + * asio::ip::tcp::acceptor acceptor(io_service); + * ... + * asio::ip::tcp::acceptor::reuse_address option(true); + * asio::error_code ec; + * acceptor.set_option(option, ec); + * if (ec) + * { + * // An error occurred. + * } + * @endcode + */ + template + asio::error_code set_option(const SettableSocketOption& option, + asio::error_code& ec) + { + return this->service.set_option(this->implementation, option, ec); + } + + /// Get an option from the acceptor. + /** + * This function is used to get the current value of an option on the + * acceptor. + * + * @param option The option value to be obtained from the acceptor. + * + * @throws asio::system_error Thrown on failure. + * + * @sa GettableSocketOption @n + * asio::socket_base::reuse_address + * + * @par Example + * Getting the value of the SOL_SOCKET/SO_REUSEADDR option: + * @code + * asio::ip::tcp::acceptor acceptor(io_service); + * ... + * asio::ip::tcp::acceptor::reuse_address option; + * acceptor.get_option(option); + * bool is_set = option.get(); + * @endcode + */ + template + void get_option(GettableSocketOption& option) + { + asio::error_code ec; + this->service.get_option(this->implementation, option, ec); + asio::detail::throw_error(ec); + } + + /// Get an option from the acceptor. + /** + * This function is used to get the current value of an option on the + * acceptor. + * + * @param option The option value to be obtained from the acceptor. + * + * @param ec Set to indicate what error occurred, if any. + * + * @sa GettableSocketOption @n + * asio::socket_base::reuse_address + * + * @par Example + * Getting the value of the SOL_SOCKET/SO_REUSEADDR option: + * @code + * asio::ip::tcp::acceptor acceptor(io_service); + * ... + * asio::ip::tcp::acceptor::reuse_address option; + * asio::error_code ec; + * acceptor.get_option(option, ec); + * if (ec) + * { + * // An error occurred. + * } + * bool is_set = option.get(); + * @endcode + */ + template + asio::error_code get_option(GettableSocketOption& option, + asio::error_code& ec) + { + return this->service.get_option(this->implementation, option, ec); + } + + /// Get the local endpoint of the acceptor. + /** + * This function is used to obtain the locally bound endpoint of the acceptor. + * + * @returns An object that represents the local endpoint of the acceptor. + * + * @throws asio::system_error Thrown on failure. + * + * @par Example + * @code + * asio::ip::tcp::acceptor acceptor(io_service); + * ... + * asio::ip::tcp::endpoint endpoint = acceptor.local_endpoint(); + * @endcode + */ + endpoint_type local_endpoint() const + { + asio::error_code ec; + endpoint_type ep = this->service.local_endpoint(this->implementation, ec); + asio::detail::throw_error(ec); + return ep; + } + + /// Get the local endpoint of the acceptor. + /** + * This function is used to obtain the locally bound endpoint of the acceptor. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns An object that represents the local endpoint of the acceptor. + * Returns a default-constructed endpoint object if an error occurred and the + * error handler did not throw an exception. + * + * @par Example + * @code + * asio::ip::tcp::acceptor acceptor(io_service); + * ... + * asio::error_code ec; + * asio::ip::tcp::endpoint endpoint = acceptor.local_endpoint(ec); + * if (ec) + * { + * // An error occurred. + * } + * @endcode + */ + endpoint_type local_endpoint(asio::error_code& ec) const + { + return this->service.local_endpoint(this->implementation, ec); + } + + /// Accept a new connection. + /** + * This function is used to accept a new connection from a peer into the + * given socket. The function call will block until a new connection has been + * accepted successfully or an error occurs. + * + * @param peer The socket into which the new connection will be accepted. + * + * @throws asio::system_error Thrown on failure. + * + * @par Example + * @code + * asio::ip::tcp::acceptor acceptor(io_service); + * ... + * asio::ip::tcp::socket socket(io_service); + * acceptor.accept(socket); + * @endcode + */ + template + void accept(basic_socket& peer) + { + asio::error_code ec; + this->service.accept(this->implementation, peer, 0, ec); + asio::detail::throw_error(ec); + } + + /// Accept a new connection. + /** + * This function is used to accept a new connection from a peer into the + * given socket. The function call will block until a new connection has been + * accepted successfully or an error occurs. + * + * @param peer The socket into which the new connection will be accepted. + * + * @param ec Set to indicate what error occurred, if any. + * + * @par Example + * @code + * asio::ip::tcp::acceptor acceptor(io_service); + * ... + * asio::ip::tcp::soocket socket(io_service); + * asio::error_code ec; + * acceptor.accept(socket, ec); + * if (ec) + * { + * // An error occurred. + * } + * @endcode + */ + template + asio::error_code accept( + basic_socket& peer, + asio::error_code& ec) + { + return this->service.accept(this->implementation, peer, 0, ec); + } + + /// Start an asynchronous accept. + /** + * This function is used to asynchronously accept a new connection into a + * socket. The function call always returns immediately. + * + * @param peer The socket into which the new connection will be accepted. + * Ownership of the peer object is retained by the caller, which must + * guarantee that it is valid until the handler is called. + * + * @param handler The handler to be called when the accept operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * const asio::error_code& error // Result of operation. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_service::post(). + * + * @par Example + * @code + * void accept_handler(const asio::error_code& error) + * { + * if (!error) + * { + * // Accept succeeded. + * } + * } + * + * ... + * + * asio::ip::tcp::acceptor acceptor(io_service); + * ... + * asio::ip::tcp::socket socket(io_service); + * acceptor.async_accept(socket, accept_handler); + * @endcode + */ + template + void async_accept(basic_socket& peer, + AcceptHandler handler) + { + this->service.async_accept(this->implementation, peer, 0, handler); + } + + /// Accept a new connection and obtain the endpoint of the peer + /** + * This function is used to accept a new connection from a peer into the + * given socket, and additionally provide the endpoint of the remote peer. + * The function call will block until a new connection has been accepted + * successfully or an error occurs. + * + * @param peer The socket into which the new connection will be accepted. + * + * @param peer_endpoint An endpoint object which will receive the endpoint of + * the remote peer. + * + * @throws asio::system_error Thrown on failure. + * + * @par Example + * @code + * asio::ip::tcp::acceptor acceptor(io_service); + * ... + * asio::ip::tcp::socket socket(io_service); + * asio::ip::tcp::endpoint endpoint; + * acceptor.accept(socket, endpoint); + * @endcode + */ + template + void accept(basic_socket& peer, + endpoint_type& peer_endpoint) + { + asio::error_code ec; + this->service.accept(this->implementation, peer, &peer_endpoint, ec); + asio::detail::throw_error(ec); + } + + /// Accept a new connection and obtain the endpoint of the peer + /** + * This function is used to accept a new connection from a peer into the + * given socket, and additionally provide the endpoint of the remote peer. + * The function call will block until a new connection has been accepted + * successfully or an error occurs. + * + * @param peer The socket into which the new connection will be accepted. + * + * @param peer_endpoint An endpoint object which will receive the endpoint of + * the remote peer. + * + * @param ec Set to indicate what error occurred, if any. + * + * @par Example + * @code + * asio::ip::tcp::acceptor acceptor(io_service); + * ... + * asio::ip::tcp::socket socket(io_service); + * asio::ip::tcp::endpoint endpoint; + * asio::error_code ec; + * acceptor.accept(socket, endpoint, ec); + * if (ec) + * { + * // An error occurred. + * } + * @endcode + */ + template + asio::error_code accept( + basic_socket& peer, + endpoint_type& peer_endpoint, asio::error_code& ec) + { + return this->service.accept(this->implementation, peer, &peer_endpoint, ec); + } + + /// Start an asynchronous accept. + /** + * This function is used to asynchronously accept a new connection into a + * socket, and additionally obtain the endpoint of the remote peer. The + * function call always returns immediately. + * + * @param peer The socket into which the new connection will be accepted. + * Ownership of the peer object is retained by the caller, which must + * guarantee that it is valid until the handler is called. + * + * @param peer_endpoint An endpoint object into which the endpoint of the + * remote peer will be written. Ownership of the peer_endpoint object is + * retained by the caller, which must guarantee that it is valid until the + * handler is called. + * + * @param handler The handler to be called when the accept operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * const asio::error_code& error // Result of operation. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_service::post(). + */ + template + void async_accept(basic_socket& peer, + endpoint_type& peer_endpoint, AcceptHandler handler) + { + this->service.async_accept(this->implementation, + peer, &peer_endpoint, handler); + } +}; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_BASIC_SOCKET_ACCEPTOR_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/basic_socket_iostream.hpp b/encryption/libtorrent/include/libtorrent/asio/basic_socket_iostream.hpp new file mode 100644 index 000000000..c48da7b62 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/basic_socket_iostream.hpp @@ -0,0 +1,146 @@ +// +// basic_socket_iostream.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_BASIC_SOCKET_IOSTREAM_HPP +#define ASIO_BASIC_SOCKET_IOSTREAM_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/basic_socket_streambuf.hpp" +#include "asio/stream_socket_service.hpp" + +#if !defined(ASIO_SOCKET_IOSTREAM_MAX_ARITY) +#define ASIO_SOCKET_IOSTREAM_MAX_ARITY 5 +#endif // !defined(ASIO_SOCKET_IOSTREAM_MAX_ARITY) + +// A macro that should expand to: +// template +// explicit basic_socket_iostream(T1 x1, ..., Tn xn) +// : basic_iostream(&this->boost::base_from_member< +// basic_socket_streambuf >::member) +// { +// if (rdbuf()->connect(x1, ..., xn) == 0) +// this->setstate(std::ios_base::failbit); +// } +// This macro should only persist within this file. + +#define ASIO_PRIVATE_CTR_DEF(z, n, data) \ + template \ + explicit basic_socket_iostream(BOOST_PP_ENUM_BINARY_PARAMS(n, T, x)) \ + : std::basic_iostream(&this->boost::base_from_member< \ + basic_socket_streambuf >::member) \ + { \ + if (rdbuf()->connect(BOOST_PP_ENUM_PARAMS(n, x)) == 0) \ + this->setstate(std::ios_base::failbit); \ + } \ + /**/ + +// A macro that should expand to: +// template +// void connect(T1 x1, ..., Tn xn) +// { +// if (rdbuf()->connect(x1, ..., xn) == 0) +// this->setstate(std::ios_base::failbit); +// } +// This macro should only persist within this file. + +#define ASIO_PRIVATE_CONNECT_DEF(z, n, data) \ + template \ + void connect(BOOST_PP_ENUM_BINARY_PARAMS(n, T, x)) \ + { \ + if (rdbuf()->connect(BOOST_PP_ENUM_PARAMS(n, x)) == 0) \ + this->setstate(std::ios_base::failbit); \ + } \ + /**/ + +namespace asio { + +/// Iostream interface for a socket. +template > +class basic_socket_iostream + : public boost::base_from_member< + basic_socket_streambuf >, + public std::basic_iostream +{ +public: + /// Construct a basic_socket_iostream without establishing a connection. + basic_socket_iostream() + : std::basic_iostream(&this->boost::base_from_member< + basic_socket_streambuf >::member) + { + } + +#if defined(GENERATING_DOCUMENTATION) + /// Establish a connection to an endpoint corresponding to a resolver query. + /** + * This constructor automatically establishes a connection based on the + * supplied resolver query parameters. The arguments are used to construct + * a resolver query object. + */ + template + explicit basic_socket_iostream(T1 t1, ..., TN tn); +#else + BOOST_PP_REPEAT_FROM_TO( + 1, BOOST_PP_INC(ASIO_SOCKET_IOSTREAM_MAX_ARITY), + ASIO_PRIVATE_CTR_DEF, _ ) +#endif + +#if defined(GENERATING_DOCUMENTATION) + /// Establish a connection to an endpoint corresponding to a resolver query. + /** + * This function automatically establishes a connection based on the supplied + * resolver query parameters. The arguments are used to construct a resolver + * query object. + */ + template + void connect(T1 t1, ..., TN tn); +#else + BOOST_PP_REPEAT_FROM_TO( + 1, BOOST_PP_INC(ASIO_SOCKET_IOSTREAM_MAX_ARITY), + ASIO_PRIVATE_CONNECT_DEF, _ ) +#endif + + /// Close the connection. + void close() + { + if (rdbuf()->close() == 0) + this->setstate(std::ios_base::failbit); + } + + /// Return a pointer to the underlying streambuf. + basic_socket_streambuf* rdbuf() const + { + return const_cast*>( + &this->boost::base_from_member< + basic_socket_streambuf >::member); + } +}; + +} // namespace asio + +#undef ASIO_PRIVATE_CTR_DEF +#undef ASIO_PRIVATE_CONNECT_DEF + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_BASIC_SOCKET_IOSTREAM_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/basic_socket_streambuf.hpp b/encryption/libtorrent/include/libtorrent/asio/basic_socket_streambuf.hpp new file mode 100644 index 000000000..2c4189c52 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/basic_socket_streambuf.hpp @@ -0,0 +1,284 @@ +// +// basic_socket_streambuf.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_BASIC_SOCKET_STREAMBUF_HPP +#define ASIO_BASIC_SOCKET_STREAMBUF_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include +#include +#include +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/basic_socket.hpp" +#include "asio/io_service.hpp" +#include "asio/stream_socket_service.hpp" +#include "asio/detail/throw_error.hpp" + +#if !defined(ASIO_SOCKET_STREAMBUF_MAX_ARITY) +#define ASIO_SOCKET_STREAMBUF_MAX_ARITY 5 +#endif // !defined(ASIO_SOCKET_STREAMBUF_MAX_ARITY) + +// A macro that should expand to: +// template +// basic_socket_streambuf* connect( +// T1 x1, ..., Tn xn) +// { +// init_buffers(); +// asio::error_code ec; +// this->basic_socket::close(ec); +// typedef typename Protocol::resolver_query resolver_query; +// resolver_query query(x1, ..., xn); +// resolve_and_connect(query, ec); +// return !ec ? this : 0; +// } +// This macro should only persist within this file. + +#define ASIO_PRIVATE_CONNECT_DEF( z, n, data ) \ + template \ + basic_socket_streambuf* connect( \ + BOOST_PP_ENUM_BINARY_PARAMS(n, T, x)) \ + { \ + init_buffers(); \ + asio::error_code ec; \ + this->basic_socket::close(ec); \ + typedef typename Protocol::resolver_query resolver_query; \ + resolver_query query(BOOST_PP_ENUM_PARAMS(n, x)); \ + resolve_and_connect(query, ec); \ + return !ec ? this : 0; \ + } \ + /**/ + +namespace asio { + +/// Iostream streambuf for a socket. +template > +class basic_socket_streambuf + : public std::streambuf, + private boost::base_from_member, + public basic_socket +{ +public: + /// The endpoint type. + typedef typename Protocol::endpoint endpoint_type; + + /// Construct a basic_socket_streambuf without establishing a connection. + basic_socket_streambuf() + : basic_socket( + boost::base_from_member::member), + unbuffered_(false) + { + init_buffers(); + } + + /// Destructor flushes buffered data. + virtual ~basic_socket_streambuf() + { + if (pptr() != pbase()) + overflow(traits_type::eof()); + } + + /// Establish a connection. + /** + * This function establishes a connection to the specified endpoint. + * + * @return \c this if a connection was successfully established, a null + * pointer otherwise. + */ + basic_socket_streambuf* connect( + const endpoint_type& endpoint) + { + init_buffers(); + asio::error_code ec; + this->basic_socket::close(ec); + this->basic_socket::connect(endpoint, ec); + return !ec ? this : 0; + } + +#if defined(GENERATING_DOCUMENTATION) + /// Establish a connection. + /** + * This function automatically establishes a connection based on the supplied + * resolver query parameters. The arguments are used to construct a resolver + * query object. + * + * @return \c this if a connection was successfully established, a null + * pointer otherwise. + */ + template + basic_socket_streambuf* connect( + T1 t1, ..., TN tn); +#else + BOOST_PP_REPEAT_FROM_TO( + 1, BOOST_PP_INC(ASIO_SOCKET_STREAMBUF_MAX_ARITY), + ASIO_PRIVATE_CONNECT_DEF, _ ) +#endif + + /// Close the connection. + /** + * @return \c this if a connection was successfully established, a null + * pointer otherwise. + */ + basic_socket_streambuf* close() + { + asio::error_code ec; + sync(); + this->basic_socket::close(ec); + if (!ec) + init_buffers(); + return !ec ? this : 0; + } + +protected: + int_type underflow() + { + if (gptr() == egptr()) + { + asio::error_code ec; + std::size_t bytes_transferred = this->service.receive( + this->implementation, + asio::buffer(asio::buffer(get_buffer_) + putback_max), + 0, ec); + if (ec) + return traits_type::eof(); + setg(get_buffer_.begin(), get_buffer_.begin() + putback_max, + get_buffer_.begin() + putback_max + bytes_transferred); + return traits_type::to_int_type(*gptr()); + } + else + { + return traits_type::eof(); + } + } + + int_type overflow(int_type c) + { + if (unbuffered_) + { + if (traits_type::eq_int_type(c, traits_type::eof())) + { + // Nothing to do. + return traits_type::not_eof(c); + } + else + { + // Send the single character immediately. + asio::error_code ec; + char_type ch = traits_type::to_char_type(c); + this->service.send(this->implementation, + asio::buffer(&ch, sizeof(char_type)), 0, ec); + if (ec) + return traits_type::eof(); + return c; + } + } + else + { + // Send all data in the output buffer. + asio::const_buffer buffer = + asio::buffer(pbase(), pptr() - pbase()); + while (asio::buffer_size(buffer) > 0) + { + asio::error_code ec; + std::size_t bytes_transferred = this->service.send( + this->implementation, asio::buffer(buffer), + 0, ec); + if (ec) + return traits_type::eof(); + buffer = buffer + bytes_transferred; + } + setp(put_buffer_.begin(), put_buffer_.end()); + + // If the new character is eof then our work here is done. + if (traits_type::eq_int_type(c, traits_type::eof())) + return traits_type::not_eof(c); + + // Add the new character to the output buffer. + *pptr() = traits_type::to_char_type(c); + pbump(1); + return c; + } + } + + int sync() + { + return overflow(traits_type::eof()); + } + + std::streambuf* setbuf(char_type* s, std::streamsize n) + { + if (pptr() == pbase() && s == 0 && n == 0) + { + unbuffered_ = true; + setp(0, 0); + return this; + } + + return 0; + } + +private: + void init_buffers() + { + setg(get_buffer_.begin(), + get_buffer_.begin() + putback_max, + get_buffer_.begin() + putback_max); + if (unbuffered_) + setp(0, 0); + else + setp(put_buffer_.begin(), put_buffer_.end()); + } + + void resolve_and_connect(const typename Protocol::resolver_query& query, + asio::error_code& ec) + { + typedef typename Protocol::resolver resolver_type; + typedef typename Protocol::resolver_iterator iterator_type; + resolver_type resolver( + boost::base_from_member::member); + iterator_type i = resolver.resolve(query, ec); + if (!ec) + { + iterator_type end; + ec = asio::error::host_not_found; + while (ec && i != end) + { + this->basic_socket::close(); + this->basic_socket::connect(*i, ec); + ++i; + } + } + } + + enum { putback_max = 8 }; + enum { buffer_size = 512 }; + boost::array get_buffer_; + boost::array put_buffer_; + bool unbuffered_; +}; + +} // namespace asio + +#undef ASIO_PRIVATE_CONNECT_DEF + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_BASIC_SOCKET_STREAMBUF_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/basic_stream_socket.hpp b/encryption/libtorrent/include/libtorrent/asio/basic_stream_socket.hpp new file mode 100644 index 000000000..59889dc33 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/basic_stream_socket.hpp @@ -0,0 +1,718 @@ +// +// basic_stream_socket.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_BASIC_STREAM_SOCKET_HPP +#define ASIO_BASIC_STREAM_SOCKET_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/basic_socket.hpp" +#include "asio/error.hpp" +#include "asio/stream_socket_service.hpp" +#include "asio/detail/throw_error.hpp" + +namespace asio { + +/// Provides stream-oriented socket functionality. +/** + * The basic_stream_socket class template provides asynchronous and blocking + * stream-oriented socket functionality. + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + * + * @par Concepts: + * AsyncReadStream, AsyncWriteStream, Stream, SyncReadStream, SyncWriteStream. + */ +template > +class basic_stream_socket + : public basic_socket +{ +public: + /// The native representation of a socket. + typedef typename StreamSocketService::native_type native_type; + + /// The protocol type. + typedef Protocol protocol_type; + + /// The endpoint type. + typedef typename Protocol::endpoint endpoint_type; + + /// Construct a basic_stream_socket without opening it. + /** + * This constructor creates a stream socket without opening it. The socket + * needs to be opened and then connected or accepted before data can be sent + * or received on it. + * + * @param io_service The io_service object that the stream socket will use to + * dispatch handlers for any asynchronous operations performed on the socket. + */ + explicit basic_stream_socket(asio::io_service& io_service) + : basic_socket(io_service) + { + } + + /// Construct and open a basic_stream_socket. + /** + * This constructor creates and opens a stream socket. The socket needs to be + * connected or accepted before data can be sent or received on it. + * + * @param io_service The io_service object that the stream socket will use to + * dispatch handlers for any asynchronous operations performed on the socket. + * + * @param protocol An object specifying protocol parameters to be used. + * + * @throws asio::system_error Thrown on failure. + */ + basic_stream_socket(asio::io_service& io_service, + const protocol_type& protocol) + : basic_socket(io_service, protocol) + { + } + + /// Construct a basic_stream_socket, opening it and binding it to the given + /// local endpoint. + /** + * This constructor creates a stream socket and automatically opens it bound + * to the specified endpoint on the local machine. The protocol used is the + * protocol associated with the given endpoint. + * + * @param io_service The io_service object that the stream socket will use to + * dispatch handlers for any asynchronous operations performed on the socket. + * + * @param endpoint An endpoint on the local machine to which the stream + * socket will be bound. + * + * @throws asio::system_error Thrown on failure. + */ + basic_stream_socket(asio::io_service& io_service, + const endpoint_type& endpoint) + : basic_socket(io_service, endpoint) + { + } + + /// Construct a basic_stream_socket on an existing native socket. + /** + * This constructor creates a stream socket object to hold an existing native + * socket. + * + * @param io_service The io_service object that the stream socket will use to + * dispatch handlers for any asynchronous operations performed on the socket. + * + * @param protocol An object specifying protocol parameters to be used. + * + * @param native_socket The new underlying socket implementation. + * + * @throws asio::system_error Thrown on failure. + */ + basic_stream_socket(asio::io_service& io_service, + const protocol_type& protocol, const native_type& native_socket) + : basic_socket( + io_service, protocol, native_socket) + { + } + + /// Send some data on the socket. + /** + * This function is used to send data on the stream socket. The function + * call will block until one or more bytes of the data has been sent + * successfully, or an until error occurs. + * + * @param buffers One or more data buffers to be sent on the socket. + * + * @returns The number of bytes sent. + * + * @throws asio::system_error Thrown on failure. + * + * @note The send operation may not transmit all of the data to the peer. + * Consider using the @ref write function if you need to ensure that all data + * is written before the blocking operation completes. + * + * @par Example + * To send a single data buffer use the @ref buffer function as follows: + * @code + * socket.send(asio::buffer(data, size)); + * @endcode + * See the @ref buffer documentation for information on sending multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + std::size_t send(const ConstBufferSequence& buffers) + { + asio::error_code ec; + std::size_t s = this->service.send( + this->implementation, buffers, 0, ec); + asio::detail::throw_error(ec); + return s; + } + + /// Send some data on the socket. + /** + * This function is used to send data on the stream socket. The function + * call will block until one or more bytes of the data has been sent + * successfully, or an until error occurs. + * + * @param buffers One or more data buffers to be sent on the socket. + * + * @param flags Flags specifying how the send call is to be made. + * + * @returns The number of bytes sent. + * + * @throws asio::system_error Thrown on failure. + * + * @note The send operation may not transmit all of the data to the peer. + * Consider using the @ref write function if you need to ensure that all data + * is written before the blocking operation completes. + * + * @par Example + * To send a single data buffer use the @ref buffer function as follows: + * @code + * socket.send(asio::buffer(data, size), 0); + * @endcode + * See the @ref buffer documentation for information on sending multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + std::size_t send(const ConstBufferSequence& buffers, + socket_base::message_flags flags) + { + asio::error_code ec; + std::size_t s = this->service.send( + this->implementation, buffers, flags, ec); + asio::detail::throw_error(ec); + return s; + } + + /// Send some data on the socket. + /** + * This function is used to send data on the stream socket. The function + * call will block until one or more bytes of the data has been sent + * successfully, or an until error occurs. + * + * @param buffers One or more data buffers to be sent on the socket. + * + * @param flags Flags specifying how the send call is to be made. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes sent. Returns 0 if an error occurred. + * + * @note The send operation may not transmit all of the data to the peer. + * Consider using the @ref write function if you need to ensure that all data + * is written before the blocking operation completes. + */ + template + std::size_t send(const ConstBufferSequence& buffers, + socket_base::message_flags flags, asio::error_code& ec) + { + return this->service.send(this->implementation, buffers, flags, ec); + } + + /// Start an asynchronous send. + /** + * This function is used to asynchronously send data on the stream socket. + * The function call always returns immediately. + * + * @param buffers One or more data buffers to be sent on the socket. Although + * the buffers object may be copied as necessary, ownership of the underlying + * memory blocks is retained by the caller, which must guarantee that they + * remain valid until the handler is called. + * + * @param handler The handler to be called when the send operation completes. + * Copies will be made of the handler as required. The function signature of + * the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes sent. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_service::post(). + * + * @note The send operation may not transmit all of the data to the peer. + * Consider using the @ref async_write function if you need to ensure that all + * data is written before the asynchronous operation completes. + * + * @par Example + * To send a single data buffer use the @ref buffer function as follows: + * @code + * socket.async_send(asio::buffer(data, size), handler); + * @endcode + * See the @ref buffer documentation for information on sending multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + void async_send(const ConstBufferSequence& buffers, WriteHandler handler) + { + this->service.async_send(this->implementation, buffers, 0, handler); + } + + /// Start an asynchronous send. + /** + * This function is used to asynchronously send data on the stream socket. + * The function call always returns immediately. + * + * @param buffers One or more data buffers to be sent on the socket. Although + * the buffers object may be copied as necessary, ownership of the underlying + * memory blocks is retained by the caller, which must guarantee that they + * remain valid until the handler is called. + * + * @param flags Flags specifying how the send call is to be made. + * + * @param handler The handler to be called when the send operation completes. + * Copies will be made of the handler as required. The function signature of + * the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes sent. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_service::post(). + * + * @note The send operation may not transmit all of the data to the peer. + * Consider using the @ref async_write function if you need to ensure that all + * data is written before the asynchronous operation completes. + * + * @par Example + * To send a single data buffer use the @ref buffer function as follows: + * @code + * socket.async_send(asio::buffer(data, size), 0, handler); + * @endcode + * See the @ref buffer documentation for information on sending multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + void async_send(const ConstBufferSequence& buffers, + socket_base::message_flags flags, WriteHandler handler) + { + this->service.async_send(this->implementation, buffers, flags, handler); + } + + /// Receive some data on the socket. + /** + * This function is used to receive data on the stream socket. The function + * call will block until one or more bytes of data has been received + * successfully, or until an error occurs. + * + * @param buffers One or more buffers into which the data will be received. + * + * @returns The number of bytes received. + * + * @throws asio::system_error Thrown on failure. An error code of + * asio::error::eof indicates that the connection was closed by the + * peer. + * + * @note The receive operation may not receive all of the requested number of + * bytes. Consider using the @ref read function if you need to ensure that the + * requested amount of data is read before the blocking operation completes. + * + * @par Example + * To receive into a single data buffer use the @ref buffer function as + * follows: + * @code + * socket.receive(asio::buffer(data, size)); + * @endcode + * See the @ref buffer documentation for information on receiving into + * multiple buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + std::size_t receive(const MutableBufferSequence& buffers) + { + asio::error_code ec; + std::size_t s = this->service.receive(this->implementation, buffers, 0, ec); + asio::detail::throw_error(ec); + return s; + } + + /// Receive some data on the socket. + /** + * This function is used to receive data on the stream socket. The function + * call will block until one or more bytes of data has been received + * successfully, or until an error occurs. + * + * @param buffers One or more buffers into which the data will be received. + * + * @param flags Flags specifying how the receive call is to be made. + * + * @returns The number of bytes received. + * + * @throws asio::system_error Thrown on failure. An error code of + * asio::error::eof indicates that the connection was closed by the + * peer. + * + * @note The receive operation may not receive all of the requested number of + * bytes. Consider using the @ref read function if you need to ensure that the + * requested amount of data is read before the blocking operation completes. + * + * @par Example + * To receive into a single data buffer use the @ref buffer function as + * follows: + * @code + * socket.receive(asio::buffer(data, size), 0); + * @endcode + * See the @ref buffer documentation for information on receiving into + * multiple buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + std::size_t receive(const MutableBufferSequence& buffers, + socket_base::message_flags flags) + { + asio::error_code ec; + std::size_t s = this->service.receive( + this->implementation, buffers, flags, ec); + asio::detail::throw_error(ec); + return s; + } + + /// Receive some data on a connected socket. + /** + * This function is used to receive data on the stream socket. The function + * call will block until one or more bytes of data has been received + * successfully, or until an error occurs. + * + * @param buffers One or more buffers into which the data will be received. + * + * @param flags Flags specifying how the receive call is to be made. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes received. Returns 0 if an error occurred. + * + * @note The receive operation may not receive all of the requested number of + * bytes. Consider using the @ref read function if you need to ensure that the + * requested amount of data is read before the blocking operation completes. + */ + template + std::size_t receive(const MutableBufferSequence& buffers, + socket_base::message_flags flags, asio::error_code& ec) + { + return this->service.receive(this->implementation, buffers, flags, ec); + } + + /// Start an asynchronous receive. + /** + * This function is used to asynchronously receive data from the stream + * socket. The function call always returns immediately. + * + * @param buffers One or more buffers into which the data will be received. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param handler The handler to be called when the receive operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes received. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_service::post(). + * + * @note The receive operation may not receive all of the requested number of + * bytes. Consider using the @ref async_read function if you need to ensure + * that the requested amount of data is received before the asynchronous + * operation completes. + * + * @par Example + * To receive into a single data buffer use the @ref buffer function as + * follows: + * @code + * socket.async_receive(asio::buffer(data, size), handler); + * @endcode + * See the @ref buffer documentation for information on receiving into + * multiple buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + void async_receive(const MutableBufferSequence& buffers, ReadHandler handler) + { + this->service.async_receive(this->implementation, buffers, 0, handler); + } + + /// Start an asynchronous receive. + /** + * This function is used to asynchronously receive data from the stream + * socket. The function call always returns immediately. + * + * @param buffers One or more buffers into which the data will be received. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param flags Flags specifying how the receive call is to be made. + * + * @param handler The handler to be called when the receive operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes received. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_service::post(). + * + * @note The receive operation may not receive all of the requested number of + * bytes. Consider using the @ref async_read function if you need to ensure + * that the requested amount of data is received before the asynchronous + * operation completes. + * + * @par Example + * To receive into a single data buffer use the @ref buffer function as + * follows: + * @code + * socket.async_receive(asio::buffer(data, size), 0, handler); + * @endcode + * See the @ref buffer documentation for information on receiving into + * multiple buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + void async_receive(const MutableBufferSequence& buffers, + socket_base::message_flags flags, ReadHandler handler) + { + this->service.async_receive(this->implementation, buffers, flags, handler); + } + + /// Write some data to the socket. + /** + * This function is used to write data to the stream socket. The function call + * will block until one or more bytes of the data has been written + * successfully, or until an error occurs. + * + * @param buffers One or more data buffers to be written to the socket. + * + * @returns The number of bytes written. + * + * @throws asio::system_error Thrown on failure. An error code of + * asio::error::eof indicates that the connection was closed by the + * peer. + * + * @note The write_some operation may not transmit all of the data to the + * peer. Consider using the @ref write function if you need to ensure that + * all data is written before the blocking operation completes. + * + * @par Example + * To write a single data buffer use the @ref buffer function as follows: + * @code + * socket.write_some(asio::buffer(data, size)); + * @endcode + * See the @ref buffer documentation for information on writing multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + std::size_t write_some(const ConstBufferSequence& buffers) + { + asio::error_code ec; + std::size_t s = this->service.send(this->implementation, buffers, 0, ec); + asio::detail::throw_error(ec); + return s; + } + + /// Write some data to the socket. + /** + * This function is used to write data to the stream socket. The function call + * will block until one or more bytes of the data has been written + * successfully, or until an error occurs. + * + * @param buffers One or more data buffers to be written to the socket. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes written. Returns 0 if an error occurred. + * + * @note The write_some operation may not transmit all of the data to the + * peer. Consider using the @ref write function if you need to ensure that + * all data is written before the blocking operation completes. + */ + template + std::size_t write_some(const ConstBufferSequence& buffers, + asio::error_code& ec) + { + return this->service.send(this->implementation, buffers, 0, ec); + } + + /// Start an asynchronous write. + /** + * This function is used to asynchronously write data to the stream socket. + * The function call always returns immediately. + * + * @param buffers One or more data buffers to be written to the socket. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param handler The handler to be called when the write operation completes. + * Copies will be made of the handler as required. The function signature of + * the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes written. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_service::post(). + * + * @note The write operation may not transmit all of the data to the peer. + * Consider using the @ref async_write function if you need to ensure that all + * data is written before the asynchronous operation completes. + * + * @par Example + * To write a single data buffer use the @ref buffer function as follows: + * @code + * socket.async_write_some(asio::buffer(data, size), handler); + * @endcode + * See the @ref buffer documentation for information on writing multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + void async_write_some(const ConstBufferSequence& buffers, + WriteHandler handler) + { + this->service.async_send(this->implementation, buffers, 0, handler); + } + + /// Read some data from the socket. + /** + * This function is used to read data from the stream socket. The function + * call will block until one or more bytes of data has been read successfully, + * or until an error occurs. + * + * @param buffers One or more buffers into which the data will be read. + * + * @returns The number of bytes read. + * + * @throws asio::system_error Thrown on failure. An error code of + * asio::error::eof indicates that the connection was closed by the + * peer. + * + * @note The read_some operation may not read all of the requested number of + * bytes. Consider using the @ref read function if you need to ensure that + * the requested amount of data is read before the blocking operation + * completes. + * + * @par Example + * To read into a single data buffer use the @ref buffer function as follows: + * @code + * socket.read_some(asio::buffer(data, size)); + * @endcode + * See the @ref buffer documentation for information on reading into multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + std::size_t read_some(const MutableBufferSequence& buffers) + { + asio::error_code ec; + std::size_t s = this->service.receive(this->implementation, buffers, 0, ec); + asio::detail::throw_error(ec); + return s; + } + + /// Read some data from the socket. + /** + * This function is used to read data from the stream socket. The function + * call will block until one or more bytes of data has been read successfully, + * or until an error occurs. + * + * @param buffers One or more buffers into which the data will be read. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes read. Returns 0 if an error occurred. + * + * @note The read_some operation may not read all of the requested number of + * bytes. Consider using the @ref read function if you need to ensure that + * the requested amount of data is read before the blocking operation + * completes. + */ + template + std::size_t read_some(const MutableBufferSequence& buffers, + asio::error_code& ec) + { + return this->service.receive(this->implementation, buffers, 0, ec); + } + + /// Start an asynchronous read. + /** + * This function is used to asynchronously read data from the stream socket. + * The function call always returns immediately. + * + * @param buffers One or more buffers into which the data will be read. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param handler The handler to be called when the read operation completes. + * Copies will be made of the handler as required. The function signature of + * the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes read. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_service::post(). + * + * @note The read operation may not read all of the requested number of bytes. + * Consider using the @ref async_read function if you need to ensure that the + * requested amount of data is read before the asynchronous operation + * completes. + * + * @par Example + * To read into a single data buffer use the @ref buffer function as follows: + * @code + * socket.async_read_some(asio::buffer(data, size), handler); + * @endcode + * See the @ref buffer documentation for information on reading into multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ + template + void async_read_some(const MutableBufferSequence& buffers, + ReadHandler handler) + { + this->service.async_receive(this->implementation, buffers, 0, handler); + } +}; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_BASIC_STREAM_SOCKET_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/basic_streambuf.hpp b/encryption/libtorrent/include/libtorrent/asio/basic_streambuf.hpp new file mode 100644 index 000000000..016445e0f --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/basic_streambuf.hpp @@ -0,0 +1,200 @@ +// +// basic_streambuf.hpp +// ~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_BASIC_STREAMBUF_HPP +#define ASIO_BASIC_STREAMBUF_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include +#include +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/buffer.hpp" +#include "asio/detail/noncopyable.hpp" + +namespace asio { + +/// Automatically resizable buffer class based on std::streambuf. +template > +class basic_streambuf + : public std::streambuf, + private noncopyable +{ +public: +#if defined(GENERATING_DOCUMENTATION) + /// The type used to represent the get area as a list of buffers. + typedef implementation_defined const_buffers_type; + + /// The type used to represent the put area as a list of buffers. + typedef implementation_defined mutable_buffers_type; +#else + typedef asio::const_buffers_1 const_buffers_type; + typedef asio::mutable_buffers_1 mutable_buffers_type; +#endif + + /// Construct a buffer with a specified maximum size. + explicit basic_streambuf( + std::size_t max_size = (std::numeric_limits::max)(), + const Allocator& allocator = Allocator()) + : max_size_(max_size), + buffer_(allocator) + { + std::size_t pend = (std::min)(max_size_, buffer_delta); + buffer_.resize((std::max)(pend, 1)); + setg(&buffer_[0], &buffer_[0], &buffer_[0]); + setp(&buffer_[0], &buffer_[0] + pend); + } + + /// Return the size of the get area in characters. + std::size_t size() const + { + return pptr() - gptr(); + } + + /// Return the maximum size of the buffer. + std::size_t max_size() const + { + return max_size_; + } + + /// Get a list of buffers that represents the get area. + const_buffers_type data() const + { + return asio::buffer(asio::const_buffer(gptr(), + (pptr() - gptr()) * sizeof(char_type))); + } + + /// Get a list of buffers that represents the put area, with the given size. + mutable_buffers_type prepare(std::size_t size) + { + reserve(size); + return asio::buffer(asio::mutable_buffer( + pptr(), size * sizeof(char_type))); + } + + /// Move the start of the put area by the specified number of characters. + void commit(std::size_t n) + { + if (pptr() + n > epptr()) + n = epptr() - pptr(); + pbump(static_cast(n)); + } + + /// Move the start of the get area by the specified number of characters. + void consume(std::size_t n) + { + while (n > 0) + { + sbumpc(); + --n; + } + } + +protected: + enum { buffer_delta = 128 }; + + int_type underflow() + { + if (gptr() < pptr()) + { + setg(&buffer_[0], gptr(), pptr()); + return traits_type::to_int_type(*gptr()); + } + else + { + return traits_type::eof(); + } + } + + int_type overflow(int_type c) + { + if (!traits_type::eq_int_type(c, traits_type::eof())) + { + if (pptr() == epptr()) + { + std::size_t buffer_size = pptr() - gptr(); + if (buffer_size < max_size_ && max_size_ - buffer_size < buffer_delta) + { + reserve(max_size_ - buffer_size); + } + else + { + reserve(buffer_delta); + } + } + + *pptr() = traits_type::to_char_type(c); + pbump(1); + return c; + } + + return traits_type::not_eof(c); + } + + void reserve(std::size_t n) + { + // Get current stream positions as offsets. + std::size_t gnext = gptr() - &buffer_[0]; + std::size_t gend = egptr() - &buffer_[0]; + std::size_t pnext = pptr() - &buffer_[0]; + std::size_t pend = epptr() - &buffer_[0]; + + // Check if there is already enough space in the put area. + if (n <= pend - pnext) + { + return; + } + + // Shift existing contents of get area to start of buffer. + if (gnext > 0) + { + std::rotate(&buffer_[0], &buffer_[0] + gnext, &buffer_[0] + pend); + gend -= gnext; + pnext -= gnext; + } + + // Ensure buffer is large enough to hold at least the specified size. + if (n > pend - pnext) + { + if (n <= max_size_ && pnext <= max_size_ - n) + { + buffer_.resize((std::max)(pnext + n, 1)); + } + else + { + throw std::length_error("asio::streambuf too long"); + } + } + + // Update stream positions. + setg(&buffer_[0], &buffer_[0], &buffer_[0] + gend); + setp(&buffer_[0] + pnext, &buffer_[0] + pnext + n); + } + +private: + std::size_t max_size_; + std::vector buffer_; +}; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_BASIC_STREAMBUF_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/buffer.hpp b/encryption/libtorrent/include/libtorrent/asio/buffer.hpp new file mode 100644 index 000000000..69214fdb1 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/buffer.hpp @@ -0,0 +1,782 @@ +// +// buffer.hpp +// ~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_BUFFER_HPP +#define ASIO_BUFFER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include +#include +#include +#include +#include "asio/detail/pop_options.hpp" + +#if defined(BOOST_MSVC) +# if defined(_HAS_ITERATOR_DEBUGGING) +# if !defined(ASIO_DISABLE_BUFFER_DEBUGGING) +# define ASIO_ENABLE_BUFFER_DEBUGGING +# endif // !defined(ASIO_DISABLE_BUFFER_DEBUGGING) +# endif // defined(_HAS_ITERATOR_DEBUGGING) +#endif // defined(BOOST_MSVC) + +#if defined(ASIO_ENABLE_BUFFER_DEBUGGING) +# include "asio/detail/push_options.hpp" +# include +# include "asio/detail/pop_options.hpp" +#endif // ASIO_ENABLE_BUFFER_DEBUGGING + +namespace asio { + +class mutable_buffer; +class const_buffer; + +namespace detail { +void* buffer_cast_helper(const mutable_buffer&); +const void* buffer_cast_helper(const const_buffer&); +std::size_t buffer_size_helper(const mutable_buffer&); +std::size_t buffer_size_helper(const const_buffer&); +} // namespace detail + +/// Holds a buffer that can be modified. +/** + * The mutable_buffer class provides a safe representation of a buffer that can + * be modified. It does not own the underlying data, and so is cheap to copy or + * assign. + */ +class mutable_buffer +{ +public: + /// Construct an empty buffer. + mutable_buffer() + : data_(0), + size_(0) + { + } + + /// Construct a buffer to represent a given memory range. + mutable_buffer(void* data, std::size_t size) + : data_(data), + size_(size) + { + } + +#if defined(ASIO_ENABLE_BUFFER_DEBUGGING) + mutable_buffer(void* data, std::size_t size, + boost::function debug_check) + : data_(data), + size_(size), + debug_check_(debug_check) + { + } + + const boost::function& get_debug_check() const + { + return debug_check_; + } +#endif // ASIO_ENABLE_BUFFER_DEBUGGING + +private: + friend void* asio::detail::buffer_cast_helper( + const mutable_buffer& b); + friend std::size_t asio::detail::buffer_size_helper( + const mutable_buffer& b); + + void* data_; + std::size_t size_; + +#if defined(ASIO_ENABLE_BUFFER_DEBUGGING) + boost::function debug_check_; +#endif // ASIO_ENABLE_BUFFER_DEBUGGING +}; + +namespace detail { + +inline void* buffer_cast_helper(const mutable_buffer& b) +{ +#if defined(ASIO_ENABLE_BUFFER_DEBUGGING) + if (b.debug_check_) + b.debug_check_(); +#endif // ASIO_ENABLE_BUFFER_DEBUGGING + return b.data_; +} + +inline std::size_t buffer_size_helper(const mutable_buffer& b) +{ + return b.size_; +} + +} // namespace detail + +/// Cast a non-modifiable buffer to a specified pointer to POD type. +/** + * @relates mutable_buffer + */ +template +inline PointerToPodType buffer_cast(const mutable_buffer& b) +{ + return static_cast(detail::buffer_cast_helper(b)); +} + +/// Get the number of bytes in a non-modifiable buffer. +/** + * @relates mutable_buffer + */ +inline std::size_t buffer_size(const mutable_buffer& b) +{ + return detail::buffer_size_helper(b); +} + +/// Create a new modifiable buffer that is offset from the start of another. +/** + * @relates mutable_buffer + */ +inline mutable_buffer operator+(const mutable_buffer& b, std::size_t start) +{ + if (start > buffer_size(b)) + return mutable_buffer(); + char* new_data = buffer_cast(b) + start; + std::size_t new_size = buffer_size(b) - start; + return mutable_buffer(new_data, new_size +#if defined(ASIO_ENABLE_BUFFER_DEBUGGING) + , b.get_debug_check() +#endif // ASIO_ENABLE_BUFFER_DEBUGGING + ); +} + +/// Create a new modifiable buffer that is offset from the start of another. +/** + * @relates mutable_buffer + */ +inline mutable_buffer operator+(std::size_t start, const mutable_buffer& b) +{ + if (start > buffer_size(b)) + return mutable_buffer(); + char* new_data = buffer_cast(b) + start; + std::size_t new_size = buffer_size(b) - start; + return mutable_buffer(new_data, new_size +#if defined(ASIO_ENABLE_BUFFER_DEBUGGING) + , b.get_debug_check() +#endif // ASIO_ENABLE_BUFFER_DEBUGGING + ); +} + +/// Adapts a single modifiable buffer so that it meets the requirements of the +/// MutableBufferSequence concept. +class mutable_buffers_1 + : public mutable_buffer +{ +public: + /// The type for each element in the list of buffers. + typedef mutable_buffer value_type; + + /// A random-access iterator type that may be used to read elements. + typedef const mutable_buffer* const_iterator; + + /// Construct to represent a single modifiable buffer. + explicit mutable_buffers_1(const mutable_buffer& b) + : mutable_buffer(b) + { + } + + /// Get a random-access iterator to the first element. + const_iterator begin() const + { + return this; + } + + /// Get a random-access iterator for one past the last element. + const_iterator end() const + { + return begin() + 1; + } +}; + +/// Holds a buffer that cannot be modified. +/** + * The const_buffer class provides a safe representation of a buffer that cannot + * be modified. It does not own the underlying data, and so is cheap to copy or + * assign. + */ +class const_buffer +{ +public: + /// Construct an empty buffer. + const_buffer() + : data_(0), + size_(0) + { + } + + /// Construct a buffer to represent a given memory range. + const_buffer(const void* data, std::size_t size) + : data_(data), + size_(size) + { + } + + /// Construct a non-modifiable buffer from a modifiable one. + const_buffer(const mutable_buffer& b) + : data_(asio::detail::buffer_cast_helper(b)), + size_(asio::detail::buffer_size_helper(b)) +#if defined(ASIO_ENABLE_BUFFER_DEBUGGING) + , debug_check_(b.get_debug_check()) +#endif // ASIO_ENABLE_BUFFER_DEBUGGING + { + } + +#if defined(ASIO_ENABLE_BUFFER_DEBUGGING) + const_buffer(const void* data, std::size_t size, + boost::function debug_check) + : data_(data), + size_(size), + debug_check_(debug_check) + { + } + + const boost::function& get_debug_check() const + { + return debug_check_; + } +#endif // ASIO_ENABLE_BUFFER_DEBUGGING + +private: + friend const void* asio::detail::buffer_cast_helper( + const const_buffer& b); + friend std::size_t asio::detail::buffer_size_helper( + const const_buffer& b); + + const void* data_; + std::size_t size_; + +#if defined(ASIO_ENABLE_BUFFER_DEBUGGING) + boost::function debug_check_; +#endif // ASIO_ENABLE_BUFFER_DEBUGGING +}; + +namespace detail { + +inline const void* buffer_cast_helper(const const_buffer& b) +{ +#if defined(ASIO_ENABLE_BUFFER_DEBUGGING) + if (b.debug_check_) + b.debug_check_(); +#endif // ASIO_ENABLE_BUFFER_DEBUGGING + return b.data_; +} + +inline std::size_t buffer_size_helper(const const_buffer& b) +{ + return b.size_; +} + +} // namespace detail + +/// Cast a non-modifiable buffer to a specified pointer to POD type. +/** + * @relates const_buffer + */ +template +inline PointerToPodType buffer_cast(const const_buffer& b) +{ + return static_cast(detail::buffer_cast_helper(b)); +} + +/// Get the number of bytes in a non-modifiable buffer. +/** + * @relates const_buffer + */ +inline std::size_t buffer_size(const const_buffer& b) +{ + return detail::buffer_size_helper(b); +} + +/// Create a new non-modifiable buffer that is offset from the start of another. +/** + * @relates const_buffer + */ +inline const_buffer operator+(const const_buffer& b, std::size_t start) +{ + if (start > buffer_size(b)) + return const_buffer(); + const char* new_data = buffer_cast(b) + start; + std::size_t new_size = buffer_size(b) - start; + return const_buffer(new_data, new_size +#if defined(ASIO_ENABLE_BUFFER_DEBUGGING) + , b.get_debug_check() +#endif // ASIO_ENABLE_BUFFER_DEBUGGING + ); +} + +/// Create a new non-modifiable buffer that is offset from the start of another. +/** + * @relates const_buffer + */ +inline const_buffer operator+(std::size_t start, const const_buffer& b) +{ + if (start > buffer_size(b)) + return const_buffer(); + const char* new_data = buffer_cast(b) + start; + std::size_t new_size = buffer_size(b) - start; + return const_buffer(new_data, new_size +#if defined(ASIO_ENABLE_BUFFER_DEBUGGING) + , b.get_debug_check() +#endif // ASIO_ENABLE_BUFFER_DEBUGGING + ); +} + +/// Adapts a single non-modifiable buffer so that it meets the requirements of +/// the ConstBufferSequence concept. +class const_buffers_1 + : public const_buffer +{ +public: + /// The type for each element in the list of buffers. + typedef const_buffer value_type; + + /// A random-access iterator type that may be used to read elements. + typedef const const_buffer* const_iterator; + + /// Construct to represent a single non-modifiable buffer. + explicit const_buffers_1(const const_buffer& b) + : const_buffer(b) + { + } + + /// Get a random-access iterator to the first element. + const_iterator begin() const + { + return this; + } + + /// Get a random-access iterator for one past the last element. + const_iterator end() const + { + return begin() + 1; + } +}; + +#if defined(ASIO_ENABLE_BUFFER_DEBUGGING) +namespace detail { + +template +class buffer_debug_check +{ +public: + buffer_debug_check(Iterator iter) + : iter_(iter) + { + } + + void operator()() + { + *iter_; + } + +private: + Iterator iter_; +}; + +} // namespace detail +#endif // ASIO_ENABLE_BUFFER_DEBUGGING + +/** @defgroup buffer asio::buffer + * + * @brief The asio::buffer function is used to create a buffer object to + * represent raw memory, an array of POD elements, or a vector of POD elements. + * + * The simplest use case involves reading or writing a single buffer of a + * specified size: + * + * @code sock.write(asio::buffer(data, size)); @endcode + * + * In the above example, the return value of asio::buffer meets the + * requirements of the ConstBufferSequence concept so that it may be directly + * passed to the socket's write function. A buffer created for modifiable + * memory also meets the requirements of the MutableBufferSequence concept. + * + * An individual buffer may be created from a builtin array, std::vector or + * boost::array of POD elements. This helps prevent buffer overruns by + * automatically determining the size of the buffer: + * + * @code char d1[128]; + * size_t bytes_transferred = sock.read(asio::buffer(d1)); + * + * std::vector d2(128); + * bytes_transferred = sock.read(asio::buffer(d2)); + * + * boost::array d3; + * bytes_transferred = sock.read(asio::buffer(d3)); @endcode + * + * To read or write using multiple buffers (i.e. scatter-gather I/O), multiple + * buffer objects may be assigned into a container that supports the + * MutableBufferSequence (for read) or ConstBufferSequence (for write) concepts: + * + * @code + * char d1[128]; + * std::vector d2(128); + * boost::array d3; + * + * boost::array bufs1 = { + * asio::buffer(d1), + * asio::buffer(d2), + * asio::buffer(d3) }; + * bytes_transferred = sock.read(bufs1); + * + * std::vector bufs2; + * bufs2.push_back(asio::buffer(d1)); + * bufs2.push_back(asio::buffer(d2)); + * bufs2.push_back(asio::buffer(d3)); + * bytes_transferred = sock.write(bufs2); @endcode + */ +/*@{*/ + +/// Create a new modifiable buffer from an existing buffer. +inline mutable_buffers_1 buffer(const mutable_buffer& b) +{ + return mutable_buffers_1(b); +} + +/// Create a new modifiable buffer from an existing buffer. +inline mutable_buffers_1 buffer(const mutable_buffer& b, + std::size_t max_size_in_bytes) +{ + return mutable_buffers_1( + mutable_buffer(buffer_cast(b), + buffer_size(b) < max_size_in_bytes + ? buffer_size(b) : max_size_in_bytes +#if defined(ASIO_ENABLE_BUFFER_DEBUGGING) + , b.get_debug_check() +#endif // ASIO_ENABLE_BUFFER_DEBUGGING + )); +} + +/// Create a new non-modifiable buffer from an existing buffer. +inline const_buffers_1 buffer(const const_buffer& b) +{ + return const_buffers_1(b); +} + +/// Create a new non-modifiable buffer from an existing buffer. +inline const_buffers_1 buffer(const const_buffer& b, + std::size_t max_size_in_bytes) +{ + return const_buffers_1( + const_buffer(buffer_cast(b), + buffer_size(b) < max_size_in_bytes + ? buffer_size(b) : max_size_in_bytes +#if defined(ASIO_ENABLE_BUFFER_DEBUGGING) + , b.get_debug_check() +#endif // ASIO_ENABLE_BUFFER_DEBUGGING + )); +} + +/// Create a new modifiable buffer that represents the given memory range. +inline mutable_buffers_1 buffer(void* data, std::size_t size_in_bytes) +{ + return mutable_buffers_1(mutable_buffer(data, size_in_bytes)); +} + +/// Create a new non-modifiable buffer that represents the given memory range. +inline const_buffers_1 buffer(const void* data, + std::size_t size_in_bytes) +{ + return const_buffers_1(const_buffer(data, size_in_bytes)); +} + +/// Create a new modifiable buffer that represents the given POD array. +template +inline mutable_buffers_1 buffer(PodType (&data)[N]) +{ + return mutable_buffers_1(mutable_buffer(data, N * sizeof(PodType))); +} + +/// Create a new modifiable buffer that represents the given POD array. +template +inline mutable_buffers_1 buffer(PodType (&data)[N], + std::size_t max_size_in_bytes) +{ + return mutable_buffers_1( + mutable_buffer(data, + N * sizeof(PodType) < max_size_in_bytes + ? N * sizeof(PodType) : max_size_in_bytes)); +} + +/// Create a new non-modifiable buffer that represents the given POD array. +template +inline const_buffers_1 buffer(const PodType (&data)[N]) +{ + return const_buffers_1(const_buffer(data, N * sizeof(PodType))); +} + +/// Create a new non-modifiable buffer that represents the given POD array. +template +inline const_buffers_1 buffer(const PodType (&data)[N], + std::size_t max_size_in_bytes) +{ + return const_buffers_1( + const_buffer(data, + N * sizeof(PodType) < max_size_in_bytes + ? N * sizeof(PodType) : max_size_in_bytes)); +} + +#if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x582)) + +// Borland C++ thinks the overloads: +// +// unspecified buffer(boost::array& array ...); +// +// and +// +// unspecified buffer(boost::array& array ...); +// +// are ambiguous. This will be worked around by using a buffer_types traits +// class that contains typedefs for the appropriate buffer and container +// classes, based on whether PodType is const or non-const. + +namespace detail { + +template +struct buffer_types_base; + +template <> +struct buffer_types_base +{ + typedef mutable_buffer buffer_type; + typedef mutable_buffers_1 container_type; +}; + +template <> +struct buffer_types_base +{ + typedef const_buffer buffer_type; + typedef const_buffers_1 container_type; +}; + +template +struct buffer_types + : public buffer_types_base::value> +{ +}; + +} // namespace detail + +template +inline typename detail::buffer_types::container_type +buffer(boost::array& data) +{ + typedef typename asio::detail::buffer_types::buffer_type + buffer_type; + typedef typename asio::detail::buffer_types::container_type + container_type; + return container_type( + buffer_type(data.c_array(), data.size() * sizeof(PodType))); +} + +template +inline typename detail::buffer_types::container_type +buffer(boost::array& data, std::size_t max_size_in_bytes) +{ + typedef typename asio::detail::buffer_types::buffer_type + buffer_type; + typedef typename asio::detail::buffer_types::container_type + container_type; + return container_type( + buffer_type(data.c_array(), + data.size() * sizeof(PodType) < max_size_in_bytes + ? data.size() * sizeof(PodType) : max_size_in_bytes)); +} + +#else // BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x582)) + +/// Create a new modifiable buffer that represents the given POD array. +template +inline mutable_buffers_1 buffer(boost::array& data) +{ + return mutable_buffers_1( + mutable_buffer(data.c_array(), data.size() * sizeof(PodType))); +} + +/// Create a new modifiable buffer that represents the given POD array. +template +inline mutable_buffers_1 buffer(boost::array& data, + std::size_t max_size_in_bytes) +{ + return mutable_buffers_1( + mutable_buffer(data.c_array(), + data.size() * sizeof(PodType) < max_size_in_bytes + ? data.size() * sizeof(PodType) : max_size_in_bytes)); +} + +/// Create a new non-modifiable buffer that represents the given POD array. +template +inline const_buffers_1 buffer(boost::array& data) +{ + return const_buffers_1( + const_buffer(data.data(), data.size() * sizeof(PodType))); +} + +/// Create a new non-modifiable buffer that represents the given POD array. +template +inline const_buffers_1 buffer(boost::array& data, + std::size_t max_size_in_bytes) +{ + return const_buffers_1( + const_buffer(data.data(), + data.size() * sizeof(PodType) < max_size_in_bytes + ? data.size() * sizeof(PodType) : max_size_in_bytes)); +} + +#endif // BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x582)) + +/// Create a new non-modifiable buffer that represents the given POD array. +template +inline const_buffers_1 buffer(const boost::array& data) +{ + return const_buffers_1( + const_buffer(data.data(), data.size() * sizeof(PodType))); +} + +/// Create a new non-modifiable buffer that represents the given POD array. +template +inline const_buffers_1 buffer(const boost::array& data, + std::size_t max_size_in_bytes) +{ + return const_buffers_1( + const_buffer(data.data(), + data.size() * sizeof(PodType) < max_size_in_bytes + ? data.size() * sizeof(PodType) : max_size_in_bytes)); +} + +/// Create a new modifiable buffer that represents the given POD vector. +/** + * @note The buffer is invalidated by any vector operation that would also + * invalidate iterators. + */ +template +inline mutable_buffers_1 buffer(std::vector& data) +{ + return mutable_buffers_1( + mutable_buffer(&data[0], data.size() * sizeof(PodType) +#if defined(ASIO_ENABLE_BUFFER_DEBUGGING) + , detail::buffer_debug_check< + typename std::vector::iterator + >(data.begin()) +#endif // ASIO_ENABLE_BUFFER_DEBUGGING + )); +} + +/// Create a new modifiable buffer that represents the given POD vector. +/** + * @note The buffer is invalidated by any vector operation that would also + * invalidate iterators. + */ +template +inline mutable_buffers_1 buffer(std::vector& data, + std::size_t max_size_in_bytes) +{ + return mutable_buffers_1( + mutable_buffer(&data[0], + data.size() * sizeof(PodType) < max_size_in_bytes + ? data.size() * sizeof(PodType) : max_size_in_bytes +#if defined(ASIO_ENABLE_BUFFER_DEBUGGING) + , detail::buffer_debug_check< + typename std::vector::iterator + >(data.begin()) +#endif // ASIO_ENABLE_BUFFER_DEBUGGING + )); +} + +/// Create a new non-modifiable buffer that represents the given POD vector. +/** + * @note The buffer is invalidated by any vector operation that would also + * invalidate iterators. + */ +template +inline const_buffers_1 buffer( + const std::vector& data) +{ + return const_buffers_1( + const_buffer(&data[0], data.size() * sizeof(PodType) +#if defined(ASIO_ENABLE_BUFFER_DEBUGGING) + , detail::buffer_debug_check< + typename std::vector::const_iterator + >(data.begin()) +#endif // ASIO_ENABLE_BUFFER_DEBUGGING + )); +} + +/// Create a new non-modifiable buffer that represents the given POD vector. +/** + * @note The buffer is invalidated by any vector operation that would also + * invalidate iterators. + */ +template +inline const_buffers_1 buffer( + const std::vector& data, std::size_t max_size_in_bytes) +{ + return const_buffers_1( + const_buffer(&data[0], + data.size() * sizeof(PodType) < max_size_in_bytes + ? data.size() * sizeof(PodType) : max_size_in_bytes +#if defined(ASIO_ENABLE_BUFFER_DEBUGGING) + , detail::buffer_debug_check< + typename std::vector::const_iterator + >(data.begin()) +#endif // ASIO_ENABLE_BUFFER_DEBUGGING + )); +} + +/// Create a new non-modifiable buffer that represents the given string. +/** + * @note The buffer is invalidated by any non-const operation called on the + * given string object. + */ +inline const_buffers_1 buffer(const std::string& data) +{ + return const_buffers_1(const_buffer(data.data(), data.size() +#if defined(ASIO_ENABLE_BUFFER_DEBUGGING) + , detail::buffer_debug_check(data.begin()) +#endif // ASIO_ENABLE_BUFFER_DEBUGGING + )); +} + +/// Create a new non-modifiable buffer that represents the given string. +/** + * @note The buffer is invalidated by any non-const operation called on the + * given string object. + */ +inline const_buffers_1 buffer(const std::string& data, + std::size_t max_size_in_bytes) +{ + return const_buffers_1( + const_buffer(data.data(), + data.size() < max_size_in_bytes + ? data.size() : max_size_in_bytes +#if defined(ASIO_ENABLE_BUFFER_DEBUGGING) + , detail::buffer_debug_check(data.begin()) +#endif // ASIO_ENABLE_BUFFER_DEBUGGING + )); +} + +/*@}*/ + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_BUFFER_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/buffered_read_stream.hpp b/encryption/libtorrent/include/libtorrent/asio/buffered_read_stream.hpp new file mode 100644 index 000000000..5cf5d688e --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/buffered_read_stream.hpp @@ -0,0 +1,407 @@ +// +// buffered_read_stream.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_BUFFERED_READ_STREAM_HPP +#define ASIO_BUFFERED_READ_STREAM_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/buffered_read_stream_fwd.hpp" +#include "asio/buffer.hpp" +#include "asio/error.hpp" +#include "asio/io_service.hpp" +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/buffer_resize_guard.hpp" +#include "asio/detail/buffered_stream_storage.hpp" +#include "asio/detail/noncopyable.hpp" + +namespace asio { + +/// Adds buffering to the read-related operations of a stream. +/** + * The buffered_read_stream class template can be used to add buffering to the + * synchronous and asynchronous read operations of a stream. + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + * + * @par Concepts: + * AsyncReadStream, AsyncWriteStream, Stream, Sync_Read_Stream, SyncWriteStream. + */ +template +class buffered_read_stream + : private noncopyable +{ +public: + /// The type of the next layer. + typedef typename boost::remove_reference::type next_layer_type; + + /// The type of the lowest layer. + typedef typename next_layer_type::lowest_layer_type lowest_layer_type; + +#if defined(GENERATING_DOCUMENTATION) + /// The default buffer size. + static const std::size_t default_buffer_size = implementation_defined; +#else + BOOST_STATIC_CONSTANT(std::size_t, default_buffer_size = 1024); +#endif + + /// Construct, passing the specified argument to initialise the next layer. + template + explicit buffered_read_stream(Arg& a) + : next_layer_(a), + storage_(default_buffer_size) + { + } + + /// Construct, passing the specified argument to initialise the next layer. + template + buffered_read_stream(Arg& a, std::size_t buffer_size) + : next_layer_(a), + storage_(buffer_size) + { + } + + /// Get a reference to the next layer. + next_layer_type& next_layer() + { + return next_layer_; + } + + /// Get a reference to the lowest layer. + lowest_layer_type& lowest_layer() + { + return next_layer_.lowest_layer(); + } + + /// Get the io_service associated with the object. + asio::io_service& io_service() + { + return next_layer_.io_service(); + } + + /// Close the stream. + void close() + { + next_layer_.close(); + } + + /// Close the stream. + asio::error_code close(asio::error_code& ec) + { + return next_layer_.close(ec); + } + + /// Write the given data to the stream. Returns the number of bytes written. + /// Throws an exception on failure. + template + std::size_t write_some(const ConstBufferSequence& buffers) + { + return next_layer_.write_some(buffers); + } + + /// Write the given data to the stream. Returns the number of bytes written, + /// or 0 if an error occurred. + template + std::size_t write_some(const ConstBufferSequence& buffers, + asio::error_code& ec) + { + return next_layer_.write_some(buffers, ec); + } + + /// Start an asynchronous write. The data being written must be valid for the + /// lifetime of the asynchronous operation. + template + void async_write_some(const ConstBufferSequence& buffers, + WriteHandler handler) + { + next_layer_.async_write_some(buffers, handler); + } + + /// Fill the buffer with some data. Returns the number of bytes placed in the + /// buffer as a result of the operation. Throws an exception on failure. + std::size_t fill() + { + detail::buffer_resize_guard + resize_guard(storage_); + std::size_t previous_size = storage_.size(); + storage_.resize(storage_.capacity()); + storage_.resize(previous_size + next_layer_.read_some(buffer( + storage_.data() + previous_size, + storage_.size() - previous_size))); + resize_guard.commit(); + return storage_.size() - previous_size; + } + + /// Fill the buffer with some data. Returns the number of bytes placed in the + /// buffer as a result of the operation, or 0 if an error occurred. + std::size_t fill(asio::error_code& ec) + { + detail::buffer_resize_guard + resize_guard(storage_); + std::size_t previous_size = storage_.size(); + storage_.resize(storage_.capacity()); + storage_.resize(previous_size + next_layer_.read_some(buffer( + storage_.data() + previous_size, + storage_.size() - previous_size), + ec)); + resize_guard.commit(); + return storage_.size() - previous_size; + } + + template + class fill_handler + { + public: + fill_handler(asio::io_service& io_service, + detail::buffered_stream_storage& storage, + std::size_t previous_size, ReadHandler handler) + : io_service_(io_service), + storage_(storage), + previous_size_(previous_size), + handler_(handler) + { + } + + void operator()(const asio::error_code& ec, + std::size_t bytes_transferred) + { + storage_.resize(previous_size_ + bytes_transferred); + io_service_.dispatch(detail::bind_handler( + handler_, ec, bytes_transferred)); + } + + private: + asio::io_service& io_service_; + detail::buffered_stream_storage& storage_; + std::size_t previous_size_; + ReadHandler handler_; + }; + + /// Start an asynchronous fill. + template + void async_fill(ReadHandler handler) + { + std::size_t previous_size = storage_.size(); + storage_.resize(storage_.capacity()); + next_layer_.async_read_some( + buffer( + storage_.data() + previous_size, + storage_.size() - previous_size), + fill_handler(io_service(), + storage_, previous_size, handler)); + } + + /// Read some data from the stream. Returns the number of bytes read. Throws + /// an exception on failure. + template + std::size_t read_some(const MutableBufferSequence& buffers) + { + if (storage_.empty()) + fill(); + return copy(buffers); + } + + /// Read some data from the stream. Returns the number of bytes read or 0 if + /// an error occurred. + template + std::size_t read_some(const MutableBufferSequence& buffers, + asio::error_code& ec) + { + ec = asio::error_code(); + if (storage_.empty() && !fill(ec)) + return 0; + return copy(buffers); + } + + template + class read_some_handler + { + public: + read_some_handler(asio::io_service& io_service, + detail::buffered_stream_storage& storage, + const MutableBufferSequence& buffers, ReadHandler handler) + : io_service_(io_service), + storage_(storage), + buffers_(buffers), + handler_(handler) + { + } + + void operator()(const asio::error_code& ec, std::size_t) + { + if (ec || storage_.empty()) + { + std::size_t length = 0; + io_service_.dispatch(detail::bind_handler(handler_, ec, length)); + } + else + { + using namespace std; // For memcpy. + + std::size_t bytes_avail = storage_.size(); + std::size_t bytes_copied = 0; + + typename MutableBufferSequence::const_iterator iter = buffers_.begin(); + typename MutableBufferSequence::const_iterator end = buffers_.end(); + for (; iter != end && bytes_avail > 0; ++iter) + { + std::size_t max_length = buffer_size(*iter); + std::size_t length = (max_length < bytes_avail) + ? max_length : bytes_avail; + memcpy(buffer_cast(*iter), + storage_.data() + bytes_copied, length); + bytes_copied += length; + bytes_avail -= length; + } + + storage_.consume(bytes_copied); + io_service_.dispatch(detail::bind_handler(handler_, ec, bytes_copied)); + } + } + + private: + asio::io_service& io_service_; + detail::buffered_stream_storage& storage_; + MutableBufferSequence buffers_; + ReadHandler handler_; + }; + + /// Start an asynchronous read. The buffer into which the data will be read + /// must be valid for the lifetime of the asynchronous operation. + template + void async_read_some(const MutableBufferSequence& buffers, + ReadHandler handler) + { + if (storage_.empty()) + { + async_fill(read_some_handler( + io_service(), storage_, buffers, handler)); + } + else + { + std::size_t length = copy(buffers); + io_service().post(detail::bind_handler( + handler, asio::error_code(), length)); + } + } + + /// Peek at the incoming data on the stream. Returns the number of bytes read. + /// Throws an exception on failure. + template + std::size_t peek(const MutableBufferSequence& buffers) + { + if (storage_.empty()) + fill(); + return peek_copy(buffers); + } + + /// Peek at the incoming data on the stream. Returns the number of bytes read, + /// or 0 if an error occurred. + template + std::size_t peek(const MutableBufferSequence& buffers, + asio::error_code& ec) + { + ec = asio::error_code(); + if (storage_.empty() && !fill(ec)) + return 0; + return peek_copy(buffers); + } + + /// Determine the amount of data that may be read without blocking. + std::size_t in_avail() + { + return storage_.size(); + } + + /// Determine the amount of data that may be read without blocking. + std::size_t in_avail(asio::error_code& ec) + { + ec = asio::error_code(); + return storage_.size(); + } + +private: + /// Copy data out of the internal buffer to the specified target buffer. + /// Returns the number of bytes copied. + template + std::size_t copy(const MutableBufferSequence& buffers) + { + using namespace std; // For memcpy. + + std::size_t bytes_avail = storage_.size(); + std::size_t bytes_copied = 0; + + typename MutableBufferSequence::const_iterator iter = buffers.begin(); + typename MutableBufferSequence::const_iterator end = buffers.end(); + for (; iter != end && bytes_avail > 0; ++iter) + { + std::size_t max_length = buffer_size(*iter); + std::size_t length = (max_length < bytes_avail) + ? max_length : bytes_avail; + memcpy(buffer_cast(*iter), storage_.data() + bytes_copied, length); + bytes_copied += length; + bytes_avail -= length; + } + + storage_.consume(bytes_copied); + return bytes_copied; + } + + /// Copy data from the internal buffer to the specified target buffer, without + /// removing the data from the internal buffer. Returns the number of bytes + /// copied. + template + std::size_t peek_copy(const MutableBufferSequence& buffers) + { + using namespace std; // For memcpy. + + std::size_t bytes_avail = storage_.size(); + std::size_t bytes_copied = 0; + + typename MutableBufferSequence::const_iterator iter = buffers.begin(); + typename MutableBufferSequence::const_iterator end = buffers.end(); + for (; iter != end && bytes_avail > 0; ++iter) + { + std::size_t max_length = buffer_size(*iter); + std::size_t length = (max_length < bytes_avail) + ? max_length : bytes_avail; + memcpy(buffer_cast(*iter), storage_.data() + bytes_copied, length); + bytes_copied += length; + bytes_avail -= length; + } + + return bytes_copied; + } + + /// The next layer. + Stream next_layer_; + + // The data in the buffer. + detail::buffered_stream_storage storage_; +}; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_BUFFERED_READ_STREAM_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/buffered_read_stream_fwd.hpp b/encryption/libtorrent/include/libtorrent/asio/buffered_read_stream_fwd.hpp new file mode 100644 index 000000000..df4270366 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/buffered_read_stream_fwd.hpp @@ -0,0 +1,29 @@ +// +// buffered_read_stream_fwd.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_BUFFERED_READ_STREAM_FWD_HPP +#define ASIO_BUFFERED_READ_STREAM_FWD_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +namespace asio { + +template +class buffered_read_stream; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_BUFFERED_READ_STREAM_FWD_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/buffered_stream.hpp b/encryption/libtorrent/include/libtorrent/asio/buffered_stream.hpp new file mode 100644 index 000000000..b6901a6d4 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/buffered_stream.hpp @@ -0,0 +1,243 @@ +// +// buffered_stream.hpp +// ~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_BUFFERED_STREAM_HPP +#define ASIO_BUFFERED_STREAM_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/buffered_read_stream.hpp" +#include "asio/buffered_write_stream.hpp" +#include "asio/buffered_stream_fwd.hpp" +#include "asio/error.hpp" +#include "asio/io_service.hpp" +#include "asio/detail/noncopyable.hpp" + +namespace asio { + +/// Adds buffering to the read- and write-related operations of a stream. +/** + * The buffered_stream class template can be used to add buffering to the + * synchronous and asynchronous read and write operations of a stream. + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + * + * @par Concepts: + * AsyncReadStream, AsyncWriteStream, Stream, SyncReadStream, SyncWriteStream. + */ +template +class buffered_stream + : private noncopyable +{ +public: + /// The type of the next layer. + typedef typename boost::remove_reference::type next_layer_type; + + /// The type of the lowest layer. + typedef typename next_layer_type::lowest_layer_type lowest_layer_type; + + /// Construct, passing the specified argument to initialise the next layer. + template + explicit buffered_stream(Arg& a) + : inner_stream_impl_(a), + stream_impl_(inner_stream_impl_) + { + } + + /// Construct, passing the specified argument to initialise the next layer. + template + explicit buffered_stream(Arg& a, std::size_t read_buffer_size, + std::size_t write_buffer_size) + : inner_stream_impl_(a, write_buffer_size), + stream_impl_(inner_stream_impl_, read_buffer_size) + { + } + + /// Get a reference to the next layer. + next_layer_type& next_layer() + { + return stream_impl_.next_layer().next_layer(); + } + + /// Get a reference to the lowest layer. + lowest_layer_type& lowest_layer() + { + return stream_impl_.lowest_layer(); + } + + /// Get the io_service associated with the object. + asio::io_service& io_service() + { + return stream_impl_.io_service(); + } + + /// Close the stream. + void close() + { + stream_impl_.close(); + } + + /// Close the stream. + asio::error_code close(asio::error_code& ec) + { + return stream_impl_.close(ec); + } + + /// Flush all data from the buffer to the next layer. Returns the number of + /// bytes written to the next layer on the last write operation. Throws an + /// exception on failure. + std::size_t flush() + { + return stream_impl_.next_layer().flush(); + } + + /// Flush all data from the buffer to the next layer. Returns the number of + /// bytes written to the next layer on the last write operation, or 0 if an + /// error occurred. + std::size_t flush(asio::error_code& ec) + { + return stream_impl_.next_layer().flush(ec); + } + + /// Start an asynchronous flush. + template + void async_flush(WriteHandler handler) + { + return stream_impl_.next_layer().async_flush(handler); + } + + /// Write the given data to the stream. Returns the number of bytes written. + /// Throws an exception on failure. + template + std::size_t write_some(const ConstBufferSequence& buffers) + { + return stream_impl_.write_some(buffers); + } + + /// Write the given data to the stream. Returns the number of bytes written, + /// or 0 if an error occurred. + template + std::size_t write_some(const ConstBufferSequence& buffers, + asio::error_code& ec) + { + return stream_impl_.write_some(buffers, ec); + } + + /// Start an asynchronous write. The data being written must be valid for the + /// lifetime of the asynchronous operation. + template + void async_write_some(const ConstBufferSequence& buffers, + WriteHandler handler) + { + stream_impl_.async_write_some(buffers, handler); + } + + /// Fill the buffer with some data. Returns the number of bytes placed in the + /// buffer as a result of the operation. Throws an exception on failure. + std::size_t fill() + { + return stream_impl_.fill(); + } + + /// Fill the buffer with some data. Returns the number of bytes placed in the + /// buffer as a result of the operation, or 0 if an error occurred. + std::size_t fill(asio::error_code& ec) + { + return stream_impl_.fill(ec); + } + + /// Start an asynchronous fill. + template + void async_fill(ReadHandler handler) + { + stream_impl_.async_fill(handler); + } + + /// Read some data from the stream. Returns the number of bytes read. Throws + /// an exception on failure. + template + std::size_t read_some(const MutableBufferSequence& buffers) + { + return stream_impl_.read_some(buffers); + } + + /// Read some data from the stream. Returns the number of bytes read or 0 if + /// an error occurred. + template + std::size_t read_some(const MutableBufferSequence& buffers, + asio::error_code& ec) + { + return stream_impl_.read_some(buffers, ec); + } + + /// Start an asynchronous read. The buffer into which the data will be read + /// must be valid for the lifetime of the asynchronous operation. + template + void async_read_some(const MutableBufferSequence& buffers, + ReadHandler handler) + { + stream_impl_.async_read_some(buffers, handler); + } + + /// Peek at the incoming data on the stream. Returns the number of bytes read. + /// Throws an exception on failure. + template + std::size_t peek(const MutableBufferSequence& buffers) + { + return stream_impl_.peek(buffers); + } + + /// Peek at the incoming data on the stream. Returns the number of bytes read, + /// or 0 if an error occurred. + template + std::size_t peek(const MutableBufferSequence& buffers, + asio::error_code& ec) + { + return stream_impl_.peek(buffers, ec); + } + + /// Determine the amount of data that may be read without blocking. + std::size_t in_avail() + { + return stream_impl_.in_avail(); + } + + /// Determine the amount of data that may be read without blocking. + std::size_t in_avail(asio::error_code& ec) + { + return stream_impl_.in_avail(ec); + } + +private: + // The buffered write stream. + typedef buffered_write_stream write_stream_type; + write_stream_type inner_stream_impl_; + + // The buffered read stream. + typedef buffered_read_stream read_stream_type; + read_stream_type stream_impl_; +}; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_BUFFERED_STREAM_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/buffered_stream_fwd.hpp b/encryption/libtorrent/include/libtorrent/asio/buffered_stream_fwd.hpp new file mode 100644 index 000000000..10d1c384e --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/buffered_stream_fwd.hpp @@ -0,0 +1,29 @@ +// +// buffered_stream_fwd.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_BUFFERED_STREAM_FWD_HPP +#define ASIO_BUFFERED_STREAM_FWD_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +namespace asio { + +template +class buffered_stream; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_BUFFERED_STREAM_FWD_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/buffered_write_stream.hpp b/encryption/libtorrent/include/libtorrent/asio/buffered_write_stream.hpp new file mode 100644 index 000000000..ffe3a2ee7 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/buffered_write_stream.hpp @@ -0,0 +1,361 @@ +// +// buffered_write_stream.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_BUFFERED_WRITE_STREAM_HPP +#define ASIO_BUFFERED_WRITE_STREAM_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/buffered_write_stream_fwd.hpp" +#include "asio/buffer.hpp" +#include "asio/completion_condition.hpp" +#include "asio/error.hpp" +#include "asio/io_service.hpp" +#include "asio/write.hpp" +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/buffered_stream_storage.hpp" +#include "asio/detail/noncopyable.hpp" + +namespace asio { + +/// Adds buffering to the write-related operations of a stream. +/** + * The buffered_write_stream class template can be used to add buffering to the + * synchronous and asynchronous write operations of a stream. + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + * + * @par Concepts: + * AsyncReadStream, AsyncWriteStream, Stream, SyncReadStream, SyncWriteStream. + */ +template +class buffered_write_stream + : private noncopyable +{ +public: + /// The type of the next layer. + typedef typename boost::remove_reference::type next_layer_type; + + /// The type of the lowest layer. + typedef typename next_layer_type::lowest_layer_type lowest_layer_type; + +#if defined(GENERATING_DOCUMENTATION) + /// The default buffer size. + static const std::size_t default_buffer_size = implementation_defined; +#else + BOOST_STATIC_CONSTANT(std::size_t, default_buffer_size = 1024); +#endif + + /// Construct, passing the specified argument to initialise the next layer. + template + explicit buffered_write_stream(Arg& a) + : next_layer_(a), + storage_(default_buffer_size) + { + } + + /// Construct, passing the specified argument to initialise the next layer. + template + buffered_write_stream(Arg& a, std::size_t buffer_size) + : next_layer_(a), + storage_(buffer_size) + { + } + + /// Get a reference to the next layer. + next_layer_type& next_layer() + { + return next_layer_; + } + + /// Get a reference to the lowest layer. + lowest_layer_type& lowest_layer() + { + return next_layer_.lowest_layer(); + } + + /// Get the io_service associated with the object. + asio::io_service& io_service() + { + return next_layer_.io_service(); + } + + /// Close the stream. + void close() + { + next_layer_.close(); + } + + /// Close the stream. + asio::error_code close(asio::error_code& ec) + { + return next_layer_.close(ec); + } + + /// Flush all data from the buffer to the next layer. Returns the number of + /// bytes written to the next layer on the last write operation. Throws an + /// exception on failure. + std::size_t flush() + { + std::size_t bytes_written = write(next_layer_, + buffer(storage_.data(), storage_.size())); + storage_.consume(bytes_written); + return bytes_written; + } + + /// Flush all data from the buffer to the next layer. Returns the number of + /// bytes written to the next layer on the last write operation, or 0 if an + /// error occurred. + std::size_t flush(asio::error_code& ec) + { + std::size_t bytes_written = write(next_layer_, + buffer(storage_.data(), storage_.size()), + transfer_all(), ec); + storage_.consume(bytes_written); + return bytes_written; + } + + template + class flush_handler + { + public: + flush_handler(asio::io_service& io_service, + detail::buffered_stream_storage& storage, WriteHandler handler) + : io_service_(io_service), + storage_(storage), + handler_(handler) + { + } + + void operator()(const asio::error_code& ec, + std::size_t bytes_written) + { + storage_.consume(bytes_written); + io_service_.dispatch(detail::bind_handler(handler_, ec, bytes_written)); + } + + private: + asio::io_service& io_service_; + detail::buffered_stream_storage& storage_; + WriteHandler handler_; + }; + + /// Start an asynchronous flush. + template + void async_flush(WriteHandler handler) + { + async_write(next_layer_, buffer(storage_.data(), storage_.size()), + flush_handler(io_service(), storage_, handler)); + } + + /// Write the given data to the stream. Returns the number of bytes written. + /// Throws an exception on failure. + template + std::size_t write_some(const ConstBufferSequence& buffers) + { + if (storage_.size() == storage_.capacity()) + flush(); + return copy(buffers); + } + + /// Write the given data to the stream. Returns the number of bytes written, + /// or 0 if an error occurred and the error handler did not throw. + template + std::size_t write_some(const ConstBufferSequence& buffers, + asio::error_code& ec) + { + ec = asio::error_code(); + if (storage_.size() == storage_.capacity() && !flush(ec)) + return 0; + return copy(buffers); + } + + template + class write_some_handler + { + public: + write_some_handler(asio::io_service& io_service, + detail::buffered_stream_storage& storage, + const ConstBufferSequence& buffers, WriteHandler handler) + : io_service_(io_service), + storage_(storage), + buffers_(buffers), + handler_(handler) + { + } + + void operator()(const asio::error_code& ec, std::size_t) + { + if (ec) + { + std::size_t length = 0; + io_service_.dispatch(detail::bind_handler(handler_, ec, length)); + } + else + { + using namespace std; // For memcpy. + + std::size_t orig_size = storage_.size(); + std::size_t space_avail = storage_.capacity() - orig_size; + std::size_t bytes_copied = 0; + + typename ConstBufferSequence::const_iterator iter = buffers_.begin(); + typename ConstBufferSequence::const_iterator end = buffers_.end(); + for (; iter != end && space_avail > 0; ++iter) + { + std::size_t bytes_avail = buffer_size(*iter); + std::size_t length = (bytes_avail < space_avail) + ? bytes_avail : space_avail; + storage_.resize(orig_size + bytes_copied + length); + memcpy(storage_.data() + orig_size + bytes_copied, + buffer_cast(*iter), length); + bytes_copied += length; + space_avail -= length; + } + + io_service_.dispatch(detail::bind_handler(handler_, ec, bytes_copied)); + } + } + + private: + asio::io_service& io_service_; + detail::buffered_stream_storage& storage_; + ConstBufferSequence buffers_; + WriteHandler handler_; + }; + + /// Start an asynchronous write. The data being written must be valid for the + /// lifetime of the asynchronous operation. + template + void async_write_some(const ConstBufferSequence& buffers, + WriteHandler handler) + { + if (storage_.size() == storage_.capacity()) + { + async_flush(write_some_handler( + io_service(), storage_, buffers, handler)); + } + else + { + std::size_t bytes_copied = copy(buffers); + io_service().post(detail::bind_handler( + handler, asio::error_code(), bytes_copied)); + } + } + + /// Read some data from the stream. Returns the number of bytes read. Throws + /// an exception on failure. + template + std::size_t read_some(const MutableBufferSequence& buffers) + { + return next_layer_.read_some(buffers); + } + + /// Read some data from the stream. Returns the number of bytes read or 0 if + /// an error occurred. + template + std::size_t read_some(const MutableBufferSequence& buffers, + asio::error_code& ec) + { + return next_layer_.read_some(buffers, ec); + } + + /// Start an asynchronous read. The buffer into which the data will be read + /// must be valid for the lifetime of the asynchronous operation. + template + void async_read_some(const MutableBufferSequence& buffers, + ReadHandler handler) + { + next_layer_.async_read_some(buffers, handler); + } + + /// Peek at the incoming data on the stream. Returns the number of bytes read. + /// Throws an exception on failure. + template + std::size_t peek(const MutableBufferSequence& buffers) + { + return next_layer_.peek(buffers); + } + + /// Peek at the incoming data on the stream. Returns the number of bytes read, + /// or 0 if an error occurred. + template + std::size_t peek(const MutableBufferSequence& buffers, + asio::error_code& ec) + { + return next_layer_.peek(buffers, ec); + } + + /// Determine the amount of data that may be read without blocking. + std::size_t in_avail() + { + return next_layer_.in_avail(); + } + + /// Determine the amount of data that may be read without blocking. + std::size_t in_avail(asio::error_code& ec) + { + return next_layer_.in_avail(ec); + } + +private: + /// Copy data into the internal buffer from the specified source buffer. + /// Returns the number of bytes copied. + template + std::size_t copy(const ConstBufferSequence& buffers) + { + using namespace std; // For memcpy. + + std::size_t orig_size = storage_.size(); + std::size_t space_avail = storage_.capacity() - orig_size; + std::size_t bytes_copied = 0; + + typename ConstBufferSequence::const_iterator iter = buffers.begin(); + typename ConstBufferSequence::const_iterator end = buffers.end(); + for (; iter != end && space_avail > 0; ++iter) + { + std::size_t bytes_avail = buffer_size(*iter); + std::size_t length = (bytes_avail < space_avail) + ? bytes_avail : space_avail; + storage_.resize(orig_size + bytes_copied + length); + memcpy(storage_.data() + orig_size + bytes_copied, + buffer_cast(*iter), length); + bytes_copied += length; + space_avail -= length; + } + + return bytes_copied; + } + + /// The next layer. + Stream next_layer_; + + // The data in the buffer. + detail::buffered_stream_storage storage_; +}; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_BUFFERED_WRITE_STREAM_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/buffered_write_stream_fwd.hpp b/encryption/libtorrent/include/libtorrent/asio/buffered_write_stream_fwd.hpp new file mode 100644 index 000000000..84cf36e3a --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/buffered_write_stream_fwd.hpp @@ -0,0 +1,29 @@ +// +// buffered_write_stream_fwd.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_BUFFERED_WRITE_STREAM_FWD_HPP +#define ASIO_BUFFERED_WRITE_STREAM_FWD_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +namespace asio { + +template +class buffered_write_stream; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_BUFFERED_WRITE_STREAM_FWD_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/completion_condition.hpp b/encryption/libtorrent/include/libtorrent/asio/completion_condition.hpp new file mode 100644 index 000000000..42696d599 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/completion_condition.hpp @@ -0,0 +1,101 @@ +// +// completion_condition.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_COMPLETION_CONDITION_HPP +#define ASIO_COMPLETION_CONDITION_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include "asio/detail/pop_options.hpp" + +namespace asio { + +namespace detail { + +class transfer_all_t +{ +public: + typedef bool result_type; + + template + bool operator()(const Error& err, std::size_t) + { + return !!err; + } +}; + +class transfer_at_least_t +{ +public: + typedef bool result_type; + + explicit transfer_at_least_t(std::size_t minimum) + : minimum_(minimum) + { + } + + template + bool operator()(const Error& err, std::size_t bytes_transferred) + { + return !!err || bytes_transferred >= minimum_; + } + +private: + std::size_t minimum_; +}; + +} // namespace detail + +/** + * @defgroup completion_condition Completion Condition Function Objects + * + * Function objects used for determining when a read or write operation should + * complete. + */ +/*@{*/ + +/// Return a completion condition function object that indicates that a read or +/// write operation should continue until all of the data has been transferred, +/// or until an error occurs. +#if defined(GENERATING_DOCUMENTATION) +unspecified transfer_all(); +#else +inline detail::transfer_all_t transfer_all() +{ + return detail::transfer_all_t(); +} +#endif + +/// Return a completion condition function object that indicates that a read or +/// write operation should continue until a minimum number of bytes has been +/// transferred, or until an error occurs. +#if defined(GENERATING_DOCUMENTATION) +unspecified transfer_at_least(std::size_t minimum); +#else +inline detail::transfer_at_least_t transfer_at_least(std::size_t minimum) +{ + return detail::transfer_at_least_t(minimum); +} +#endif + +/*@}*/ + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_COMPLETION_CONDITION_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/datagram_socket_service.hpp b/encryption/libtorrent/include/libtorrent/asio/datagram_socket_service.hpp new file mode 100644 index 000000000..1f858de61 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/datagram_socket_service.hpp @@ -0,0 +1,320 @@ +// +// datagram_socket_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DATAGRAM_SOCKET_SERVICE_HPP +#define ASIO_DATAGRAM_SOCKET_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/error.hpp" +#include "asio/io_service.hpp" +#include "asio/detail/epoll_reactor.hpp" +#include "asio/detail/kqueue_reactor.hpp" +#include "asio/detail/select_reactor.hpp" +#include "asio/detail/service_base.hpp" +#include "asio/detail/reactive_socket_service.hpp" +#include "asio/detail/win_iocp_socket_service.hpp" + +namespace asio { + +/// Default service implementation for a datagram socket. +template +class datagram_socket_service +#if defined(GENERATING_DOCUMENTATION) + : public asio::io_service::service +#else + : public asio::detail::service_base > +#endif +{ +public: +#if defined(GENERATING_DOCUMENTATION) + /// The unique service identifier. + static asio::io_service::id id; +#endif + + /// The protocol type. + typedef Protocol protocol_type; + + /// The endpoint type. + typedef typename Protocol::endpoint endpoint_type; + +private: + // The type of the platform-specific implementation. +#if defined(ASIO_HAS_IOCP) + typedef detail::win_iocp_socket_service service_impl_type; +#elif defined(ASIO_HAS_EPOLL) + typedef detail::reactive_socket_service< + Protocol, detail::epoll_reactor > service_impl_type; +#elif defined(ASIO_HAS_KQUEUE) + typedef detail::reactive_socket_service< + Protocol, detail::kqueue_reactor > service_impl_type; +#else + typedef detail::reactive_socket_service< + Protocol, detail::select_reactor > service_impl_type; +#endif + +public: + /// The type of a datagram socket. +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined implementation_type; +#else + typedef typename service_impl_type::implementation_type implementation_type; +#endif + + /// The native socket type. +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined native_type; +#else + typedef typename service_impl_type::native_type native_type; +#endif + + /// Construct a new datagram socket service for the specified io_service. + explicit datagram_socket_service(asio::io_service& io_service) + : asio::detail::service_base< + datagram_socket_service >(io_service), + service_impl_(asio::use_service(io_service)) + { + } + + /// Destroy all user-defined handler objects owned by the service. + void shutdown_service() + { + } + + /// Construct a new datagram socket implementation. + void construct(implementation_type& impl) + { + service_impl_.construct(impl); + } + + /// Destroy a datagram socket implementation. + void destroy(implementation_type& impl) + { + service_impl_.destroy(impl); + } + + // Open a new datagram socket implementation. + asio::error_code open(implementation_type& impl, + const protocol_type& protocol, asio::error_code& ec) + { + if (protocol.type() == SOCK_DGRAM) + service_impl_.open(impl, protocol, ec); + else + ec = asio::error::invalid_argument; + return ec; + } + + /// Assign an existing native socket to a datagram socket. + asio::error_code assign(implementation_type& impl, + const protocol_type& protocol, const native_type& native_socket, + asio::error_code& ec) + { + return service_impl_.assign(impl, protocol, native_socket, ec); + } + + /// Determine whether the socket is open. + bool is_open(const implementation_type& impl) const + { + return service_impl_.is_open(impl); + } + + /// Close a datagram socket implementation. + asio::error_code close(implementation_type& impl, + asio::error_code& ec) + { + return service_impl_.close(impl, ec); + } + + /// Get the native socket implementation. + native_type native(implementation_type& impl) + { + return service_impl_.native(impl); + } + + /// Cancel all asynchronous operations associated with the socket. + asio::error_code cancel(implementation_type& impl, + asio::error_code& ec) + { + return service_impl_.cancel(impl, ec); + } + + /// Determine whether the socket is at the out-of-band data mark. + bool at_mark(const implementation_type& impl, + asio::error_code& ec) const + { + return service_impl_.at_mark(impl, ec); + } + + /// Determine the number of bytes available for reading. + std::size_t available(const implementation_type& impl, + asio::error_code& ec) const + { + return service_impl_.available(impl, ec); + } + + // Bind the datagram socket to the specified local endpoint. + asio::error_code bind(implementation_type& impl, + const endpoint_type& endpoint, asio::error_code& ec) + { + return service_impl_.bind(impl, endpoint, ec); + } + + /// Connect the datagram socket to the specified endpoint. + asio::error_code connect(implementation_type& impl, + const endpoint_type& peer_endpoint, asio::error_code& ec) + { + return service_impl_.connect(impl, peer_endpoint, ec); + } + + /// Start an asynchronous connect. + template + void async_connect(implementation_type& impl, + const endpoint_type& peer_endpoint, ConnectHandler handler) + { + service_impl_.async_connect(impl, peer_endpoint, handler); + } + + /// Set a socket option. + template + asio::error_code set_option(implementation_type& impl, + const SettableSocketOption& option, asio::error_code& ec) + { + return service_impl_.set_option(impl, option, ec); + } + + /// Get a socket option. + template + asio::error_code get_option(const implementation_type& impl, + GettableSocketOption& option, asio::error_code& ec) const + { + return service_impl_.get_option(impl, option, ec); + } + + /// Perform an IO control command on the socket. + template + asio::error_code io_control(implementation_type& impl, + IoControlCommand& command, asio::error_code& ec) + { + return service_impl_.io_control(impl, command, ec); + } + + /// Get the local endpoint. + endpoint_type local_endpoint(const implementation_type& impl, + asio::error_code& ec) const + { + return service_impl_.local_endpoint(impl, ec); + } + + /// Get the remote endpoint. + endpoint_type remote_endpoint(const implementation_type& impl, + asio::error_code& ec) const + { + return service_impl_.remote_endpoint(impl, ec); + } + + /// Disable sends or receives on the socket. + asio::error_code shutdown(implementation_type& impl, + socket_base::shutdown_type what, asio::error_code& ec) + { + return service_impl_.shutdown(impl, what, ec); + } + + /// Send the given data to the peer. + template + std::size_t send(implementation_type& impl, + const ConstBufferSequence& buffers, + socket_base::message_flags flags, asio::error_code& ec) + { + return service_impl_.send(impl, buffers, flags, ec); + } + + /// Start an asynchronous send. + template + void async_send(implementation_type& impl, const ConstBufferSequence& buffers, + socket_base::message_flags flags, WriteHandler handler) + { + service_impl_.async_send(impl, buffers, flags, handler); + } + + /// Send a datagram to the specified endpoint. + template + std::size_t send_to(implementation_type& impl, + const ConstBufferSequence& buffers, const endpoint_type& destination, + socket_base::message_flags flags, asio::error_code& ec) + { + return service_impl_.send_to(impl, buffers, destination, flags, ec); + } + + /// Start an asynchronous send. + template + void async_send_to(implementation_type& impl, + const ConstBufferSequence& buffers, const endpoint_type& destination, + socket_base::message_flags flags, WriteHandler handler) + { + service_impl_.async_send_to(impl, buffers, destination, flags, handler); + } + + /// Receive some data from the peer. + template + std::size_t receive(implementation_type& impl, + const MutableBufferSequence& buffers, + socket_base::message_flags flags, asio::error_code& ec) + { + return service_impl_.receive(impl, buffers, flags, ec); + } + + /// Start an asynchronous receive. + template + void async_receive(implementation_type& impl, + const MutableBufferSequence& buffers, + socket_base::message_flags flags, ReadHandler handler) + { + service_impl_.async_receive(impl, buffers, flags, handler); + } + + /// Receive a datagram with the endpoint of the sender. + template + std::size_t receive_from(implementation_type& impl, + const MutableBufferSequence& buffers, endpoint_type& sender_endpoint, + socket_base::message_flags flags, asio::error_code& ec) + { + return service_impl_.receive_from(impl, buffers, sender_endpoint, flags, + ec); + } + + /// Start an asynchronous receive that will get the endpoint of the sender. + template + void async_receive_from(implementation_type& impl, + const MutableBufferSequence& buffers, endpoint_type& sender_endpoint, + socket_base::message_flags flags, ReadHandler handler) + { + service_impl_.async_receive_from(impl, buffers, sender_endpoint, flags, + handler); + } + +private: + // The service that provides the platform-specific implementation. + service_impl_type& service_impl_; +}; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DATAGRAM_SOCKET_SERVICE_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/deadline_timer.hpp b/encryption/libtorrent/include/libtorrent/asio/deadline_timer.hpp new file mode 100644 index 000000000..2079c5d61 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/deadline_timer.hpp @@ -0,0 +1,37 @@ +// +// deadline_timer.hpp +// ~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DEADLINE_TIMER_HPP +#define ASIO_DEADLINE_TIMER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/socket_types.hpp" // Must come before posix_time. + +#include "asio/detail/push_options.hpp" +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/basic_deadline_timer.hpp" + +namespace asio { + +/// Typedef for the typical usage of timer. +typedef basic_deadline_timer deadline_timer; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DEADLINE_TIMER_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/deadline_timer_service.hpp b/encryption/libtorrent/include/libtorrent/asio/deadline_timer_service.hpp new file mode 100644 index 000000000..17b97350b --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/deadline_timer_service.hpp @@ -0,0 +1,164 @@ +// +// deadline_timer_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DEADLINE_TIMER_SERVICE_HPP +#define ASIO_DEADLINE_TIMER_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/io_service.hpp" +#include "asio/time_traits.hpp" +#include "asio/detail/deadline_timer_service.hpp" +#include "asio/detail/epoll_reactor.hpp" +#include "asio/detail/kqueue_reactor.hpp" +#include "asio/detail/select_reactor.hpp" +#include "asio/detail/service_base.hpp" + +namespace asio { + +/// Default service implementation for a timer. +template > +class deadline_timer_service +#if defined(GENERATING_DOCUMENTATION) + : public asio::io_service::service +#else + : public asio::detail::service_base< + deadline_timer_service > +#endif +{ +public: +#if defined(GENERATING_DOCUMENTATION) + /// The unique service identifier. + static asio::io_service::id id; +#endif + + /// The time traits type. + typedef TimeTraits traits_type; + + /// The time type. + typedef typename traits_type::time_type time_type; + + /// The duration type. + typedef typename traits_type::duration_type duration_type; + +private: + // The type of the platform-specific implementation. +#if defined(ASIO_HAS_IOCP) + typedef detail::deadline_timer_service< + traits_type, detail::select_reactor > service_impl_type; +#elif defined(ASIO_HAS_EPOLL) + typedef detail::deadline_timer_service< + traits_type, detail::epoll_reactor > service_impl_type; +#elif defined(ASIO_HAS_KQUEUE) + typedef detail::deadline_timer_service< + traits_type, detail::kqueue_reactor > service_impl_type; +#else + typedef detail::deadline_timer_service< + traits_type, detail::select_reactor > service_impl_type; +#endif + +public: + /// The implementation type of the deadline timer. +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined implementation_type; +#else + typedef typename service_impl_type::implementation_type implementation_type; +#endif + + /// Construct a new timer service for the specified io_service. + explicit deadline_timer_service(asio::io_service& io_service) + : asio::detail::service_base< + deadline_timer_service >(io_service), + service_impl_(asio::use_service(io_service)) + { + } + + /// Destroy all user-defined handler objects owned by the service. + void shutdown_service() + { + } + + /// Construct a new timer implementation. + void construct(implementation_type& impl) + { + service_impl_.construct(impl); + } + + /// Destroy a timer implementation. + void destroy(implementation_type& impl) + { + service_impl_.destroy(impl); + } + + /// Cancel any asynchronous wait operations associated with the timer. + std::size_t cancel(implementation_type& impl, asio::error_code& ec) + { + return service_impl_.cancel(impl, ec); + } + + /// Get the expiry time for the timer as an absolute time. + time_type expires_at(const implementation_type& impl) const + { + return service_impl_.expires_at(impl); + } + + /// Set the expiry time for the timer as an absolute time. + std::size_t expires_at(implementation_type& impl, + const time_type& expiry_time, asio::error_code& ec) + { + return service_impl_.expires_at(impl, expiry_time, ec); + } + + /// Get the expiry time for the timer relative to now. + duration_type expires_from_now(const implementation_type& impl) const + { + return service_impl_.expires_from_now(impl); + } + + /// Set the expiry time for the timer relative to now. + std::size_t expires_from_now(implementation_type& impl, + const duration_type& expiry_time, asio::error_code& ec) + { + return service_impl_.expires_from_now(impl, expiry_time, ec); + } + + // Perform a blocking wait on the timer. + void wait(implementation_type& impl, asio::error_code& ec) + { + service_impl_.wait(impl, ec); + } + + // Start an asynchronous wait on the timer. + template + void async_wait(implementation_type& impl, WaitHandler handler) + { + service_impl_.async_wait(impl, handler); + } + +private: + // The service that provides the platform-specific implementation. + service_impl_type& service_impl_; +}; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DEADLINE_TIMER_SERVICE_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/detail/bind_handler.hpp b/encryption/libtorrent/include/libtorrent/asio/detail/bind_handler.hpp new file mode 100644 index 000000000..497bcfcc2 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/detail/bind_handler.hpp @@ -0,0 +1,349 @@ +// +// bind_handler.hpp +// ~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_BIND_HANDLER_HPP +#define ASIO_DETAIL_BIND_HANDLER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" + +namespace asio { +namespace detail { + +template +class binder1 +{ +public: + binder1(const Handler& handler, const Arg1& arg1) + : handler_(handler), + arg1_(arg1) + { + } + + void operator()() + { + handler_(arg1_); + } + + void operator()() const + { + handler_(arg1_); + } + +//private: + Handler handler_; + Arg1 arg1_; +}; + +template +inline void* asio_handler_allocate(std::size_t size, + binder1* this_handler) +{ + return asio_handler_alloc_helpers::allocate( + size, &this_handler->handler_); +} + +template +inline void asio_handler_deallocate(void* pointer, std::size_t size, + binder1* this_handler) +{ + asio_handler_alloc_helpers::deallocate( + pointer, size, &this_handler->handler_); +} + +template +inline void asio_handler_invoke(const Function& function, + binder1* this_handler) +{ + asio_handler_invoke_helpers::invoke( + function, &this_handler->handler_); +} + +template +inline binder1 bind_handler(const Handler& handler, + const Arg1& arg1) +{ + return binder1(handler, arg1); +} + +template +class binder2 +{ +public: + binder2(const Handler& handler, const Arg1& arg1, const Arg2& arg2) + : handler_(handler), + arg1_(arg1), + arg2_(arg2) + { + } + + void operator()() + { + handler_(arg1_, arg2_); + } + + void operator()() const + { + handler_(arg1_, arg2_); + } + +//private: + Handler handler_; + Arg1 arg1_; + Arg2 arg2_; +}; + +template +inline void* asio_handler_allocate(std::size_t size, + binder2* this_handler) +{ + return asio_handler_alloc_helpers::allocate( + size, &this_handler->handler_); +} + +template +inline void asio_handler_deallocate(void* pointer, std::size_t size, + binder2* this_handler) +{ + asio_handler_alloc_helpers::deallocate( + pointer, size, &this_handler->handler_); +} + +template +inline void asio_handler_invoke(const Function& function, + binder2* this_handler) +{ + asio_handler_invoke_helpers::invoke( + function, &this_handler->handler_); +} + +template +inline binder2 bind_handler(const Handler& handler, + const Arg1& arg1, const Arg2& arg2) +{ + return binder2(handler, arg1, arg2); +} + +template +class binder3 +{ +public: + binder3(const Handler& handler, const Arg1& arg1, const Arg2& arg2, + const Arg3& arg3) + : handler_(handler), + arg1_(arg1), + arg2_(arg2), + arg3_(arg3) + { + } + + void operator()() + { + handler_(arg1_, arg2_, arg3_); + } + + void operator()() const + { + handler_(arg1_, arg2_, arg3_); + } + +//private: + Handler handler_; + Arg1 arg1_; + Arg2 arg2_; + Arg3 arg3_; +}; + +template +inline void* asio_handler_allocate(std::size_t size, + binder3* this_handler) +{ + return asio_handler_alloc_helpers::allocate( + size, &this_handler->handler_); +} + +template +inline void asio_handler_deallocate(void* pointer, std::size_t size, + binder3* this_handler) +{ + asio_handler_alloc_helpers::deallocate( + pointer, size, &this_handler->handler_); +} + +template +inline void asio_handler_invoke(const Function& function, + binder3* this_handler) +{ + asio_handler_invoke_helpers::invoke( + function, &this_handler->handler_); +} + +template +inline binder3 bind_handler(const Handler& handler, + const Arg1& arg1, const Arg2& arg2, const Arg3& arg3) +{ + return binder3(handler, arg1, arg2, arg3); +} + +template +class binder4 +{ +public: + binder4(const Handler& handler, const Arg1& arg1, const Arg2& arg2, + const Arg3& arg3, const Arg4& arg4) + : handler_(handler), + arg1_(arg1), + arg2_(arg2), + arg3_(arg3), + arg4_(arg4) + { + } + + void operator()() + { + handler_(arg1_, arg2_, arg3_, arg4_); + } + + void operator()() const + { + handler_(arg1_, arg2_, arg3_, arg4_); + } + +//private: + Handler handler_; + Arg1 arg1_; + Arg2 arg2_; + Arg3 arg3_; + Arg4 arg4_; +}; + +template +inline void* asio_handler_allocate(std::size_t size, + binder4* this_handler) +{ + return asio_handler_alloc_helpers::allocate( + size, &this_handler->handler_); +} + +template +inline void asio_handler_deallocate(void* pointer, std::size_t size, + binder4* this_handler) +{ + asio_handler_alloc_helpers::deallocate( + pointer, size, &this_handler->handler_); +} + +template +inline void asio_handler_invoke(const Function& function, + binder4* this_handler) +{ + asio_handler_invoke_helpers::invoke( + function, &this_handler->handler_); +} + +template +inline binder4 bind_handler( + const Handler& handler, const Arg1& arg1, const Arg2& arg2, + const Arg3& arg3, const Arg4& arg4) +{ + return binder4(handler, arg1, arg2, arg3, + arg4); +} + +template +class binder5 +{ +public: + binder5(const Handler& handler, const Arg1& arg1, const Arg2& arg2, + const Arg3& arg3, const Arg4& arg4, const Arg5& arg5) + : handler_(handler), + arg1_(arg1), + arg2_(arg2), + arg3_(arg3), + arg4_(arg4), + arg5_(arg5) + { + } + + void operator()() + { + handler_(arg1_, arg2_, arg3_, arg4_, arg5_); + } + + void operator()() const + { + handler_(arg1_, arg2_, arg3_, arg4_, arg5_); + } + +//private: + Handler handler_; + Arg1 arg1_; + Arg2 arg2_; + Arg3 arg3_; + Arg4 arg4_; + Arg5 arg5_; +}; + +template +inline void* asio_handler_allocate(std::size_t size, + binder5* this_handler) +{ + return asio_handler_alloc_helpers::allocate( + size, &this_handler->handler_); +} + +template +inline void asio_handler_deallocate(void* pointer, std::size_t size, + binder5* this_handler) +{ + asio_handler_alloc_helpers::deallocate( + pointer, size, &this_handler->handler_); +} + +template +inline void asio_handler_invoke(const Function& function, + binder5* this_handler) +{ + asio_handler_invoke_helpers::invoke( + function, &this_handler->handler_); +} + +template +inline binder5 bind_handler( + const Handler& handler, const Arg1& arg1, const Arg2& arg2, + const Arg3& arg3, const Arg4& arg4, const Arg5& arg5) +{ + return binder5(handler, arg1, arg2, + arg3, arg4, arg5); +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_BIND_HANDLER_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/detail/buffer_resize_guard.hpp b/encryption/libtorrent/include/libtorrent/asio/detail/buffer_resize_guard.hpp new file mode 100644 index 000000000..0dcbe6956 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/detail/buffer_resize_guard.hpp @@ -0,0 +1,70 @@ +// +// buffer_resize_guard.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_BUFFER_RESIZE_GUARD_HPP +#define ASIO_DETAIL_BUFFER_RESIZE_GUARD_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include "asio/detail/pop_options.hpp" + +namespace asio { +namespace detail { + +// Helper class to manage buffer resizing in an exception safe way. +template +class buffer_resize_guard +{ +public: + // Constructor. + buffer_resize_guard(Buffer& buffer) + : buffer_(buffer), + old_size_(buffer.size()) + { + } + + // Destructor rolls back the buffer resize unless commit was called. + ~buffer_resize_guard() + { + if (old_size_ + != std::numeric_limits::max BOOST_PREVENT_MACRO_SUBSTITUTION()) + { + buffer_.resize(old_size_); + } + } + + // Commit the resize transaction. + void commit() + { + old_size_ + = std::numeric_limits::max BOOST_PREVENT_MACRO_SUBSTITUTION(); + } + +private: + // The buffer being managed. + Buffer& buffer_; + + // The size of the buffer at the time the guard was constructed. + size_t old_size_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_BUFFER_RESIZE_GUARD_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/detail/buffered_stream_storage.hpp b/encryption/libtorrent/include/libtorrent/asio/detail/buffered_stream_storage.hpp new file mode 100644 index 000000000..2a84d876d --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/detail/buffered_stream_storage.hpp @@ -0,0 +1,127 @@ +// +// buffered_stream_storage.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_BUFFERED_STREAM_STORAGE_HPP +#define ASIO_DETAIL_BUFFERED_STREAM_STORAGE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include +#include +#include +#include "asio/detail/pop_options.hpp" + +namespace asio { +namespace detail { + +class buffered_stream_storage +{ +public: + // The type of the bytes stored in the buffer. + typedef unsigned char byte_type; + + // The type used for offsets into the buffer. + typedef std::size_t size_type; + + // Constructor. + explicit buffered_stream_storage(std::size_t capacity) + : begin_offset_(0), + end_offset_(0), + buffer_(capacity) + { + } + + /// Clear the buffer. + void clear() + { + begin_offset_ = 0; + end_offset_ = 0; + } + + // Return a pointer to the beginning of the unread data. + byte_type* data() + { + return &buffer_[0] + begin_offset_; + } + + // Return a pointer to the beginning of the unread data. + const byte_type* data() const + { + return &buffer_[0] + begin_offset_; + } + + // Is there no unread data in the buffer. + bool empty() const + { + return begin_offset_ == end_offset_; + } + + // Return the amount of unread data the is in the buffer. + size_type size() const + { + return end_offset_ - begin_offset_; + } + + // Resize the buffer to the specified length. + void resize(size_type length) + { + assert(length <= capacity()); + if (begin_offset_ + length <= capacity()) + { + end_offset_ = begin_offset_ + length; + } + else + { + using namespace std; // For memmove. + memmove(&buffer_[0], &buffer_[0] + begin_offset_, size()); + end_offset_ = length; + begin_offset_ = 0; + } + } + + // Return the maximum size for data in the buffer. + size_type capacity() const + { + return buffer_.size(); + } + + // Consume multiple bytes from the beginning of the buffer. + void consume(size_type count) + { + assert(begin_offset_ + count <= end_offset_); + begin_offset_ += count; + if (empty()) + clear(); + } + +private: + // The offset to the beginning of the unread data. + size_type begin_offset_; + + // The offset to the end of the unread data. + size_type end_offset_; + + // The data in the buffer. + std::vector buffer_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_BUFFERED_STREAM_STORAGE_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/detail/call_stack.hpp b/encryption/libtorrent/include/libtorrent/asio/detail/call_stack.hpp new file mode 100644 index 000000000..1373f46c7 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/detail/call_stack.hpp @@ -0,0 +1,90 @@ +// +// call_stack.hpp +// ~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_CALL_STACK_HPP +#define ASIO_DETAIL_CALL_STACK_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/tss_ptr.hpp" + +namespace asio { +namespace detail { + +// Helper class to determine whether or not the current thread is inside an +// invocation of io_service::run() for a specified io_service object. +template +class call_stack +{ +public: + // Context class automatically pushes an owner on to the stack. + class context + : private noncopyable + { + public: + // Push the owner on to the stack. + explicit context(Owner* d) + : owner_(d), + next_(call_stack::top_) + { + call_stack::top_ = this; + } + + // Pop the owner from the stack. + ~context() + { + call_stack::top_ = next_; + } + + private: + friend class call_stack; + + // The owner associated with the context. + Owner* owner_; + + // The next element in the stack. + context* next_; + }; + + friend class context; + + // Determine whether the specified owner is on the stack. + static bool contains(Owner* d) + { + context* elem = top_; + while (elem) + { + if (elem->owner_ == d) + return true; + elem = elem->next_; + } + return false; + } + +private: + // The top of the stack of calls for the current thread. + static tss_ptr top_; +}; + +template +tss_ptr::context> +call_stack::top_; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_CALL_STACK_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/detail/const_buffers_iterator.hpp b/encryption/libtorrent/include/libtorrent/asio/detail/const_buffers_iterator.hpp new file mode 100644 index 000000000..964294865 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/detail/const_buffers_iterator.hpp @@ -0,0 +1,151 @@ +// +// const_buffers_iterator.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_CONST_BUFFERS_ITERATOR_HPP +#define ASIO_DETAIL_CONST_BUFFERS_ITERATOR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/buffer.hpp" + +namespace asio { +namespace detail { + +// A proxy iterator for a sub-range in a list of buffers. +template +class const_buffers_iterator + : public boost::iterator_facade, + const char, boost::bidirectional_traversal_tag> +{ +public: + // Default constructor creates an iterator in an undefined state. + const_buffers_iterator() + { + } + + // Create an iterator for the specified position. + const_buffers_iterator(const ConstBufferSequence& buffers, + std::size_t position) + : begin_(buffers.begin()), + current_(buffers.begin()), + end_(buffers.end()), + position_(0) + { + while (current_ != end_) + { + current_buffer_ = *current_; + std::size_t buffer_size = asio::buffer_size(current_buffer_); + if (position - position_ < buffer_size) + { + current_buffer_position_ = position - position_; + position_ = position; + return; + } + position_ += buffer_size; + ++current_; + } + current_buffer_ = asio::const_buffer(); + current_buffer_position_ = 0; + } + + std::size_t position() const + { + return position_; + } + +private: + friend class boost::iterator_core_access; + + void increment() + { + if (current_ == end_) + return; + + ++position_; + + ++current_buffer_position_; + if (current_buffer_position_ != asio::buffer_size(current_buffer_)) + return; + + ++current_; + current_buffer_position_ = 0; + while (current_ != end_) + { + current_buffer_ = *current_; + if (asio::buffer_size(current_buffer_) > 0) + return; + ++current_; + } + } + + void decrement() + { + if (position_ == 0) + return; + + --position_; + + if (current_buffer_position_ != 0) + { + --current_buffer_position_; + return; + } + + typename ConstBufferSequence::const_iterator iter = current_; + while (iter != begin_) + { + --iter; + asio::const_buffer buffer = *iter; + std::size_t buffer_size = asio::buffer_size(buffer); + if (buffer_size > 0) + { + current_ = iter; + current_buffer_ = buffer; + current_buffer_position_ = buffer_size - 1; + return; + } + } + } + + bool equal(const const_buffers_iterator& other) const + { + return position_ == other.position_; + } + + const char& dereference() const + { + return asio::buffer_cast( + current_buffer_)[current_buffer_position_]; + } + + asio::const_buffer current_buffer_; + std::size_t current_buffer_position_; + typename ConstBufferSequence::const_iterator begin_; + typename ConstBufferSequence::const_iterator current_; + typename ConstBufferSequence::const_iterator end_; + std::size_t position_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_CONST_BUFFERS_ITERATOR_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/detail/consuming_buffers.hpp b/encryption/libtorrent/include/libtorrent/asio/detail/consuming_buffers.hpp new file mode 100644 index 000000000..cbe38a926 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/detail/consuming_buffers.hpp @@ -0,0 +1,205 @@ +// +// consuming_buffers.hpp +// ~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_CONSUMING_BUFFERS_HPP +#define ASIO_DETAIL_CONSUMING_BUFFERS_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include +#include +#include "asio/detail/pop_options.hpp" + +namespace asio { +namespace detail { + +// A proxy iterator for a sub-range in a list of buffers. +template +class consuming_buffers_iterator + : public boost::iterator_facade< + consuming_buffers_iterator, + const Buffer, boost::forward_traversal_tag> +{ +public: + // Default constructor creates an end iterator. + consuming_buffers_iterator() + : at_end_(true) + { + } + + // Construct with a buffer for the first entry and an iterator + // range for the remaining entries. + consuming_buffers_iterator(bool at_end, const Buffer& first, + Buffer_Iterator begin_remainder, Buffer_Iterator end_remainder) + : at_end_(at_end), + first_(buffer(first, max_size)), + begin_remainder_(begin_remainder), + end_remainder_(end_remainder), + offset_(0) + { + } + +private: + friend class boost::iterator_core_access; + + enum { max_size = 65536 }; + + void increment() + { + if (!at_end_) + { + if (begin_remainder_ == end_remainder_ + || offset_ + buffer_size(first_) >= max_size) + { + at_end_ = true; + } + else + { + offset_ += buffer_size(first_); + first_ = buffer(*begin_remainder_++, max_size - offset_); + } + } + } + + bool equal(const consuming_buffers_iterator& other) const + { + if (at_end_ && other.at_end_) + return true; + return !at_end_ && !other.at_end_ + && buffer_cast(first_) + == buffer_cast(other.first_) + && buffer_size(first_) == buffer_size(other.first_) + && begin_remainder_ == other.begin_remainder_ + && end_remainder_ == other.end_remainder_; + } + + const Buffer& dereference() const + { + return first_; + } + + bool at_end_; + Buffer first_; + Buffer_Iterator begin_remainder_; + Buffer_Iterator end_remainder_; + std::size_t offset_; +}; + +// A proxy for a sub-range in a list of buffers. +template +class consuming_buffers +{ +public: + // The type for each element in the list of buffers. + typedef Buffer value_type; + + // A forward-only iterator type that may be used to read elements. + typedef consuming_buffers_iterator + const_iterator; + + // Construct to represent the entire list of buffers. + consuming_buffers(const Buffers& buffers) + : buffers_(buffers), + at_end_(buffers_.begin() == buffers_.end()), + first_(*buffers_.begin()), + begin_remainder_(buffers_.begin()) + { + if (!at_end_) + ++begin_remainder_; + } + + // Copy constructor. + consuming_buffers(const consuming_buffers& other) + : buffers_(other.buffers_), + at_end_(other.at_end_), + first_(other.first_), + begin_remainder_(buffers_.begin()) + { + typename Buffers::const_iterator first = other.buffers_.begin(); + typename Buffers::const_iterator second = other.begin_remainder_; + std::advance(begin_remainder_, std::distance(first, second)); + } + + // Assignment operator. + consuming_buffers& operator=(const consuming_buffers& other) + { + buffers_ = other.buffers_; + at_end_ = other.at_end_; + first_ = other.first_; + begin_remainder_ = buffers_.begin(); + typename Buffers::const_iterator first = other.buffers_.begin(); + typename Buffers::const_iterator second = other.begin_remainder_; + std::advance(begin_remainder_, std::distance(first, second)); + return *this; + } + + // Get a forward-only iterator to the first element. + const_iterator begin() const + { + return const_iterator(at_end_, first_, begin_remainder_, buffers_.end()); + } + + // Get a forward-only iterator for one past the last element. + const_iterator end() const + { + return const_iterator(); + } + + // Consume the specified number of bytes from the buffers. + void consume(std::size_t size) + { + // Remove buffers from the start until the specified size is reached. + while (size > 0 && !at_end_) + { + if (buffer_size(first_) <= size) + { + size -= buffer_size(first_); + if (begin_remainder_ == buffers_.end()) + at_end_ = true; + else + first_ = *begin_remainder_++; + } + else + { + first_ = first_ + size; + size = 0; + } + } + + // Remove any more empty buffers at the start. + while (!at_end_ && buffer_size(first_) == 0) + { + if (begin_remainder_ == buffers_.end()) + at_end_ = true; + else + first_ = *begin_remainder_++; + } + } + +private: + Buffers buffers_; + bool at_end_; + Buffer first_; + typename Buffers::const_iterator begin_remainder_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_CONSUMING_BUFFERS_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/detail/deadline_timer_service.hpp b/encryption/libtorrent/include/libtorrent/asio/detail/deadline_timer_service.hpp new file mode 100644 index 000000000..c22c5a7b7 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/detail/deadline_timer_service.hpp @@ -0,0 +1,199 @@ +// +// deadline_timer_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_DEADLINE_TIMER_SERVICE_HPP +#define ASIO_DETAIL_DEADLINE_TIMER_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/error.hpp" +#include "asio/io_service.hpp" +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/service_base.hpp" +#include "asio/detail/socket_ops.hpp" +#include "asio/detail/socket_types.hpp" +#include "asio/detail/timer_queue.hpp" + +namespace asio { +namespace detail { + +template +class deadline_timer_service + : public asio::detail::service_base< + deadline_timer_service > +{ +public: + // The time type. + typedef typename Time_Traits::time_type time_type; + + // The duration type. + typedef typename Time_Traits::duration_type duration_type; + + // The implementation type of the timer. This type is dependent on the + // underlying implementation of the timer service. + struct implementation_type + : private asio::detail::noncopyable + { + time_type expiry; + bool might_have_pending_waits; + }; + + // Constructor. + deadline_timer_service(asio::io_service& io_service) + : asio::detail::service_base< + deadline_timer_service >(io_service), + scheduler_(asio::use_service(io_service)) + { + scheduler_.add_timer_queue(timer_queue_); + } + + // Destructor. + ~deadline_timer_service() + { + scheduler_.remove_timer_queue(timer_queue_); + } + + // Destroy all user-defined handler objects owned by the service. + void shutdown_service() + { + } + + // Construct a new timer implementation. + void construct(implementation_type& impl) + { + impl.expiry = time_type(); + impl.might_have_pending_waits = false; + } + + // Destroy a timer implementation. + void destroy(implementation_type& impl) + { + asio::error_code ec; + cancel(impl, ec); + } + + // Cancel any asynchronous wait operations associated with the timer. + std::size_t cancel(implementation_type& impl, asio::error_code& ec) + { + if (!impl.might_have_pending_waits) + { + ec = asio::error_code(); + return 0; + } + std::size_t count = scheduler_.cancel_timer(timer_queue_, &impl); + impl.might_have_pending_waits = false; + ec = asio::error_code(); + return count; + } + + // Get the expiry time for the timer as an absolute time. + time_type expires_at(const implementation_type& impl) const + { + return impl.expiry; + } + + // Set the expiry time for the timer as an absolute time. + std::size_t expires_at(implementation_type& impl, + const time_type& expiry_time, asio::error_code& ec) + { + std::size_t count = cancel(impl, ec); + impl.expiry = expiry_time; + ec = asio::error_code(); + return count; + } + + // Get the expiry time for the timer relative to now. + duration_type expires_from_now(const implementation_type& impl) const + { + return Time_Traits::subtract(expires_at(impl), Time_Traits::now()); + } + + // Set the expiry time for the timer relative to now. + std::size_t expires_from_now(implementation_type& impl, + const duration_type& expiry_time, asio::error_code& ec) + { + return expires_at(impl, + Time_Traits::add(Time_Traits::now(), expiry_time), ec); + } + + // Perform a blocking wait on the timer. + void wait(implementation_type& impl, asio::error_code& ec) + { + time_type now = Time_Traits::now(); + while (Time_Traits::less_than(now, impl.expiry)) + { + boost::posix_time::time_duration timeout = + Time_Traits::to_posix_duration(Time_Traits::subtract(impl.expiry, now)); + ::timeval tv; + tv.tv_sec = timeout.total_seconds(); + tv.tv_usec = timeout.total_microseconds() % 1000000; + asio::error_code ec; + socket_ops::select(0, 0, 0, 0, &tv, ec); + now = Time_Traits::now(); + } + ec = asio::error_code(); + } + + template + class wait_handler + { + public: + wait_handler(asio::io_service& io_service, Handler handler) + : io_service_(io_service), + work_(io_service), + handler_(handler) + { + } + + void operator()(const asio::error_code& result) + { + io_service_.post(detail::bind_handler(handler_, result)); + } + + private: + asio::io_service& io_service_; + asio::io_service::work work_; + Handler handler_; + }; + + // Start an asynchronous wait on the timer. + template + void async_wait(implementation_type& impl, Handler handler) + { + impl.might_have_pending_waits = true; + scheduler_.schedule_timer(timer_queue_, impl.expiry, + wait_handler(this->io_service(), handler), &impl); + } + +private: + // The queue of timers. + timer_queue timer_queue_; + + // The object that schedules and executes timers. Usually a reactor. + Timer_Scheduler& scheduler_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_DEADLINE_TIMER_SERVICE_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/detail/epoll_reactor.hpp b/encryption/libtorrent/include/libtorrent/asio/detail/epoll_reactor.hpp new file mode 100644 index 000000000..d55e86454 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/detail/epoll_reactor.hpp @@ -0,0 +1,613 @@ +// +// epoll_reactor.hpp +// ~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_EPOLL_REACTOR_HPP +#define ASIO_DETAIL_EPOLL_REACTOR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/epoll_reactor_fwd.hpp" + +#if defined(ASIO_HAS_EPOLL) + +#include "asio/detail/push_options.hpp" +#include +#include +#include +#include +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/error.hpp" +#include "asio/io_service.hpp" +#include "asio/system_error.hpp" +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/hash_map.hpp" +#include "asio/detail/mutex.hpp" +#include "asio/detail/task_io_service.hpp" +#include "asio/detail/thread.hpp" +#include "asio/detail/reactor_op_queue.hpp" +#include "asio/detail/select_interrupter.hpp" +#include "asio/detail/service_base.hpp" +#include "asio/detail/signal_blocker.hpp" +#include "asio/detail/socket_types.hpp" +#include "asio/detail/timer_queue.hpp" + +namespace asio { +namespace detail { + +template +class epoll_reactor + : public asio::detail::service_base > +{ +public: + // Constructor. + epoll_reactor(asio::io_service& io_service) + : asio::detail::service_base >(io_service), + mutex_(), + epoll_fd_(do_epoll_create()), + wait_in_progress_(false), + interrupter_(), + read_op_queue_(), + write_op_queue_(), + except_op_queue_(), + pending_cancellations_(), + stop_thread_(false), + thread_(0), + shutdown_(false) + { + // Start the reactor's internal thread only if needed. + if (Own_Thread) + { + asio::detail::signal_blocker sb; + thread_ = new asio::detail::thread( + bind_handler(&epoll_reactor::call_run_thread, this)); + } + + // Add the interrupter's descriptor to epoll. + epoll_event ev = { 0, { 0 } }; + ev.events = EPOLLIN | EPOLLERR; + ev.data.fd = interrupter_.read_descriptor(); + epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, interrupter_.read_descriptor(), &ev); + } + + // Destructor. + ~epoll_reactor() + { + shutdown_service(); + close(epoll_fd_); + } + + // Destroy all user-defined handler objects owned by the service. + void shutdown_service() + { + asio::detail::mutex::scoped_lock lock(mutex_); + shutdown_ = true; + stop_thread_ = true; + lock.unlock(); + + if (thread_) + { + interrupter_.interrupt(); + thread_->join(); + delete thread_; + thread_ = 0; + } + + read_op_queue_.destroy_operations(); + write_op_queue_.destroy_operations(); + except_op_queue_.destroy_operations(); + + for (std::size_t i = 0; i < timer_queues_.size(); ++i) + timer_queues_[i]->destroy_timers(); + timer_queues_.clear(); + } + + // Register a socket with the reactor. Returns 0 on success, system error + // code on failure. + int register_descriptor(socket_type descriptor) + { + // No need to lock according to epoll documentation. + + epoll_event ev = { 0, { 0 } }; + ev.events = 0; + ev.data.fd = descriptor; + int result = epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, descriptor, &ev); + if (result != 0) + return errno; + return 0; + } + + // Start a new read operation. The handler object will be invoked when the + // given descriptor is ready to be read, or an error has occurred. + template + void start_read_op(socket_type descriptor, Handler handler) + { + asio::detail::mutex::scoped_lock lock(mutex_); + + if (shutdown_) + return; + + if (!read_op_queue_.has_operation(descriptor)) + if (handler(asio::error_code())) + return; + + if (read_op_queue_.enqueue_operation(descriptor, handler)) + { + epoll_event ev = { 0, { 0 } }; + ev.events = EPOLLIN | EPOLLERR | EPOLLHUP; + if (write_op_queue_.has_operation(descriptor)) + ev.events |= EPOLLOUT; + if (except_op_queue_.has_operation(descriptor)) + ev.events |= EPOLLPRI; + ev.data.fd = descriptor; + + int result = epoll_ctl(epoll_fd_, EPOLL_CTL_MOD, descriptor, &ev); + if (result != 0) + { + asio::error_code ec(errno, asio::native_ecat); + read_op_queue_.dispatch_all_operations(descriptor, ec); + } + } + } + + // Start a new write operation. The handler object will be invoked when the + // given descriptor is ready to be written, or an error has occurred. + template + void start_write_op(socket_type descriptor, Handler handler) + { + asio::detail::mutex::scoped_lock lock(mutex_); + + if (shutdown_) + return; + + if (!write_op_queue_.has_operation(descriptor)) + if (handler(asio::error_code())) + return; + + if (write_op_queue_.enqueue_operation(descriptor, handler)) + { + epoll_event ev = { 0, { 0 } }; + ev.events = EPOLLOUT | EPOLLERR | EPOLLHUP; + if (read_op_queue_.has_operation(descriptor)) + ev.events |= EPOLLIN; + if (except_op_queue_.has_operation(descriptor)) + ev.events |= EPOLLPRI; + ev.data.fd = descriptor; + + int result = epoll_ctl(epoll_fd_, EPOLL_CTL_MOD, descriptor, &ev); + if (result != 0) + { + asio::error_code ec(errno, asio::native_ecat); + write_op_queue_.dispatch_all_operations(descriptor, ec); + } + } + } + + // Start a new exception operation. The handler object will be invoked when + // the given descriptor has exception information, or an error has occurred. + template + void start_except_op(socket_type descriptor, Handler handler) + { + asio::detail::mutex::scoped_lock lock(mutex_); + + if (shutdown_) + return; + + if (except_op_queue_.enqueue_operation(descriptor, handler)) + { + epoll_event ev = { 0, { 0 } }; + ev.events = EPOLLPRI | EPOLLERR | EPOLLHUP; + if (read_op_queue_.has_operation(descriptor)) + ev.events |= EPOLLIN; + if (write_op_queue_.has_operation(descriptor)) + ev.events |= EPOLLOUT; + ev.data.fd = descriptor; + + int result = epoll_ctl(epoll_fd_, EPOLL_CTL_MOD, descriptor, &ev); + if (result != 0) + { + asio::error_code ec(errno, asio::native_ecat); + except_op_queue_.dispatch_all_operations(descriptor, ec); + } + } + } + + // Start new write and exception operations. The handler object will be + // invoked when the given descriptor is ready for writing or has exception + // information available, or an error has occurred. + template + void start_write_and_except_ops(socket_type descriptor, Handler handler) + { + asio::detail::mutex::scoped_lock lock(mutex_); + + if (shutdown_) + return; + + bool need_mod = write_op_queue_.enqueue_operation(descriptor, handler); + need_mod = except_op_queue_.enqueue_operation(descriptor, handler) + && need_mod; + if (need_mod) + { + epoll_event ev = { 0, { 0 } }; + ev.events = EPOLLOUT | EPOLLPRI | EPOLLERR | EPOLLHUP; + if (read_op_queue_.has_operation(descriptor)) + ev.events |= EPOLLIN; + ev.data.fd = descriptor; + + int result = epoll_ctl(epoll_fd_, EPOLL_CTL_MOD, descriptor, &ev); + if (result != 0) + { + asio::error_code ec(errno, asio::native_ecat); + write_op_queue_.dispatch_all_operations(descriptor, ec); + except_op_queue_.dispatch_all_operations(descriptor, ec); + } + } + } + + // Cancel all operations associated with the given descriptor. The + // handlers associated with the descriptor will be invoked with the + // operation_aborted error. + void cancel_ops(socket_type descriptor) + { + asio::detail::mutex::scoped_lock lock(mutex_); + cancel_ops_unlocked(descriptor); + } + + // Enqueue cancellation of all operations associated with the given + // descriptor. The handlers associated with the descriptor will be invoked + // with the operation_aborted error. This function does not acquire the + // epoll_reactor's mutex, and so should only be used from within a reactor + // handler. + void enqueue_cancel_ops_unlocked(socket_type descriptor) + { + pending_cancellations_.push_back(descriptor); + } + + // Cancel any operations that are running against the descriptor and remove + // its registration from the reactor. + void close_descriptor(socket_type descriptor) + { + asio::detail::mutex::scoped_lock lock(mutex_); + + // Remove the descriptor from epoll. + epoll_event ev = { 0, { 0 } }; + epoll_ctl(epoll_fd_, EPOLL_CTL_DEL, descriptor, &ev); + + // Cancel any outstanding operations associated with the descriptor. + cancel_ops_unlocked(descriptor); + } + + // Add a new timer queue to the reactor. + template + void add_timer_queue(timer_queue& timer_queue) + { + asio::detail::mutex::scoped_lock lock(mutex_); + timer_queues_.push_back(&timer_queue); + } + + // Remove a timer queue from the reactor. + template + void remove_timer_queue(timer_queue& timer_queue) + { + asio::detail::mutex::scoped_lock lock(mutex_); + for (std::size_t i = 0; i < timer_queues_.size(); ++i) + { + if (timer_queues_[i] == &timer_queue) + { + timer_queues_.erase(timer_queues_.begin() + i); + return; + } + } + } + + // Schedule a timer in the given timer queue to expire at the specified + // absolute time. The handler object will be invoked when the timer expires. + template + void schedule_timer(timer_queue& timer_queue, + const typename Time_Traits::time_type& time, Handler handler, void* token) + { + asio::detail::mutex::scoped_lock lock(mutex_); + if (!shutdown_) + if (timer_queue.enqueue_timer(time, handler, token)) + interrupter_.interrupt(); + } + + // Cancel the timer associated with the given token. Returns the number of + // handlers that have been posted or dispatched. + template + std::size_t cancel_timer(timer_queue& timer_queue, void* token) + { + asio::detail::mutex::scoped_lock lock(mutex_); + return timer_queue.cancel_timer(token); + } + +private: + friend class task_io_service >; + + // Run epoll once until interrupted or events are ready to be dispatched. + void run(bool block) + { + asio::detail::mutex::scoped_lock lock(mutex_); + + // Dispatch any operation cancellations that were made while the select + // loop was not running. + read_op_queue_.dispatch_cancellations(); + write_op_queue_.dispatch_cancellations(); + except_op_queue_.dispatch_cancellations(); + + // Check if the thread is supposed to stop. + if (stop_thread_) + { + // Clean up operations. We must not hold the lock since the operations may + // make calls back into this reactor. + lock.unlock(); + read_op_queue_.cleanup_operations(); + write_op_queue_.cleanup_operations(); + except_op_queue_.cleanup_operations(); + return; + } + + // We can return immediately if there's no work to do and the reactor is + // not supposed to block. + if (!block && read_op_queue_.empty() && write_op_queue_.empty() + && except_op_queue_.empty() && all_timer_queues_are_empty()) + { + // Clean up operations. We must not hold the lock since the operations may + // make calls back into this reactor. + lock.unlock(); + read_op_queue_.cleanup_operations(); + write_op_queue_.cleanup_operations(); + except_op_queue_.cleanup_operations(); + return; + } + + int timeout = block ? get_timeout() : 0; + wait_in_progress_ = true; + lock.unlock(); + + // Block on the epoll descriptor. + epoll_event events[128]; + int num_events = epoll_wait(epoll_fd_, events, 128, timeout); + + lock.lock(); + wait_in_progress_ = false; + + // Block signals while dispatching operations. + asio::detail::signal_blocker sb; + + // Dispatch the waiting events. + for (int i = 0; i < num_events; ++i) + { + int descriptor = events[i].data.fd; + if (descriptor == interrupter_.read_descriptor()) + { + interrupter_.reset(); + } + else + { + if (events[i].events & (EPOLLERR | EPOLLHUP)) + { + asio::error_code ec; + except_op_queue_.dispatch_all_operations(descriptor, ec); + read_op_queue_.dispatch_all_operations(descriptor, ec); + write_op_queue_.dispatch_all_operations(descriptor, ec); + + epoll_event ev = { 0, { 0 } }; + ev.events = 0; + ev.data.fd = descriptor; + epoll_ctl(epoll_fd_, EPOLL_CTL_MOD, descriptor, &ev); + } + else + { + bool more_reads = false; + bool more_writes = false; + bool more_except = false; + asio::error_code ec; + + // Exception operations must be processed first to ensure that any + // out-of-band data is read before normal data. + if (events[i].events & EPOLLPRI) + more_except = except_op_queue_.dispatch_operation(descriptor, ec); + else + more_except = except_op_queue_.has_operation(descriptor); + + if (events[i].events & EPOLLIN) + more_reads = read_op_queue_.dispatch_operation(descriptor, ec); + else + more_reads = read_op_queue_.has_operation(descriptor); + + if (events[i].events & EPOLLOUT) + more_writes = write_op_queue_.dispatch_operation(descriptor, ec); + else + more_writes = write_op_queue_.has_operation(descriptor); + + epoll_event ev = { 0, { 0 } }; + ev.events = EPOLLERR | EPOLLHUP; + if (more_reads) + ev.events |= EPOLLIN; + if (more_writes) + ev.events |= EPOLLOUT; + if (more_except) + ev.events |= EPOLLPRI; + ev.data.fd = descriptor; + int result = epoll_ctl(epoll_fd_, EPOLL_CTL_MOD, descriptor, &ev); + if (result != 0) + { + ec = asio::error_code(errno, asio::native_ecat); + read_op_queue_.dispatch_all_operations(descriptor, ec); + write_op_queue_.dispatch_all_operations(descriptor, ec); + except_op_queue_.dispatch_all_operations(descriptor, ec); + } + } + } + } + read_op_queue_.dispatch_cancellations(); + write_op_queue_.dispatch_cancellations(); + except_op_queue_.dispatch_cancellations(); + for (std::size_t i = 0; i < timer_queues_.size(); ++i) + timer_queues_[i]->dispatch_timers(); + + // Issue any pending cancellations. + for (size_t i = 0; i < pending_cancellations_.size(); ++i) + cancel_ops_unlocked(pending_cancellations_[i]); + pending_cancellations_.clear(); + + // Clean up operations. We must not hold the lock since the operations may + // make calls back into this reactor. + lock.unlock(); + read_op_queue_.cleanup_operations(); + write_op_queue_.cleanup_operations(); + except_op_queue_.cleanup_operations(); + } + + // Run the select loop in the thread. + void run_thread() + { + asio::detail::mutex::scoped_lock lock(mutex_); + while (!stop_thread_) + { + lock.unlock(); + run(true); + lock.lock(); + } + } + + // Entry point for the select loop thread. + static void call_run_thread(epoll_reactor* reactor) + { + reactor->run_thread(); + } + + // Interrupt the select loop. + void interrupt() + { + interrupter_.interrupt(); + } + + // The hint to pass to epoll_create to size its data structures. + enum { epoll_size = 20000 }; + + // Create the epoll file descriptor. Throws an exception if the descriptor + // cannot be created. + static int do_epoll_create() + { + int fd = epoll_create(epoll_size); + if (fd == -1) + { + boost::throw_exception(asio::system_error( + asio::error_code(errno, asio::native_ecat), + "epoll")); + } + return fd; + } + + // Check if all timer queues are empty. + bool all_timer_queues_are_empty() const + { + for (std::size_t i = 0; i < timer_queues_.size(); ++i) + if (!timer_queues_[i]->empty()) + return false; + return true; + } + + // Get the timeout value for the epoll_wait call. The timeout value is + // returned as a number of milliseconds. A return value of -1 indicates + // that epoll_wait should block indefinitely. + int get_timeout() + { + if (all_timer_queues_are_empty()) + return -1; + + // By default we will wait no longer than 5 minutes. This will ensure that + // any changes to the system clock are detected after no longer than this. + boost::posix_time::time_duration minimum_wait_duration + = boost::posix_time::minutes(5); + + for (std::size_t i = 0; i < timer_queues_.size(); ++i) + { + boost::posix_time::time_duration wait_duration + = timer_queues_[i]->wait_duration(); + if (wait_duration < minimum_wait_duration) + minimum_wait_duration = wait_duration; + } + + if (minimum_wait_duration > boost::posix_time::time_duration()) + { + return minimum_wait_duration.total_milliseconds(); + } + else + { + return 0; + } + } + + // Cancel all operations associated with the given descriptor. The do_cancel + // function of the handler objects will be invoked. This function does not + // acquire the epoll_reactor's mutex. + void cancel_ops_unlocked(socket_type descriptor) + { + bool interrupt = read_op_queue_.cancel_operations(descriptor); + interrupt = write_op_queue_.cancel_operations(descriptor) || interrupt; + interrupt = except_op_queue_.cancel_operations(descriptor) || interrupt; + if (interrupt) + interrupter_.interrupt(); + } + + // Mutex to protect access to internal data. + asio::detail::mutex mutex_; + + // The epoll file descriptor. + int epoll_fd_; + + // Whether the epoll_wait call is currently in progress + bool wait_in_progress_; + + // The interrupter is used to break a blocking epoll_wait call. + select_interrupter interrupter_; + + // The queue of read operations. + reactor_op_queue read_op_queue_; + + // The queue of write operations. + reactor_op_queue write_op_queue_; + + // The queue of except operations. + reactor_op_queue except_op_queue_; + + // The timer queues. + std::vector timer_queues_; + + // The descriptors that are pending cancellation. + std::vector pending_cancellations_; + + // Does the reactor loop thread need to stop. + bool stop_thread_; + + // The thread that is running the reactor loop. + asio::detail::thread* thread_; + + // Whether the service has been shut down. + bool shutdown_; +}; + +} // namespace detail +} // namespace asio + +#endif // defined(ASIO_HAS_EPOLL) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_EPOLL_REACTOR_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/detail/epoll_reactor_fwd.hpp b/encryption/libtorrent/include/libtorrent/asio/detail/epoll_reactor_fwd.hpp new file mode 100644 index 000000000..720d7ca71 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/detail/epoll_reactor_fwd.hpp @@ -0,0 +1,44 @@ +// +// epoll_reactor_fwd.hpp +// ~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_EPOLL_REACTOR_FWD_HPP +#define ASIO_DETAIL_EPOLL_REACTOR_FWD_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#if !defined(ASIO_DISABLE_EPOLL) +#if defined(__linux__) // This service is only supported on Linux. + +#include "asio/detail/push_options.hpp" +#include +#include "asio/detail/pop_options.hpp" + +// Define this to indicate that epoll is supported on the target platform. +#define ASIO_HAS_EPOLL 1 + +namespace asio { +namespace detail { + +template +class epoll_reactor; + +} // namespace detail +} // namespace asio + +#endif // defined(__linux__) +#endif // !defined(ASIO_DISABLE_EPOLL) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_EPOLL_REACTOR_FWD_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/detail/event.hpp b/encryption/libtorrent/include/libtorrent/asio/detail/event.hpp new file mode 100644 index 000000000..766f51716 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/detail/event.hpp @@ -0,0 +1,50 @@ +// +// event.hpp +// ~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_EVENT_HPP +#define ASIO_DETAIL_EVENT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include "asio/detail/pop_options.hpp" + +#if !defined(BOOST_HAS_THREADS) +# include "asio/detail/null_event.hpp" +#elif defined(BOOST_WINDOWS) +# include "asio/detail/win_event.hpp" +#elif defined(BOOST_HAS_PTHREADS) +# include "asio/detail/posix_event.hpp" +#else +# error Only Windows and POSIX are supported! +#endif + +namespace asio { +namespace detail { + +#if !defined(BOOST_HAS_THREADS) +typedef null_event event; +#elif defined(BOOST_WINDOWS) +typedef win_event event; +#elif defined(BOOST_HAS_PTHREADS) +typedef posix_event event; +#endif + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_EVENT_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/detail/fd_set_adapter.hpp b/encryption/libtorrent/include/libtorrent/asio/detail/fd_set_adapter.hpp new file mode 100644 index 000000000..1d01dc5fb --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/detail/fd_set_adapter.hpp @@ -0,0 +1,41 @@ +// +// fd_set_adapter.hpp +// ~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_FD_SET_ADAPTER_HPP +#define ASIO_DETAIL_FD_SET_ADAPTER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/detail/posix_fd_set_adapter.hpp" +#include "asio/detail/win_fd_set_adapter.hpp" + +namespace asio { +namespace detail { + +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) +typedef win_fd_set_adapter fd_set_adapter; +#else +typedef posix_fd_set_adapter fd_set_adapter; +#endif + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_FD_SET_ADAPTER_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/detail/handler_alloc_helpers.hpp b/encryption/libtorrent/include/libtorrent/asio/detail/handler_alloc_helpers.hpp new file mode 100644 index 000000000..68e7cb15d --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/detail/handler_alloc_helpers.hpp @@ -0,0 +1,256 @@ +// +// handler_alloc_helpers.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_HANDLER_ALLOC_HELPERS_HPP +#define ASIO_DETAIL_HANDLER_ALLOC_HELPERS_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/handler_alloc_hook.hpp" +#include "asio/detail/noncopyable.hpp" + +// Calls to asio_handler_allocate and asio_handler_deallocate must be made from +// a namespace that does not contain any overloads of these functions. The +// asio_handler_alloc_helpers namespace is defined here for that purpose. +namespace asio_handler_alloc_helpers { + +template +inline void* allocate(std::size_t s, Handler* h) +{ +#if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564)) + return ::operator new(s); +#else + using namespace asio; + return asio_handler_allocate(s, h); +#endif +} + +template +inline void deallocate(void* p, std::size_t s, Handler* h) +{ +#if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564)) + ::operator delete(p); +#else + using namespace asio; + asio_handler_deallocate(p, s, h); +#endif +} + +} // namespace asio_handler_alloc_helpers + +namespace asio { +namespace detail { + +// Traits for handler allocation. +template +struct handler_alloc_traits +{ + typedef Handler handler_type; + typedef Object value_type; + typedef Object* pointer_type; + BOOST_STATIC_CONSTANT(std::size_t, value_size = sizeof(Object)); +}; + +template +class handler_ptr; + +// Helper class to provide RAII on uninitialised handler memory. +template +class raw_handler_ptr + : private noncopyable +{ +public: + typedef typename Alloc_Traits::handler_type handler_type; + typedef typename Alloc_Traits::value_type value_type; + typedef typename Alloc_Traits::pointer_type pointer_type; + BOOST_STATIC_CONSTANT(std::size_t, value_size = Alloc_Traits::value_size); + + // Constructor allocates the memory. + raw_handler_ptr(handler_type& handler) + : handler_(handler), + pointer_(static_cast( + asio_handler_alloc_helpers::allocate(value_size, &handler_))) + { + } + + // Destructor automatically deallocates memory, unless it has been stolen by + // a handler_ptr object. + ~raw_handler_ptr() + { + if (pointer_) + asio_handler_alloc_helpers::deallocate( + pointer_, value_size, &handler_); + } + +private: + friend class handler_ptr; + handler_type& handler_; + pointer_type pointer_; +}; + +// Helper class to provide RAII on uninitialised handler memory. +template +class handler_ptr + : private noncopyable +{ +public: + typedef typename Alloc_Traits::handler_type handler_type; + typedef typename Alloc_Traits::value_type value_type; + typedef typename Alloc_Traits::pointer_type pointer_type; + BOOST_STATIC_CONSTANT(std::size_t, value_size = Alloc_Traits::value_size); + typedef raw_handler_ptr raw_ptr_type; + + // Take ownership of existing memory. + handler_ptr(handler_type& handler, pointer_type pointer) + : handler_(handler), + pointer_(pointer) + { + } + + // Construct object in raw memory and take ownership if construction succeeds. + handler_ptr(raw_ptr_type& raw_ptr) + : handler_(raw_ptr.handler_), + pointer_(new (raw_ptr.pointer_) value_type) + { + raw_ptr.pointer_ = 0; + } + + // Construct object in raw memory and take ownership if construction succeeds. + template + handler_ptr(raw_ptr_type& raw_ptr, Arg1& a1) + : handler_(raw_ptr.handler_), + pointer_(new (raw_ptr.pointer_) value_type(a1)) + { + raw_ptr.pointer_ = 0; + } + + // Construct object in raw memory and take ownership if construction succeeds. + template + handler_ptr(raw_ptr_type& raw_ptr, Arg1& a1, Arg2& a2) + : handler_(raw_ptr.handler_), + pointer_(new (raw_ptr.pointer_) value_type(a1, a2)) + { + raw_ptr.pointer_ = 0; + } + + // Construct object in raw memory and take ownership if construction succeeds. + template + handler_ptr(raw_ptr_type& raw_ptr, Arg1& a1, Arg2& a2, Arg3& a3) + : handler_(raw_ptr.handler_), + pointer_(new (raw_ptr.pointer_) value_type(a1, a2, a3)) + { + raw_ptr.pointer_ = 0; + } + + // Construct object in raw memory and take ownership if construction succeeds. + template + handler_ptr(raw_ptr_type& raw_ptr, Arg1& a1, Arg2& a2, Arg3& a3, Arg4& a4) + : handler_(raw_ptr.handler_), + pointer_(new (raw_ptr.pointer_) value_type(a1, a2, a3, a4)) + { + raw_ptr.pointer_ = 0; + } + + // Construct object in raw memory and take ownership if construction succeeds. + template + handler_ptr(raw_ptr_type& raw_ptr, Arg1& a1, Arg2& a2, Arg3& a3, Arg4& a4, + Arg5& a5) + : handler_(raw_ptr.handler_), + pointer_(new (raw_ptr.pointer_) value_type(a1, a2, a3, a4, a5)) + { + raw_ptr.pointer_ = 0; + } + + // Construct object in raw memory and take ownership if construction succeeds. + template + handler_ptr(raw_ptr_type& raw_ptr, Arg1& a1, Arg2& a2, Arg3& a3, Arg4& a4, + Arg5& a5, Arg6& a6) + : handler_(raw_ptr.handler_), + pointer_(new (raw_ptr.pointer_) value_type(a1, a2, a3, a4, a5, a6)) + { + raw_ptr.pointer_ = 0; + } + + // Construct object in raw memory and take ownership if construction succeeds. + template + handler_ptr(raw_ptr_type& raw_ptr, Arg1& a1, Arg2& a2, Arg3& a3, Arg4& a4, + Arg5& a5, Arg6& a6, Arg7& a7) + : handler_(raw_ptr.handler_), + pointer_(new (raw_ptr.pointer_) value_type(a1, a2, a3, a4, a5, a6, a7)) + { + raw_ptr.pointer_ = 0; + } + + // Construct object in raw memory and take ownership if construction succeeds. + template + handler_ptr(raw_ptr_type& raw_ptr, Arg1& a1, Arg2& a2, Arg3& a3, Arg4& a4, + Arg5& a5, Arg6& a6, Arg7& a7, Arg8& a8) + : handler_(raw_ptr.handler_), + pointer_(new (raw_ptr.pointer_) value_type( + a1, a2, a3, a4, a5, a6, a7, a8)) + { + raw_ptr.pointer_ = 0; + } + + // Destructor automatically deallocates memory, unless it has been released. + ~handler_ptr() + { + reset(); + } + + // Get the memory. + pointer_type get() const + { + return pointer_; + } + + // Release ownership of the memory. + pointer_type release() + { + pointer_type tmp = pointer_; + pointer_ = 0; + return tmp; + } + + // Explicitly destroy and deallocate the memory. + void reset() + { + if (pointer_) + { + pointer_->value_type::~value_type(); + asio_handler_alloc_helpers::deallocate( + pointer_, value_size, &handler_); + pointer_ = 0; + } + } + +private: + handler_type& handler_; + pointer_type pointer_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_HANDLER_ALLOC_HELPERS_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/detail/handler_invoke_helpers.hpp b/encryption/libtorrent/include/libtorrent/asio/detail/handler_invoke_helpers.hpp new file mode 100644 index 000000000..b260c426c --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/detail/handler_invoke_helpers.hpp @@ -0,0 +1,47 @@ +// +// handler_invoke_helpers.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_HANDLER_INVOKE_HELPERS_HPP +#define ASIO_DETAIL_HANDLER_INVOKE_HELPERS_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/handler_invoke_hook.hpp" + +// Calls to asio_handler_invoke must be made from a namespace that does not +// contain overloads of this function. The asio_handler_invoke_helpers +// namespace is defined here for that purpose. +namespace asio_handler_invoke_helpers { + +template +inline void invoke(const Function& function, Context* context) +{ +#if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564)) + Function tmp(function); + tmp(); +#else + using namespace asio; + asio_handler_invoke(function, context); +#endif +} + +} // namespace asio_handler_invoke_helpers + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_HANDLER_INVOKE_HELPERS_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/detail/hash_map.hpp b/encryption/libtorrent/include/libtorrent/asio/detail/hash_map.hpp new file mode 100644 index 000000000..05cebdf58 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/detail/hash_map.hpp @@ -0,0 +1,209 @@ +// +// hash_map.hpp +// ~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_HASH_MAP_HPP +#define ASIO_DETAIL_HASH_MAP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/socket_types.hpp" + +namespace asio { +namespace detail { + +template +inline std::size_t calculate_hash_value(const T& t) +{ + return boost::hash_value(t); +} + +#if defined(_WIN64) +inline std::size_t calculate_hash_value(SOCKET s) +{ + return static_cast(s); +} +#endif // defined(_WIN64) + +template +class hash_map + : private noncopyable +{ +public: + // The type of a value in the map. + typedef std::pair value_type; + + // The type of a non-const iterator over the hash map. + typedef typename std::list::iterator iterator; + + // The type of a const iterator over the hash map. + typedef typename std::list::const_iterator const_iterator; + + // Constructor. + hash_map() + { + // Initialise all buckets to empty. + for (size_t i = 0; i < num_buckets; ++i) + buckets_[i].first = buckets_[i].last = values_.end(); + } + + // Get an iterator for the beginning of the map. + iterator begin() + { + return values_.begin(); + } + + // Get an iterator for the beginning of the map. + const_iterator begin() const + { + return values_.begin(); + } + + // Get an iterator for the end of the map. + iterator end() + { + return values_.end(); + } + + // Get an iterator for the end of the map. + const_iterator end() const + { + return values_.end(); + } + + // Check whether the map is empty. + bool empty() const + { + return values_.empty(); + } + + // Find an entry in the map. + iterator find(const K& k) + { + size_t bucket = calculate_hash_value(k) % num_buckets; + iterator it = buckets_[bucket].first; + if (it == values_.end()) + return values_.end(); + iterator end = buckets_[bucket].last; + ++end; + while (it != end) + { + if (it->first == k) + return it; + ++it; + } + return values_.end(); + } + + // Find an entry in the map. + const_iterator find(const K& k) const + { + size_t bucket = calculate_hash_value(k) % num_buckets; + const_iterator it = buckets_[bucket].first; + if (it == values_.end()) + return it; + const_iterator end = buckets_[bucket].last; + ++end; + while (it != end) + { + if (it->first == k) + return it; + ++it; + } + return values_.end(); + } + + // Insert a new entry into the map. + std::pair insert(const value_type& v) + { + size_t bucket = calculate_hash_value(v.first) % num_buckets; + iterator it = buckets_[bucket].first; + if (it == values_.end()) + { + buckets_[bucket].first = buckets_[bucket].last = + values_.insert(values_.end(), v); + return std::pair(buckets_[bucket].last, true); + } + iterator end = buckets_[bucket].last; + ++end; + while (it != end) + { + if (it->first == v.first) + return std::pair(it, false); + ++it; + } + buckets_[bucket].last = values_.insert(end, v); + return std::pair(buckets_[bucket].last, true); + } + + // Erase an entry from the map. + void erase(iterator it) + { + assert(it != values_.end()); + + size_t bucket = calculate_hash_value(it->first) % num_buckets; + bool is_first = (it == buckets_[bucket].first); + bool is_last = (it == buckets_[bucket].last); + if (is_first && is_last) + buckets_[bucket].first = buckets_[bucket].last = values_.end(); + else if (is_first) + ++buckets_[bucket].first; + else if (is_last) + --buckets_[bucket].last; + + values_.erase(it); + } + + // Remove all entries from the map. + void clear() + { + // Clear the values. + values_.clear(); + + // Initialise all buckets to empty. + for (size_t i = 0; i < num_buckets; ++i) + buckets_[i].first = buckets_[i].last = values_.end(); + } + +private: + // The list of all values in the hash map. + std::list values_; + + // The type for a bucket in the hash table. + struct bucket_type + { + iterator first; + iterator last; + }; + + // The number of buckets in the hash. + enum { num_buckets = 1021 }; + + // The buckets in the hash. + bucket_type buckets_[num_buckets]; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_HASH_MAP_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/detail/io_control.hpp b/encryption/libtorrent/include/libtorrent/asio/detail/io_control.hpp new file mode 100644 index 000000000..feb02d9d5 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/detail/io_control.hpp @@ -0,0 +1,137 @@ +// +// io_control.hpp +// ~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IO_CONTROL_HPP +#define ASIO_DETAIL_IO_CONTROL_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/detail/socket_types.hpp" + +namespace asio { +namespace detail { +namespace io_control { + +// IO control command for non-blocking I/O. +class non_blocking_io +{ +public: + // Default constructor. + non_blocking_io() + : value_(0) + { + } + + // Construct with a specific command value. + non_blocking_io(bool value) + : value_(value ? 1 : 0) + { + } + + // Get the name of the IO control command. + int name() const + { + return FIONBIO; + } + + // Set the value of the I/O control command. + void set(bool value) + { + value_ = value ? 1 : 0; + } + + // Get the current value of the I/O control command. + bool get() const + { + return value_ != 0; + } + + // Get the address of the command data. + detail::ioctl_arg_type* data() + { + return &value_; + } + + // Get the address of the command data. + const detail::ioctl_arg_type* data() const + { + return &value_; + } + +private: + detail::ioctl_arg_type value_; +}; + +// I/O control command for getting number of bytes available. +class bytes_readable +{ +public: + // Default constructor. + bytes_readable() + : value_(0) + { + } + + // Construct with a specific command value. + bytes_readable(std::size_t value) + : value_(static_cast(value)) + { + } + + // Get the name of the IO control command. + int name() const + { + return FIONREAD; + } + + // Set the value of the I/O control command. + void set(std::size_t value) + { + value_ = static_cast(value); + } + + // Get the current value of the I/O control command. + std::size_t get() const + { + return static_cast(value_); + } + + // Get the address of the command data. + detail::ioctl_arg_type* data() + { + return &value_; + } + + // Get the address of the command data. + const detail::ioctl_arg_type* data() const + { + return &value_; + } + +private: + detail::ioctl_arg_type value_; +}; + +} // namespace io_control +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_IO_CONTROL_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/detail/kqueue_reactor.hpp b/encryption/libtorrent/include/libtorrent/asio/detail/kqueue_reactor.hpp new file mode 100644 index 000000000..6628803af --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/detail/kqueue_reactor.hpp @@ -0,0 +1,620 @@ +// +// kqueue_reactor.hpp +// ~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2005 Stefan Arentz (stefan at soze dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_KQUEUE_REACTOR_HPP +#define ASIO_DETAIL_KQUEUE_REACTOR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/kqueue_reactor_fwd.hpp" + +#if defined(ASIO_HAS_KQUEUE) + +#include "asio/detail/push_options.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/error.hpp" +#include "asio/io_service.hpp" +#include "asio/system_error.hpp" +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/mutex.hpp" +#include "asio/detail/task_io_service.hpp" +#include "asio/detail/thread.hpp" +#include "asio/detail/reactor_op_queue.hpp" +#include "asio/detail/select_interrupter.hpp" +#include "asio/detail/service_base.hpp" +#include "asio/detail/signal_blocker.hpp" +#include "asio/detail/socket_types.hpp" +#include "asio/detail/timer_queue.hpp" + +// Older versions of Mac OS X may not define EV_OOBAND. +#if !defined(EV_OOBAND) +# define EV_OOBAND EV_FLAG1 +#endif // !defined(EV_OOBAND) + +namespace asio { +namespace detail { + +template +class kqueue_reactor + : public asio::detail::service_base > +{ +public: + // Constructor. + kqueue_reactor(asio::io_service& io_service) + : asio::detail::service_base< + kqueue_reactor >(io_service), + mutex_(), + kqueue_fd_(do_kqueue_create()), + wait_in_progress_(false), + interrupter_(), + read_op_queue_(), + write_op_queue_(), + except_op_queue_(), + pending_cancellations_(), + stop_thread_(false), + thread_(0), + shutdown_(false) + { + // Start the reactor's internal thread only if needed. + if (Own_Thread) + { + asio::detail::signal_blocker sb; + thread_ = new asio::detail::thread( + bind_handler(&kqueue_reactor::call_run_thread, this)); + } + + // Add the interrupter's descriptor to the kqueue. + struct kevent event; + EV_SET(&event, interrupter_.read_descriptor(), + EVFILT_READ, EV_ADD, 0, 0, 0); + ::kevent(kqueue_fd_, &event, 1, 0, 0, 0); + } + + // Destructor. + ~kqueue_reactor() + { + shutdown_service(); + close(kqueue_fd_); + } + + // Destroy all user-defined handler objects owned by the service. + void shutdown_service() + { + asio::detail::mutex::scoped_lock lock(mutex_); + shutdown_ = true; + stop_thread_ = true; + lock.unlock(); + + if (thread_) + { + interrupter_.interrupt(); + thread_->join(); + delete thread_; + thread_ = 0; + } + + read_op_queue_.destroy_operations(); + write_op_queue_.destroy_operations(); + except_op_queue_.destroy_operations(); + + for (std::size_t i = 0; i < timer_queues_.size(); ++i) + timer_queues_[i]->destroy_timers(); + timer_queues_.clear(); + } + + // Register a socket with the reactor. Returns 0 on success, system error + // code on failure. + int register_descriptor(socket_type) + { + return 0; + } + + // Start a new read operation. The handler object will be invoked when the + // given descriptor is ready to be read, or an error has occurred. + template + void start_read_op(socket_type descriptor, Handler handler) + { + asio::detail::mutex::scoped_lock lock(mutex_); + + if (shutdown_) + return; + + if (!read_op_queue_.has_operation(descriptor)) + if (handler(asio::error_code())) + return; + + if (read_op_queue_.enqueue_operation(descriptor, handler)) + { + struct kevent event; + EV_SET(&event, descriptor, EVFILT_READ, EV_ADD, 0, 0, 0); + if (::kevent(kqueue_fd_, &event, 1, 0, 0, 0) == -1) + { + asio::error_code ec(errno, asio::native_ecat); + read_op_queue_.dispatch_all_operations(descriptor, ec); + } + } + } + + // Start a new write operation. The handler object will be invoked when the + // given descriptor is ready to be written, or an error has occurred. + template + void start_write_op(socket_type descriptor, Handler handler) + { + asio::detail::mutex::scoped_lock lock(mutex_); + + if (shutdown_) + return; + + if (!write_op_queue_.has_operation(descriptor)) + if (handler(asio::error_code())) + return; + + if (write_op_queue_.enqueue_operation(descriptor, handler)) + { + struct kevent event; + EV_SET(&event, descriptor, EVFILT_WRITE, EV_ADD, 0, 0, 0); + if (::kevent(kqueue_fd_, &event, 1, 0, 0, 0) == -1) + { + asio::error_code ec(errno, asio::native_ecat); + write_op_queue_.dispatch_all_operations(descriptor, ec); + } + } + } + + // Start a new exception operation. The handler object will be invoked when + // the given descriptor has exception information, or an error has occurred. + template + void start_except_op(socket_type descriptor, Handler handler) + { + asio::detail::mutex::scoped_lock lock(mutex_); + + if (shutdown_) + return; + + if (except_op_queue_.enqueue_operation(descriptor, handler)) + { + struct kevent event; + if (read_op_queue_.has_operation(descriptor)) + EV_SET(&event, descriptor, EVFILT_READ, EV_ADD, 0, 0, 0); + else + EV_SET(&event, descriptor, EVFILT_READ, EV_ADD, EV_OOBAND, 0, 0); + if (::kevent(kqueue_fd_, &event, 1, 0, 0, 0) == -1) + { + asio::error_code ec(errno, asio::native_ecat); + except_op_queue_.dispatch_all_operations(descriptor, ec); + } + } + } + + // Start new write and exception operations. The handler object will be + // invoked when the given descriptor is ready for writing or has exception + // information available, or an error has occurred. + template + void start_write_and_except_ops(socket_type descriptor, Handler handler) + { + asio::detail::mutex::scoped_lock lock(mutex_); + + if (shutdown_) + return; + + if (write_op_queue_.enqueue_operation(descriptor, handler)) + { + struct kevent event; + EV_SET(&event, descriptor, EVFILT_WRITE, EV_ADD, 0, 0, 0); + if (::kevent(kqueue_fd_, &event, 1, 0, 0, 0) == -1) + { + asio::error_code ec(errno, asio::native_ecat); + write_op_queue_.dispatch_all_operations(descriptor, ec); + } + } + + if (except_op_queue_.enqueue_operation(descriptor, handler)) + { + struct kevent event; + if (read_op_queue_.has_operation(descriptor)) + EV_SET(&event, descriptor, EVFILT_READ, EV_ADD, 0, 0, 0); + else + EV_SET(&event, descriptor, EVFILT_READ, EV_ADD, EV_OOBAND, 0, 0); + if (::kevent(kqueue_fd_, &event, 1, 0, 0, 0) == -1) + { + asio::error_code ec(errno, asio::native_ecat); + except_op_queue_.dispatch_all_operations(descriptor, ec); + write_op_queue_.dispatch_all_operations(descriptor, ec); + } + } + } + + // Cancel all operations associated with the given descriptor. The + // handlers associated with the descriptor will be invoked with the + // operation_aborted error. + void cancel_ops(socket_type descriptor) + { + asio::detail::mutex::scoped_lock lock(mutex_); + cancel_ops_unlocked(descriptor); + } + + // Enqueue cancellation of all operations associated with the given + // descriptor. The handlers associated with the descriptor will be invoked + // with the operation_aborted error. This function does not acquire the + // kqueue_reactor's mutex, and so should only be used from within a reactor + // handler. + void enqueue_cancel_ops_unlocked(socket_type descriptor) + { + pending_cancellations_.push_back(descriptor); + } + + // Cancel any operations that are running against the descriptor and remove + // its registration from the reactor. + void close_descriptor(socket_type descriptor) + { + asio::detail::mutex::scoped_lock lock(mutex_); + + // Remove the descriptor from kqueue. + struct kevent event[2]; + EV_SET(&event[0], descriptor, EVFILT_READ, EV_DELETE, 0, 0, 0); + EV_SET(&event[1], descriptor, EVFILT_WRITE, EV_DELETE, 0, 0, 0); + ::kevent(kqueue_fd_, event, 2, 0, 0, 0); + + // Cancel any outstanding operations associated with the descriptor. + cancel_ops_unlocked(descriptor); + } + + // Add a new timer queue to the reactor. + template + void add_timer_queue(timer_queue& timer_queue) + { + asio::detail::mutex::scoped_lock lock(mutex_); + timer_queues_.push_back(&timer_queue); + } + + // Remove a timer queue from the reactor. + template + void remove_timer_queue(timer_queue& timer_queue) + { + asio::detail::mutex::scoped_lock lock(mutex_); + for (std::size_t i = 0; i < timer_queues_.size(); ++i) + { + if (timer_queues_[i] == &timer_queue) + { + timer_queues_.erase(timer_queues_.begin() + i); + return; + } + } + } + + // Schedule a timer in the given timer queue to expire at the specified + // absolute time. The handler object will be invoked when the timer expires. + template + void schedule_timer(timer_queue& timer_queue, + const typename Time_Traits::time_type& time, Handler handler, void* token) + { + asio::detail::mutex::scoped_lock lock(mutex_); + if (!shutdown_) + if (timer_queue.enqueue_timer(time, handler, token)) + interrupter_.interrupt(); + } + + // Cancel the timer associated with the given token. Returns the number of + // handlers that have been posted or dispatched. + template + std::size_t cancel_timer(timer_queue& timer_queue, void* token) + { + asio::detail::mutex::scoped_lock lock(mutex_); + return timer_queue.cancel_timer(token); + } + +private: + friend class task_io_service >; + + // Run the kqueue loop. + void run(bool block) + { + asio::detail::mutex::scoped_lock lock(mutex_); + + // Dispatch any operation cancellations that were made while the select + // loop was not running. + read_op_queue_.dispatch_cancellations(); + write_op_queue_.dispatch_cancellations(); + except_op_queue_.dispatch_cancellations(); + + // Check if the thread is supposed to stop. + if (stop_thread_) + { + // Clean up operations. We must not hold the lock since the operations may + // make calls back into this reactor. + lock.unlock(); + read_op_queue_.cleanup_operations(); + write_op_queue_.cleanup_operations(); + except_op_queue_.cleanup_operations(); + return; + } + + // We can return immediately if there's no work to do and the reactor is + // not supposed to block. + if (!block && read_op_queue_.empty() && write_op_queue_.empty() + && except_op_queue_.empty() && all_timer_queues_are_empty()) + { + // Clean up operations. We must not hold the lock since the operations may + // make calls back into this reactor. + lock.unlock(); + read_op_queue_.cleanup_operations(); + write_op_queue_.cleanup_operations(); + except_op_queue_.cleanup_operations(); + return; + } + + // Determine how long to block while waiting for events. + timespec timeout_buf = { 0, 0 }; + timespec* timeout = block ? get_timeout(timeout_buf) : &timeout_buf; + + wait_in_progress_ = true; + lock.unlock(); + + // Block on the kqueue descriptor. + struct kevent events[128]; + int num_events = kevent(kqueue_fd_, 0, 0, events, 128, timeout); + + lock.lock(); + wait_in_progress_ = false; + + // Block signals while dispatching operations. + asio::detail::signal_blocker sb; + + // Dispatch the waiting events. + for (int i = 0; i < num_events; ++i) + { + int descriptor = events[i].ident; + if (descriptor == interrupter_.read_descriptor()) + { + interrupter_.reset(); + } + else if (events[i].filter == EVFILT_READ) + { + // Dispatch operations associated with the descriptor. + bool more_reads = false; + bool more_except = false; + if (events[i].flags & EV_ERROR) + { + asio::error_code error( + events[i].data, asio::native_ecat); + except_op_queue_.dispatch_all_operations(descriptor, error); + read_op_queue_.dispatch_all_operations(descriptor, error); + } + else if (events[i].flags & EV_OOBAND) + { + asio::error_code error; + more_except = except_op_queue_.dispatch_operation(descriptor, error); + if (events[i].data > 0) + more_reads = read_op_queue_.dispatch_operation(descriptor, error); + else + more_reads = read_op_queue_.has_operation(descriptor); + } + else + { + asio::error_code error; + more_reads = read_op_queue_.dispatch_operation(descriptor, error); + more_except = except_op_queue_.has_operation(descriptor); + } + + // Update the descriptor in the kqueue. + struct kevent event; + if (more_reads) + EV_SET(&event, descriptor, EVFILT_READ, EV_ADD, 0, 0, 0); + else if (more_except) + EV_SET(&event, descriptor, EVFILT_READ, EV_ADD, EV_OOBAND, 0, 0); + else + EV_SET(&event, descriptor, EVFILT_READ, EV_DELETE, 0, 0, 0); + if (::kevent(kqueue_fd_, &event, 1, 0, 0, 0) == -1) + { + asio::error_code error(errno, asio::native_ecat); + except_op_queue_.dispatch_all_operations(descriptor, error); + read_op_queue_.dispatch_all_operations(descriptor, error); + } + } + else if (events[i].filter == EVFILT_WRITE) + { + // Dispatch operations associated with the descriptor. + bool more_writes = false; + if (events[i].flags & EV_ERROR) + { + asio::error_code error( + events[i].data, asio::native_ecat); + write_op_queue_.dispatch_all_operations(descriptor, error); + } + else + { + asio::error_code error; + more_writes = write_op_queue_.dispatch_operation(descriptor, error); + } + + // Update the descriptor in the kqueue. + struct kevent event; + if (more_writes) + EV_SET(&event, descriptor, EVFILT_WRITE, EV_ADD, 0, 0, 0); + else + EV_SET(&event, descriptor, EVFILT_WRITE, EV_DELETE, 0, 0, 0); + if (::kevent(kqueue_fd_, &event, 1, 0, 0, 0) == -1) + { + asio::error_code error(errno, asio::native_ecat); + write_op_queue_.dispatch_all_operations(descriptor, error); + } + } + } + + read_op_queue_.dispatch_cancellations(); + write_op_queue_.dispatch_cancellations(); + except_op_queue_.dispatch_cancellations(); + for (std::size_t i = 0; i < timer_queues_.size(); ++i) + timer_queues_[i]->dispatch_timers(); + + // Issue any pending cancellations. + for (std::size_t i = 0; i < pending_cancellations_.size(); ++i) + cancel_ops_unlocked(pending_cancellations_[i]); + pending_cancellations_.clear(); + + // Clean up operations. We must not hold the lock since the operations may + // make calls back into this reactor. + lock.unlock(); + read_op_queue_.cleanup_operations(); + write_op_queue_.cleanup_operations(); + except_op_queue_.cleanup_operations(); + } + + // Run the select loop in the thread. + void run_thread() + { + asio::detail::mutex::scoped_lock lock(mutex_); + while (!stop_thread_) + { + lock.unlock(); + run(true); + lock.lock(); + } + } + + // Entry point for the select loop thread. + static void call_run_thread(kqueue_reactor* reactor) + { + reactor->run_thread(); + } + + // Interrupt the select loop. + void interrupt() + { + interrupter_.interrupt(); + } + + // Create the kqueue file descriptor. Throws an exception if the descriptor + // cannot be created. + static int do_kqueue_create() + { + int fd = kqueue(); + if (fd == -1) + { + boost::throw_exception(asio::system_error( + asio::error_code(errno, asio::native_ecat), + "kqueue")); + } + return fd; + } + + // Check if all timer queues are empty. + bool all_timer_queues_are_empty() const + { + for (std::size_t i = 0; i < timer_queues_.size(); ++i) + if (!timer_queues_[i]->empty()) + return false; + return true; + } + + // Get the timeout value for the kevent call. + timespec* get_timeout(timespec& ts) + { + if (all_timer_queues_are_empty()) + return 0; + + // By default we will wait no longer than 5 minutes. This will ensure that + // any changes to the system clock are detected after no longer than this. + boost::posix_time::time_duration minimum_wait_duration + = boost::posix_time::minutes(5); + + for (std::size_t i = 0; i < timer_queues_.size(); ++i) + { + boost::posix_time::time_duration wait_duration + = timer_queues_[i]->wait_duration(); + if (wait_duration < minimum_wait_duration) + minimum_wait_duration = wait_duration; + } + + if (minimum_wait_duration > boost::posix_time::time_duration()) + { + ts.tv_sec = minimum_wait_duration.total_seconds(); + ts.tv_nsec = minimum_wait_duration.total_nanoseconds() % 1000000000; + } + else + { + ts.tv_sec = 0; + ts.tv_nsec = 0; + } + + return &ts; + } + + // Cancel all operations associated with the given descriptor. The do_cancel + // function of the handler objects will be invoked. This function does not + // acquire the kqueue_reactor's mutex. + void cancel_ops_unlocked(socket_type descriptor) + { + bool interrupt = read_op_queue_.cancel_operations(descriptor); + interrupt = write_op_queue_.cancel_operations(descriptor) || interrupt; + interrupt = except_op_queue_.cancel_operations(descriptor) || interrupt; + if (interrupt) + interrupter_.interrupt(); + } + + // Mutex to protect access to internal data. + asio::detail::mutex mutex_; + + // The kqueue file descriptor. + int kqueue_fd_; + + // Whether the kqueue wait call is currently in progress + bool wait_in_progress_; + + // The interrupter is used to break a blocking kevent call. + select_interrupter interrupter_; + + // The queue of read operations. + reactor_op_queue read_op_queue_; + + // The queue of write operations. + reactor_op_queue write_op_queue_; + + // The queue of except operations. + reactor_op_queue except_op_queue_; + + // The timer queues. + std::vector timer_queues_; + + // The descriptors that are pending cancellation. + std::vector pending_cancellations_; + + // Does the reactor loop thread need to stop. + bool stop_thread_; + + // The thread that is running the reactor loop. + asio::detail::thread* thread_; + + // Whether the service has been shut down. + bool shutdown_; +}; + +} // namespace detail +} // namespace asio + +#endif // defined(ASIO_HAS_KQUEUE) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_KQUEUE_REACTOR_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/detail/kqueue_reactor_fwd.hpp b/encryption/libtorrent/include/libtorrent/asio/detail/kqueue_reactor_fwd.hpp new file mode 100644 index 000000000..5171f4215 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/detail/kqueue_reactor_fwd.hpp @@ -0,0 +1,41 @@ +// +// kqueue_reactor_fwd.hpp +// ~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2005 Stefan Arentz (stefan at soze dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_KQUEUE_REACTOR_FWD_HPP +#define ASIO_DETAIL_KQUEUE_REACTOR_FWD_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#if !defined(ASIO_DISABLE_KQUEUE) +#if defined(__MACH__) && defined(__APPLE__) + +// Define this to indicate that epoll is supported on the target platform. +#define ASIO_HAS_KQUEUE 1 + +namespace asio { +namespace detail { + +template +class kqueue_reactor; + +} // namespace detail +} // namespace asio + +#endif // defined(__MACH__) && defined(__APPLE__) +#endif // !defined(ASIO_DISABLE_KQUEUE) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_KQUEUE_REACTOR_FWD_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/detail/local_free_on_block_exit.hpp b/encryption/libtorrent/include/libtorrent/asio/detail/local_free_on_block_exit.hpp new file mode 100644 index 000000000..8e1f6088d --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/detail/local_free_on_block_exit.hpp @@ -0,0 +1,59 @@ +// +// local_free_on_block_exit.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_LOCAL_FREE_ON_BLOCK_EXIT_HPP +#define ASIO_DETAIL_LOCAL_FREE_ON_BLOCK_EXIT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include "asio/detail/pop_options.hpp" + +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/socket_types.hpp" + +namespace asio { +namespace detail { + +class local_free_on_block_exit + : private noncopyable +{ +public: + // Constructor blocks all signals for the calling thread. + explicit local_free_on_block_exit(void* p) + : p_(p) + { + } + + // Destructor restores the previous signal mask. + ~local_free_on_block_exit() + { + ::LocalFree(p_); + } + +private: + void* p_; +}; + +} // namespace detail +} // namespace asio + +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_LOCAL_FREE_ON_BLOCK_EXIT_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/detail/mutex.hpp b/encryption/libtorrent/include/libtorrent/asio/detail/mutex.hpp new file mode 100644 index 000000000..87d32ba3c --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/detail/mutex.hpp @@ -0,0 +1,50 @@ +// +// mutex.hpp +// ~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_MUTEX_HPP +#define ASIO_DETAIL_MUTEX_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include "asio/detail/pop_options.hpp" + +#if !defined(BOOST_HAS_THREADS) +# include "asio/detail/null_mutex.hpp" +#elif defined(BOOST_WINDOWS) +# include "asio/detail/win_mutex.hpp" +#elif defined(BOOST_HAS_PTHREADS) +# include "asio/detail/posix_mutex.hpp" +#else +# error Only Windows and POSIX are supported! +#endif + +namespace asio { +namespace detail { + +#if !defined(BOOST_HAS_THREADS) +typedef null_mutex mutex; +#elif defined(BOOST_WINDOWS) +typedef win_mutex mutex; +#elif defined(BOOST_HAS_PTHREADS) +typedef posix_mutex mutex; +#endif + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_MUTEX_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/detail/noncopyable.hpp b/encryption/libtorrent/include/libtorrent/asio/detail/noncopyable.hpp new file mode 100644 index 000000000..8e9d54683 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/detail/noncopyable.hpp @@ -0,0 +1,55 @@ +// +// noncopyable.hpp +// ~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_NONCOPYABLE_HPP +#define ASIO_DETAIL_NONCOPYABLE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include +#include "asio/detail/pop_options.hpp" + +namespace asio { +namespace detail { + +#if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564)) +// Redefine the noncopyable class for Borland C++ since that compiler does not +// apply the empty base optimisation unless the base class contains a dummy +// char data member. +class noncopyable +{ +protected: + noncopyable() {} + ~noncopyable() {} +private: + noncopyable(const noncopyable&); + const noncopyable& operator=(const noncopyable&); + char dummy_; +}; +#else +using boost::noncopyable; +#endif + +} // namespace detail + +using asio::detail::noncopyable; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_NONCOPYABLE_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/detail/null_event.hpp b/encryption/libtorrent/include/libtorrent/asio/detail/null_event.hpp new file mode 100644 index 000000000..df522ce0f --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/detail/null_event.hpp @@ -0,0 +1,68 @@ +// +// null_event.hpp +// ~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_NULL_EVENT_HPP +#define ASIO_DETAIL_NULL_EVENT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include "asio/detail/pop_options.hpp" + +#if !defined(BOOST_HAS_THREADS) + +#include "asio/detail/noncopyable.hpp" + +namespace asio { +namespace detail { + +class null_event + : private noncopyable +{ +public: + // Constructor. + null_event() + { + } + + // Destructor. + ~null_event() + { + } + + // Signal the event. + void signal() + { + } + + // Reset the event. + void clear() + { + } + + // Wait for the event to become signalled. + void wait() + { + } +}; + +} // namespace detail +} // namespace asio + +#endif // !defined(BOOST_HAS_THREADS) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_NULL_EVENT_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/detail/null_mutex.hpp b/encryption/libtorrent/include/libtorrent/asio/detail/null_mutex.hpp new file mode 100644 index 000000000..2f6a72084 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/detail/null_mutex.hpp @@ -0,0 +1,66 @@ +// +// null_mutex.hpp +// ~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_NULL_MUTEX_HPP +#define ASIO_DETAIL_NULL_MUTEX_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include "asio/detail/pop_options.hpp" + +#if !defined(BOOST_HAS_THREADS) + +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/scoped_lock.hpp" + +namespace asio { +namespace detail { + +class null_mutex + : private noncopyable +{ +public: + typedef asio::detail::scoped_lock scoped_lock; + + // Constructor. + null_mutex() + { + } + + // Destructor. + ~null_mutex() + { + } + + // Lock the mutex. + void lock() + { + } + + // Unlock the mutex. + void unlock() + { + } +}; + +} // namespace detail +} // namespace asio + +#endif // !defined(BOOST_HAS_THREADS) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_NULL_MUTEX_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/detail/null_signal_blocker.hpp b/encryption/libtorrent/include/libtorrent/asio/detail/null_signal_blocker.hpp new file mode 100644 index 000000000..c10017d98 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/detail/null_signal_blocker.hpp @@ -0,0 +1,63 @@ +// +// null_signal_blocker.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_NULL_SIGNAL_BLOCKER_HPP +#define ASIO_DETAIL_NULL_SIGNAL_BLOCKER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include "asio/detail/pop_options.hpp" + +#if !defined(BOOST_HAS_THREADS) + +#include "asio/detail/noncopyable.hpp" + +namespace asio { +namespace detail { + +class null_signal_blocker + : private noncopyable +{ +public: + // Constructor blocks all signals for the calling thread. + null_signal_blocker() + { + } + + // Destructor restores the previous signal mask. + ~null_signal_blocker() + { + } + + // Block all signals for the calling thread. + void block() + { + } + + // Restore the previous signal mask. + void unblock() + { + } +}; + +} // namespace detail +} // namespace asio + +#endif // !defined(BOOST_HAS_THREADS) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_NULL_SIGNAL_BLOCKER_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/detail/null_thread.hpp b/encryption/libtorrent/include/libtorrent/asio/detail/null_thread.hpp new file mode 100644 index 000000000..c013a7701 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/detail/null_thread.hpp @@ -0,0 +1,68 @@ +// +// null_thread.hpp +// ~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_NULL_THREAD_HPP +#define ASIO_DETAIL_NULL_THREAD_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include "asio/detail/pop_options.hpp" + +#if !defined(BOOST_HAS_THREADS) + +#include "asio/detail/push_options.hpp" +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/error.hpp" +#include "asio/system_error.hpp" +#include "asio/detail/noncopyable.hpp" + +namespace asio { +namespace detail { + +class null_thread + : private noncopyable +{ +public: + // Constructor. + template + null_thread(Function f) + { + asio::system_error e( + asio::error::operation_not_supported, "thread"); + boost::throw_exception(e); + } + + // Destructor. + ~null_thread() + { + } + + // Wait for the thread to exit. + void join() + { + } +}; + +} // namespace detail +} // namespace asio + +#endif // !defined(BOOST_HAS_THREADS) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_NULL_THREAD_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/detail/null_tss_ptr.hpp b/encryption/libtorrent/include/libtorrent/asio/detail/null_tss_ptr.hpp new file mode 100644 index 000000000..9f3b0ad0c --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/detail/null_tss_ptr.hpp @@ -0,0 +1,70 @@ +// +// null_tss_ptr.hpp +// ~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_NULL_TSS_PTR_HPP +#define ASIO_DETAIL_NULL_TSS_PTR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include "asio/detail/pop_options.hpp" + +#if !defined(BOOST_HAS_THREADS) + +#include "asio/detail/noncopyable.hpp" + +namespace asio { +namespace detail { + +template +class null_tss_ptr + : private noncopyable +{ +public: + // Constructor. + null_tss_ptr() + : value_(0) + { + } + + // Destructor. + ~null_tss_ptr() + { + } + + // Get the value. + operator T*() const + { + return value_; + } + + // Set the value. + void operator=(T* value) + { + value_ = value; + } + +private: + T* value_; +}; + +} // namespace detail +} // namespace asio + +#endif // !defined(BOOST_HAS_THREADS) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_NULL_TSS_PTR_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/detail/old_win_sdk_compat.hpp b/encryption/libtorrent/include/libtorrent/asio/detail/old_win_sdk_compat.hpp new file mode 100644 index 000000000..da78f956d --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/detail/old_win_sdk_compat.hpp @@ -0,0 +1,317 @@ +// +// old_win_sdk_compat.hpp +// ~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_OLD_WIN_SDK_COMPAT_HPP +#define ASIO_DETAIL_OLD_WIN_SDK_COMPAT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include "asio/detail/pop_options.hpp" + +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + +// Guess whether we are building against on old Platform SDK. +#if !defined(IN6ADDR_ANY_INIT) +#define ASIO_HAS_OLD_WIN_SDK 1 +#endif // !defined(IN6ADDR_ANY_INIT) + +#if defined(ASIO_HAS_OLD_WIN_SDK) + +// Emulation of types that are missing from old Platform SDKs. + +namespace asio { +namespace detail { + +enum +{ + sockaddr_storage_maxsize = 128, // Maximum size. + sockaddr_storage_alignsize = (sizeof(__int64)), // Desired alignment. + sockaddr_storage_pad1size = (sockaddr_storage_alignsize - sizeof(short)), + sockaddr_storage_pad2size = (sockaddr_storage_maxsize - + (sizeof(short) + sockaddr_storage_pad1size + sockaddr_storage_alignsize)) +}; + +struct sockaddr_storage_emulation +{ + short ss_family; + char __ss_pad1[sockaddr_storage_pad1size]; + __int64 __ss_align; + char __ss_pad2[sockaddr_storage_pad2size]; +}; + +struct in6_addr_emulation +{ + u_char s6_addr[16]; +}; + +struct sockaddr_in6_emulation +{ + short sin6_family; + u_short sin6_port; + u_long sin6_flowinfo; + in6_addr_emulation sin6_addr; + u_long sin6_scope_id; +}; + +struct ipv6_mreq_emulation +{ + in6_addr_emulation ipv6mr_multiaddr; + unsigned int ipv6mr_interface; +}; + +#if !defined(IN6ADDR_ANY_INIT) +# define IN6ADDR_ANY_INIT { 0 } +#endif + +#if !defined(IN6ADDR_LOOPBACK_INIT) +# define IN6ADDR_LOOPBACK_INIT { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 } +#endif + +struct addrinfo_emulation +{ + int ai_flags; + int ai_family; + int ai_socktype; + int ai_protocol; + size_t ai_addrlen; + char* ai_canonname; + sockaddr* ai_addr; + addrinfo_emulation* ai_next; +}; + +#if !defined(AI_PASSIVE) +# define AI_PASSIVE 0x1 +#endif + +#if !defined(AI_CANONNAME) +# define AI_CANONNAME 0x2 +#endif + +#if !defined(AI_NUMERICHOST) +# define AI_NUMERICHOST 0x4 +#endif + +#if !defined(EAI_AGAIN) +# define EAI_AGAIN WSATRY_AGAIN +#endif + +#if !defined(EAI_BADFLAGS) +# define EAI_BADFLAGS WSAEINVAL +#endif + +#if !defined(EAI_FAIL) +# define EAI_FAIL WSANO_RECOVERY +#endif + +#if !defined(EAI_FAMILY) +# define EAI_FAMILY WSAEAFNOSUPPORT +#endif + +#if !defined(EAI_MEMORY) +# define EAI_MEMORY WSA_NOT_ENOUGH_MEMORY +#endif + +#if !defined(EAI_NODATA) +# define EAI_NODATA WSANO_DATA +#endif + +#if !defined(EAI_NONAME) +# define EAI_NONAME WSAHOST_NOT_FOUND +#endif + +#if !defined(EAI_SERVICE) +# define EAI_SERVICE WSATYPE_NOT_FOUND +#endif + +#if !defined(EAI_SOCKTYPE) +# define EAI_SOCKTYPE WSAESOCKTNOSUPPORT +#endif + +#if !defined(NI_NOFQDN) +# define NI_NOFQDN 0x01 +#endif + +#if !defined(NI_NUMERICHOST) +# define NI_NUMERICHOST 0x02 +#endif + +#if !defined(NI_NAMEREQD) +# define NI_NAMEREQD 0x04 +#endif + +#if !defined(NI_NUMERICSERV) +# define NI_NUMERICSERV 0x08 +#endif + +#if !defined(NI_DGRAM) +# define NI_DGRAM 0x10 +#endif + +#if !defined(IPPROTO_IPV6) +# define IPPROTO_IPV6 41 +#endif + +#if !defined(IPV6_MULTICAST_IF) +# define IPV6_MULTICAST_IF 9 +#endif + +#if !defined(IPV6_MULTICAST_HOPS) +# define IPV6_MULTICAST_HOPS 10 +#endif + +#if !defined(IPV6_MULTICAST_LOOP) +# define IPV6_MULTICAST_LOOP 11 +#endif + +#if !defined(IPV6_JOIN_GROUP) +# define IPV6_JOIN_GROUP 12 +#endif + +#if !defined(IPV6_LEAVE_GROUP) +# define IPV6_LEAVE_GROUP 13 +#endif + +inline int IN6_IS_ADDR_UNSPECIFIED(const in6_addr_emulation* a) +{ + return ((a->s6_addr[0] == 0) + && (a->s6_addr[1] == 0) + && (a->s6_addr[2] == 0) + && (a->s6_addr[3] == 0) + && (a->s6_addr[4] == 0) + && (a->s6_addr[5] == 0) + && (a->s6_addr[6] == 0) + && (a->s6_addr[7] == 0) + && (a->s6_addr[8] == 0) + && (a->s6_addr[9] == 0) + && (a->s6_addr[10] == 0) + && (a->s6_addr[11] == 0) + && (a->s6_addr[12] == 0) + && (a->s6_addr[13] == 0) + && (a->s6_addr[14] == 0) + && (a->s6_addr[15] == 0)); +} + +inline int IN6_IS_ADDR_LOOPBACK(const in6_addr_emulation* a) +{ + return ((a->s6_addr[0] == 0) + && (a->s6_addr[1] == 0) + && (a->s6_addr[2] == 0) + && (a->s6_addr[3] == 0) + && (a->s6_addr[4] == 0) + && (a->s6_addr[5] == 0) + && (a->s6_addr[6] == 0) + && (a->s6_addr[7] == 0) + && (a->s6_addr[8] == 0) + && (a->s6_addr[9] == 0) + && (a->s6_addr[10] == 0) + && (a->s6_addr[11] == 0) + && (a->s6_addr[12] == 0) + && (a->s6_addr[13] == 0) + && (a->s6_addr[14] == 0) + && (a->s6_addr[15] == 1)); +} + +inline int IN6_IS_ADDR_MULTICAST(const in6_addr_emulation* a) +{ + return (a->s6_addr[0] == 0xff); +} + +inline int IN6_IS_ADDR_LINKLOCAL(const in6_addr_emulation* a) +{ + return ((a->s6_addr[0] == 0xfe) && ((a->s6_addr[1] & 0xc0) == 0x80)); +} + +inline int IN6_IS_ADDR_SITELOCAL(const in6_addr_emulation* a) +{ + return ((a->s6_addr[0] == 0xfe) && ((a->s6_addr[1] & 0xc0) == 0xc0)); +} + +inline int IN6_IS_ADDR_V4MAPPED(const in6_addr_emulation* a) +{ + return ((a->s6_addr[0] == 0) + && (a->s6_addr[1] == 0) + && (a->s6_addr[2] == 0) + && (a->s6_addr[3] == 0) + && (a->s6_addr[4] == 0) + && (a->s6_addr[5] == 0) + && (a->s6_addr[6] == 0) + && (a->s6_addr[7] == 0) + && (a->s6_addr[8] == 0) + && (a->s6_addr[9] == 0) + && (a->s6_addr[10] == 0xff) + && (a->s6_addr[11] == 0xff)); +} + +inline int IN6_IS_ADDR_V4COMPAT(const in6_addr_emulation* a) +{ + return ((a->s6_addr[0] == 0) + && (a->s6_addr[1] == 0) + && (a->s6_addr[2] == 0) + && (a->s6_addr[3] == 0) + && (a->s6_addr[4] == 0) + && (a->s6_addr[5] == 0) + && (a->s6_addr[6] == 0) + && (a->s6_addr[7] == 0) + && (a->s6_addr[8] == 0) + && (a->s6_addr[9] == 0) + && (a->s6_addr[10] == 0xff) + && (a->s6_addr[11] == 0xff) + && !((a->s6_addr[12] == 0) + && (a->s6_addr[13] == 0) + && (a->s6_addr[14] == 0) + && ((a->s6_addr[15] == 0) || (a->s6_addr[15] == 1)))); +} + +inline int IN6_IS_ADDR_MC_NODELOCAL(const in6_addr_emulation* a) +{ + return IN6_IS_ADDR_MULTICAST(a) && ((a->s6_addr[1] & 0xf) == 1); +} + +inline int IN6_IS_ADDR_MC_LINKLOCAL(const in6_addr_emulation* a) +{ + return IN6_IS_ADDR_MULTICAST(a) && ((a->s6_addr[1] & 0xf) == 2); +} + +inline int IN6_IS_ADDR_MC_SITELOCAL(const in6_addr_emulation* a) +{ + return IN6_IS_ADDR_MULTICAST(a) && ((a->s6_addr[1] & 0xf) == 5); +} + +inline int IN6_IS_ADDR_MC_ORGLOCAL(const in6_addr_emulation* a) +{ + return IN6_IS_ADDR_MULTICAST(a) && ((a->s6_addr[1] & 0xf) == 8); +} + +inline int IN6_IS_ADDR_MC_GLOBAL(const in6_addr_emulation* a) +{ + return IN6_IS_ADDR_MULTICAST(a) && ((a->s6_addr[1] & 0xf) == 0xe); +} + +} // namespace detail +} // namespace asio + +#endif // defined(ASIO_HAS_OLD_WIN_SDK) + +// Even newer Platform SDKs that support IPv6 may not define IPV6_V6ONLY. +#if !defined(IPV6_V6ONLY) +# define IPV6_V6ONLY 27 +#endif + +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_OLD_WIN_SDK_COMPAT_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/detail/pipe_select_interrupter.hpp b/encryption/libtorrent/include/libtorrent/asio/detail/pipe_select_interrupter.hpp new file mode 100644 index 000000000..e203669cb --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/detail/pipe_select_interrupter.hpp @@ -0,0 +1,104 @@ +// +// pipe_select_interrupter.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_PIPE_SELECT_INTERRUPTER_HPP +#define ASIO_DETAIL_PIPE_SELECT_INTERRUPTER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include "asio/detail/pop_options.hpp" + +#if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) + +#include "asio/detail/push_options.hpp" +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/detail/socket_types.hpp" + +namespace asio { +namespace detail { + +class pipe_select_interrupter +{ +public: + // Constructor. + pipe_select_interrupter() + { + int pipe_fds[2]; + if (pipe(pipe_fds) == 0) + { + read_descriptor_ = pipe_fds[0]; + ::fcntl(read_descriptor_, F_SETFL, O_NONBLOCK); + write_descriptor_ = pipe_fds[1]; + ::fcntl(write_descriptor_, F_SETFL, O_NONBLOCK); + } + } + + // Destructor. + ~pipe_select_interrupter() + { + if (read_descriptor_ != -1) + ::close(read_descriptor_); + if (write_descriptor_ != -1) + ::close(write_descriptor_); + } + + // Interrupt the select call. + void interrupt() + { + char byte = 0; + ::write(write_descriptor_, &byte, 1); + } + + // Reset the select interrupt. Returns true if the call was interrupted. + bool reset() + { + char data[1024]; + int bytes_read = ::read(read_descriptor_, data, sizeof(data)); + bool was_interrupted = (bytes_read > 0); + while (bytes_read == sizeof(data)) + bytes_read = ::read(read_descriptor_, data, sizeof(data)); + return was_interrupted; + } + + // Get the read descriptor to be passed to select. + int read_descriptor() const + { + return read_descriptor_; + } + +private: + // The read end of a connection used to interrupt the select call. This file + // descriptor is passed to select such that when it is time to stop, a single + // byte will be written on the other end of the connection and this + // descriptor will become readable. + int read_descriptor_; + + // The write end of a connection used to interrupt the select call. A single + // byte may be written to this to wake up the select which is waiting for the + // other end to become readable. + int write_descriptor_; +}; + +} // namespace detail +} // namespace asio + +#endif // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_PIPE_SELECT_INTERRUPTER_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/detail/pop_options.hpp b/encryption/libtorrent/include/libtorrent/asio/detail/pop_options.hpp new file mode 100644 index 000000000..c8b0ed93b --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/detail/pop_options.hpp @@ -0,0 +1,88 @@ +// +// pop_options.hpp +// ~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +// No header guard + +#if defined(__COMO__) + +// Comeau C++ + +#elif defined(__DMC__) + +// Digital Mars C++ + +#elif defined(__INTEL_COMPILER) || defined(__ICL) \ + || defined(__ICC) || defined(__ECC) + +// Intel C++ + +#elif defined(__GNUC__) + +// GNU C++ + +# if defined(__MINGW32__) || defined(__CYGWIN__) +# pragma pack (pop) +# endif + +#elif defined(__KCC) + +// Kai C++ + +#elif defined(__sgi) + +// SGI MIPSpro C++ + +#elif defined(__DECCXX) + +// Compaq Tru64 Unix cxx + +#elif defined(__ghs) + +// Greenhills C++ + +#elif defined(__BORLANDC__) + +// Borland C++ + +# pragma option pop +# pragma nopushoptwarn +# pragma nopackwarning + +#elif defined(__MWERKS__) + +// Metrowerks CodeWarrior + +#elif defined(__SUNPRO_CC) + +// Sun Workshop Compiler C++ + +#elif defined(__HP_aCC) + +// HP aCC + +#elif defined(__MRC__) || defined(__SC__) + +// MPW MrCpp or SCpp + +#elif defined(__IBMCPP__) + +// IBM Visual Age + +#elif defined(_MSC_VER) + +// Microsoft Visual C++ +// +// Must remain the last #elif since some other vendors (Metrowerks, for example) +// also #define _MSC_VER + +# pragma warning (pop) +# pragma pack (pop) + +#endif diff --git a/encryption/libtorrent/include/libtorrent/asio/detail/posix_event.hpp b/encryption/libtorrent/include/libtorrent/asio/detail/posix_event.hpp new file mode 100644 index 000000000..408c23bb9 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/detail/posix_event.hpp @@ -0,0 +1,111 @@ +// +// posix_event.hpp +// ~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_POSIX_EVENT_HPP +#define ASIO_DETAIL_POSIX_EVENT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include "asio/detail/pop_options.hpp" + +#if defined(BOOST_HAS_PTHREADS) + +#include "asio/detail/push_options.hpp" +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/system_error.hpp" +#include "asio/detail/noncopyable.hpp" + +namespace asio { +namespace detail { + +class posix_event + : private noncopyable +{ +public: + // Constructor. + posix_event() + : signalled_(false) + { + int error = ::pthread_mutex_init(&mutex_, 0); + if (error != 0) + { + asio::system_error e( + asio::error_code(error, asio::native_ecat), + "event"); + boost::throw_exception(e); + } + + error = ::pthread_cond_init(&cond_, 0); + if (error != 0) + { + ::pthread_mutex_destroy(&mutex_); + asio::system_error e( + asio::error_code(error, asio::native_ecat), + "event"); + boost::throw_exception(e); + } + } + + // Destructor. + ~posix_event() + { + ::pthread_cond_destroy(&cond_); + ::pthread_mutex_destroy(&mutex_); + } + + // Signal the event. + void signal() + { + ::pthread_mutex_lock(&mutex_); // Ignore EINVAL and EDEADLK. + signalled_ = true; + ::pthread_cond_signal(&cond_); // Ignore EINVAL. + ::pthread_mutex_unlock(&mutex_); // Ignore EINVAL and EPERM. + } + + // Reset the event. + void clear() + { + ::pthread_mutex_lock(&mutex_); // Ignore EINVAL and EDEADLK. + signalled_ = false; + ::pthread_mutex_unlock(&mutex_); // Ignore EINVAL and EPERM. + } + + // Wait for the event to become signalled. + void wait() + { + ::pthread_mutex_lock(&mutex_); // Ignore EINVAL and EDEADLK. + while (!signalled_) + ::pthread_cond_wait(&cond_, &mutex_); // Ignore EINVAL. + ::pthread_mutex_unlock(&mutex_); // Ignore EINVAL and EPERM. + } + +private: + ::pthread_mutex_t mutex_; + ::pthread_cond_t cond_; + bool signalled_; +}; + +} // namespace detail +} // namespace asio + +#endif // defined(BOOST_HAS_PTHREADS) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_POSIX_EVENT_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/detail/posix_fd_set_adapter.hpp b/encryption/libtorrent/include/libtorrent/asio/detail/posix_fd_set_adapter.hpp new file mode 100644 index 000000000..ae5bf6642 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/detail/posix_fd_set_adapter.hpp @@ -0,0 +1,72 @@ +// +// posix_fd_set_adapter.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_POSIX_FD_SET_ADAPTER_HPP +#define ASIO_DETAIL_POSIX_FD_SET_ADAPTER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/socket_types.hpp" + +#if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) + +namespace asio { +namespace detail { + +// Adapts the FD_SET type to meet the Descriptor_Set concept's requirements. +class posix_fd_set_adapter +{ +public: + posix_fd_set_adapter() + : max_descriptor_(invalid_socket) + { + using namespace std; // Needed for memset on Solaris. + FD_ZERO(&fd_set_); + } + + void set(socket_type descriptor) + { + if (max_descriptor_ == invalid_socket || descriptor > max_descriptor_) + max_descriptor_ = descriptor; + FD_SET(descriptor, &fd_set_); + } + + bool is_set(socket_type descriptor) const + { + return FD_ISSET(descriptor, &fd_set_) != 0; + } + + operator fd_set*() + { + return &fd_set_; + } + + socket_type max_descriptor() const + { + return max_descriptor_; + } + +private: + fd_set fd_set_; + socket_type max_descriptor_; +}; + +} // namespace detail +} // namespace asio + +#endif // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_POSIX_FD_SET_ADAPTER_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/detail/posix_mutex.hpp b/encryption/libtorrent/include/libtorrent/asio/detail/posix_mutex.hpp new file mode 100644 index 000000000..154089f3c --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/detail/posix_mutex.hpp @@ -0,0 +1,100 @@ +// +// posix_mutex.hpp +// ~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_POSIX_MUTEX_HPP +#define ASIO_DETAIL_POSIX_MUTEX_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include "asio/detail/pop_options.hpp" + +#if defined(BOOST_HAS_PTHREADS) + +#include "asio/detail/push_options.hpp" +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/system_error.hpp" +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/scoped_lock.hpp" + +namespace asio { +namespace detail { + +class posix_mutex + : private noncopyable +{ +public: + typedef asio::detail::scoped_lock scoped_lock; + + // Constructor. + posix_mutex() + { + int error = ::pthread_mutex_init(&mutex_, 0); + if (error != 0) + { + asio::system_error e( + asio::error_code(error, asio::native_ecat), + "mutex"); + boost::throw_exception(e); + } + } + + // Destructor. + ~posix_mutex() + { + ::pthread_mutex_destroy(&mutex_); + } + + // Lock the mutex. + void lock() + { + int error = ::pthread_mutex_lock(&mutex_); + if (error != 0) + { + asio::system_error e( + asio::error_code(error, asio::native_ecat), + "mutex"); + boost::throw_exception(e); + } + } + + // Unlock the mutex. + void unlock() + { + int error = ::pthread_mutex_unlock(&mutex_); + if (error != 0) + { + asio::system_error e( + asio::error_code(error, asio::native_ecat), + "mutex"); + boost::throw_exception(e); + } + } + +private: + ::pthread_mutex_t mutex_; +}; + +} // namespace detail +} // namespace asio + +#endif // defined(BOOST_HAS_PTHREADS) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_POSIX_MUTEX_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/detail/posix_signal_blocker.hpp b/encryption/libtorrent/include/libtorrent/asio/detail/posix_signal_blocker.hpp new file mode 100644 index 000000000..73d0581e1 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/detail/posix_signal_blocker.hpp @@ -0,0 +1,90 @@ +// +// posix_signal_blocker.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_POSIX_SIGNAL_BLOCKER_HPP +#define ASIO_DETAIL_POSIX_SIGNAL_BLOCKER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include "asio/detail/pop_options.hpp" + +#if defined(BOOST_HAS_PTHREADS) + +#include "asio/detail/push_options.hpp" +#include +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/detail/noncopyable.hpp" + +namespace asio { +namespace detail { + +class posix_signal_blocker + : private noncopyable +{ +public: + // Constructor blocks all signals for the calling thread. + posix_signal_blocker() + : blocked_(false) + { + sigset_t new_mask; + sigfillset(&new_mask); + blocked_ = (pthread_sigmask(SIG_BLOCK, &new_mask, &old_mask_) == 0); + } + + // Destructor restores the previous signal mask. + ~posix_signal_blocker() + { + if (blocked_) + pthread_sigmask(SIG_SETMASK, &old_mask_, 0); + } + + // Block all signals for the calling thread. + void block() + { + if (!blocked_) + { + sigset_t new_mask; + sigfillset(&new_mask); + blocked_ = (pthread_sigmask(SIG_BLOCK, &new_mask, &old_mask_) == 0); + } + } + + // Restore the previous signal mask. + void unblock() + { + if (blocked_) + blocked_ = (pthread_sigmask(SIG_SETMASK, &old_mask_, 0) != 0); + } + +private: + // Have signals been blocked. + bool blocked_; + + // The previous signal mask. + sigset_t old_mask_; +}; + +} // namespace detail +} // namespace asio + +#endif // defined(BOOST_HAS_PTHREADS) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_POSIX_SIGNAL_BLOCKER_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/detail/posix_thread.hpp b/encryption/libtorrent/include/libtorrent/asio/detail/posix_thread.hpp new file mode 100644 index 000000000..f01b40428 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/detail/posix_thread.hpp @@ -0,0 +1,127 @@ +// +// posix_thread.hpp +// ~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_POSIX_THREAD_HPP +#define ASIO_DETAIL_POSIX_THREAD_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include "asio/detail/pop_options.hpp" + +#if defined(BOOST_HAS_PTHREADS) + +#include "asio/detail/push_options.hpp" +#include +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/system_error.hpp" +#include "asio/detail/noncopyable.hpp" + +namespace asio { +namespace detail { + +extern "C" void* asio_detail_posix_thread_function(void* arg); + +class posix_thread + : private noncopyable +{ +public: + // Constructor. + template + posix_thread(Function f) + : joined_(false) + { + std::auto_ptr arg(new func(f)); + int error = ::pthread_create(&thread_, 0, + asio_detail_posix_thread_function, arg.get()); + if (error != 0) + { + asio::system_error e( + asio::error_code(error, asio::native_ecat), + "thread"); + boost::throw_exception(e); + } + arg.release(); + } + + // Destructor. + ~posix_thread() + { + if (!joined_) + ::pthread_detach(thread_); + } + + // Wait for the thread to exit. + void join() + { + if (!joined_) + { + ::pthread_join(thread_, 0); + joined_ = true; + } + } + +private: + friend void* asio_detail_posix_thread_function(void* arg); + + class func_base + { + public: + virtual ~func_base() {} + virtual void run() = 0; + }; + + template + class func + : public func_base + { + public: + func(Function f) + : f_(f) + { + } + + virtual void run() + { + f_(); + } + + private: + Function f_; + }; + + ::pthread_t thread_; + bool joined_; +}; + +inline void* asio_detail_posix_thread_function(void* arg) +{ + std::auto_ptr f( + static_cast(arg)); + f->run(); + return 0; +} + +} // namespace detail +} // namespace asio + +#endif // defined(BOOST_HAS_PTHREADS) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_POSIX_THREAD_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/detail/posix_tss_ptr.hpp b/encryption/libtorrent/include/libtorrent/asio/detail/posix_tss_ptr.hpp new file mode 100644 index 000000000..93fce3479 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/detail/posix_tss_ptr.hpp @@ -0,0 +1,86 @@ +// +// posix_tss_ptr.hpp +// ~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_POSIX_TSS_PTR_HPP +#define ASIO_DETAIL_POSIX_TSS_PTR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include "asio/detail/pop_options.hpp" + +#if defined(BOOST_HAS_PTHREADS) + +#include "asio/detail/push_options.hpp" +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/system_error.hpp" +#include "asio/detail/noncopyable.hpp" + +namespace asio { +namespace detail { + +template +class posix_tss_ptr + : private noncopyable +{ +public: + // Constructor. + posix_tss_ptr() + { + int error = ::pthread_key_create(&tss_key_, 0); + if (error != 0) + { + asio::system_error e( + asio::error_code(error, asio::native_ecat), + "tss"); + boost::throw_exception(e); + } + } + + // Destructor. + ~posix_tss_ptr() + { + ::pthread_key_delete(tss_key_); + } + + // Get the value. + operator T*() const + { + return static_cast(::pthread_getspecific(tss_key_)); + } + + // Set the value. + void operator=(T* value) + { + ::pthread_setspecific(tss_key_, value); + } + +private: + // Thread-specific storage to allow unlocked access to determine whether a + // thread is a member of the pool. + pthread_key_t tss_key_; +}; + +} // namespace detail +} // namespace asio + +#endif // defined(BOOST_HAS_PTHREADS) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_POSIX_TSS_PTR_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/detail/push_options.hpp b/encryption/libtorrent/include/libtorrent/asio/detail/push_options.hpp new file mode 100644 index 000000000..0b68d2933 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/detail/push_options.hpp @@ -0,0 +1,106 @@ +// +// push_options.hpp +// ~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +// No header guard + +#if defined(__COMO__) + +// Comeau C++ + +#elif defined(__DMC__) + +// Digital Mars C++ + +#elif defined(__INTEL_COMPILER) || defined(__ICL) \ + || defined(__ICC) || defined(__ECC) + +// Intel C++ + +#elif defined(__GNUC__) + +// GNU C++ + +# if defined(__MINGW32__) || defined(__CYGWIN__) +# pragma pack (push, 8) +# endif + +#elif defined(__KCC) + +// Kai C++ + +#elif defined(__sgi) + +// SGI MIPSpro C++ + +#elif defined(__DECCXX) + +// Compaq Tru64 Unix cxx + +#elif defined(__ghs) + +// Greenhills C++ + +#elif defined(__BORLANDC__) + +// Borland C++ + +# pragma option push -a8 -b -Ve- -Vx- -w-inl -vi- +# pragma nopushoptwarn +# pragma nopackwarning +# if !defined(__MT__) +# error Multithreaded RTL must be selected. +# endif // !defined(__MT__) + +#elif defined(__MWERKS__) + +// Metrowerks CodeWarrior + +#elif defined(__SUNPRO_CC) + +// Sun Workshop Compiler C++ + +#elif defined(__HP_aCC) + +// HP aCC + +#elif defined(__MRC__) || defined(__SC__) + +// MPW MrCpp or SCpp + +#elif defined(__IBMCPP__) + +// IBM Visual Age + +#elif defined(_MSC_VER) + +// Microsoft Visual C++ +// +// Must remain the last #elif since some other vendors (Metrowerks, for example) +// also #define _MSC_VER + +# pragma warning (disable:4103) +# pragma warning (push) +# pragma warning (disable:4244) +# pragma warning (disable:4355) +# pragma warning (disable:4675) +# pragma pack (push, 8) +// Note that if the /Og optimisation flag is enabled with MSVC6, the compiler +// has a tendency to incorrectly optimise away some calls to member template +// functions, even though those functions contain code that should not be +// optimised away! Therefore we will always disable this optimisation option +// for the MSVC6 compiler. +# if (_MSC_VER < 1300) +# pragma optimize ("g", off) +# endif +# if !defined(_MT) +# error Multithreaded RTL must be selected. +# endif // !defined(_MT) + +#endif diff --git a/encryption/libtorrent/include/libtorrent/asio/detail/reactive_socket_service.hpp b/encryption/libtorrent/include/libtorrent/asio/detail/reactive_socket_service.hpp new file mode 100644 index 000000000..b58f62781 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/detail/reactive_socket_service.hpp @@ -0,0 +1,1486 @@ +// +// reactive_socket_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_REACTIVE_SOCKET_SERVICE_HPP +#define ASIO_DETAIL_REACTIVE_SOCKET_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/buffer.hpp" +#include "asio/error.hpp" +#include "asio/io_service.hpp" +#include "asio/socket_base.hpp" +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/service_base.hpp" +#include "asio/detail/socket_holder.hpp" +#include "asio/detail/socket_ops.hpp" +#include "asio/detail/socket_types.hpp" + +namespace asio { +namespace detail { + +template +class reactive_socket_service + : public asio::detail::service_base< + reactive_socket_service > +{ +public: + // The protocol type. + typedef Protocol protocol_type; + + // The endpoint type. + typedef typename Protocol::endpoint endpoint_type; + + // The native type of a socket. + typedef socket_type native_type; + + // The implementation type of the socket. + class implementation_type + : private asio::detail::noncopyable + { + public: + // Default constructor. + implementation_type() + : socket_(invalid_socket), + flags_(0), + protocol_(endpoint_type().protocol()) + { + } + + private: + // Only this service will have access to the internal values. + friend class reactive_socket_service; + + // The native socket representation. + socket_type socket_; + + enum + { + user_set_non_blocking = 1, // The user wants a non-blocking socket. + internal_non_blocking = 2, // The socket has been set non-blocking. + enable_connection_aborted = 4, // User wants connection_aborted errors. + user_set_linger = 8 // The user set the linger option. + }; + + // Flags indicating the current state of the socket. + unsigned char flags_; + + // The protocol associated with the socket. + protocol_type protocol_; + }; + + // The maximum number of buffers to support in a single operation. + enum { max_buffers = 16 }; + + // Constructor. + reactive_socket_service(asio::io_service& io_service) + : asio::detail::service_base< + reactive_socket_service >(io_service), + reactor_(asio::use_service(io_service)) + { + } + + // Destroy all user-defined handler objects owned by the service. + void shutdown_service() + { + } + + // Construct a new socket implementation. + void construct(implementation_type& impl) + { + impl.socket_ = invalid_socket; + impl.flags_ = 0; + } + + // Destroy a socket implementation. + void destroy(implementation_type& impl) + { + if (impl.socket_ != invalid_socket) + { + reactor_.close_descriptor(impl.socket_); + + if (impl.flags_ & implementation_type::internal_non_blocking) + { + ioctl_arg_type non_blocking = 0; + asio::error_code ignored_ec; + socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ignored_ec); + impl.flags_ &= ~implementation_type::internal_non_blocking; + } + + if (impl.flags_ & implementation_type::user_set_linger) + { + ::linger opt; + opt.l_onoff = 0; + opt.l_linger = 0; + asio::error_code ignored_ec; + socket_ops::setsockopt(impl.socket_, + SOL_SOCKET, SO_LINGER, &opt, sizeof(opt), ignored_ec); + } + + asio::error_code ignored_ec; + socket_ops::close(impl.socket_, ignored_ec); + + impl.socket_ = invalid_socket; + } + } + + // Open a new socket implementation. + asio::error_code open(implementation_type& impl, + const protocol_type& protocol, asio::error_code& ec) + { + if (is_open(impl)) + { + ec = asio::error::already_open; + return ec; + } + + socket_holder sock(socket_ops::socket(protocol.family(), + protocol.type(), protocol.protocol(), ec)); + if (sock.get() == invalid_socket) + return ec; + + if (int err = reactor_.register_descriptor(sock.get())) + { + ec = asio::error_code(err, asio::native_ecat); + return ec; + } + + impl.socket_ = sock.release(); + impl.flags_ = 0; + impl.protocol_ = protocol; + ec = asio::error_code(); + return ec; + } + + // Assign a native socket to a socket implementation. + asio::error_code assign(implementation_type& impl, + const protocol_type& protocol, const native_type& native_socket, + asio::error_code& ec) + { + if (is_open(impl)) + { + ec = asio::error::already_open; + return ec; + } + + if (int err = reactor_.register_descriptor(native_socket)) + { + ec = asio::error_code(err, asio::native_ecat); + return ec; + } + + impl.socket_ = native_socket; + impl.flags_ = 0; + impl.protocol_ = protocol; + ec = asio::error_code(); + return ec; + } + + // Determine whether the socket is open. + bool is_open(const implementation_type& impl) const + { + return impl.socket_ != invalid_socket; + } + + // Destroy a socket implementation. + asio::error_code close(implementation_type& impl, + asio::error_code& ec) + { + if (is_open(impl)) + { + reactor_.close_descriptor(impl.socket_); + + if (impl.flags_ & implementation_type::internal_non_blocking) + { + ioctl_arg_type non_blocking = 0; + asio::error_code ignored_ec; + socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ignored_ec); + impl.flags_ &= ~implementation_type::internal_non_blocking; + } + + if (socket_ops::close(impl.socket_, ec) == socket_error_retval) + return ec; + + impl.socket_ = invalid_socket; + } + + ec = asio::error_code(); + return ec; + } + + // Get the native socket representation. + native_type native(implementation_type& impl) + { + return impl.socket_; + } + + // Cancel all operations associated with the socket. + asio::error_code cancel(implementation_type& impl, + asio::error_code& ec) + { + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return ec; + } + + reactor_.cancel_ops(impl.socket_); + ec = asio::error_code(); + return ec; + } + + // Determine whether the socket is at the out-of-band data mark. + bool at_mark(const implementation_type& impl, + asio::error_code& ec) const + { + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return false; + } + + asio::detail::ioctl_arg_type value = 0; + socket_ops::ioctl(impl.socket_, SIOCATMARK, &value, ec); +#if defined(ENOTTY) + if (ec.value() == ENOTTY) + ec = asio::error::not_socket; +#endif // defined(ENOTTY) + return ec ? false : value != 0; + } + + // Determine the number of bytes available for reading. + std::size_t available(const implementation_type& impl, + asio::error_code& ec) const + { + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return 0; + } + + asio::detail::ioctl_arg_type value = 0; + socket_ops::ioctl(impl.socket_, FIONREAD, &value, ec); +#if defined(ENOTTY) + if (ec.value() == ENOTTY) + ec = asio::error::not_socket; +#endif // defined(ENOTTY) + return ec ? static_cast(0) : static_cast(value); + } + + // Bind the socket to the specified local endpoint. + asio::error_code bind(implementation_type& impl, + const endpoint_type& endpoint, asio::error_code& ec) + { + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return ec; + } + + socket_ops::bind(impl.socket_, endpoint.data(), endpoint.size(), ec); + return ec; + } + + // Place the socket into the state where it will listen for new connections. + asio::error_code listen(implementation_type& impl, int backlog, + asio::error_code& ec) + { + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return ec; + } + + socket_ops::listen(impl.socket_, backlog, ec); + return ec; + } + + // Set a socket option. + template + asio::error_code set_option(implementation_type& impl, + const Option& option, asio::error_code& ec) + { + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return ec; + } + + if (option.level(impl.protocol_) == custom_socket_option_level + && option.name(impl.protocol_) == enable_connection_aborted_option) + { + if (option.size(impl.protocol_) != sizeof(int)) + { + ec = asio::error::invalid_argument; + } + else + { + if (*reinterpret_cast(option.data(impl.protocol_))) + impl.flags_ |= implementation_type::enable_connection_aborted; + else + impl.flags_ &= ~implementation_type::enable_connection_aborted; + ec = asio::error_code(); + } + return ec; + } + else + { + if (option.level(impl.protocol_) == SOL_SOCKET + && option.name(impl.protocol_) == SO_LINGER) + { + impl.flags_ |= implementation_type::user_set_linger; + } + + socket_ops::setsockopt(impl.socket_, + option.level(impl.protocol_), option.name(impl.protocol_), + option.data(impl.protocol_), option.size(impl.protocol_), ec); + return ec; + } + } + + // Set a socket option. + template + asio::error_code get_option(const implementation_type& impl, + Option& option, asio::error_code& ec) const + { + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return ec; + } + + if (option.level(impl.protocol_) == custom_socket_option_level + && option.name(impl.protocol_) == enable_connection_aborted_option) + { + if (option.size(impl.protocol_) != sizeof(int)) + { + ec = asio::error::invalid_argument; + } + else + { + int* target = reinterpret_cast(option.data(impl.protocol_)); + if (impl.flags_ & implementation_type::enable_connection_aborted) + *target = 1; + else + *target = 0; + option.resize(impl.protocol_, sizeof(int)); + ec = asio::error_code(); + } + return ec; + } + else + { + size_t size = option.size(impl.protocol_); + socket_ops::getsockopt(impl.socket_, + option.level(impl.protocol_), option.name(impl.protocol_), + option.data(impl.protocol_), &size, ec); + if (!ec) + option.resize(impl.protocol_, size); + return ec; + } + } + + // Perform an IO control command on the socket. + template + asio::error_code io_control(implementation_type& impl, + IO_Control_Command& command, asio::error_code& ec) + { + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return ec; + } + + if (command.name() == static_cast(FIONBIO)) + { + if (command.get()) + impl.flags_ |= implementation_type::user_set_non_blocking; + else + impl.flags_ &= ~implementation_type::user_set_non_blocking; + ec = asio::error_code(); + } + else + { + socket_ops::ioctl(impl.socket_, command.name(), + static_cast(command.data()), ec); + } + return ec; + } + + // Get the local endpoint. + endpoint_type local_endpoint(const implementation_type& impl, + asio::error_code& ec) const + { + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return endpoint_type(); + } + + endpoint_type endpoint; + socket_addr_len_type addr_len = endpoint.capacity(); + if (socket_ops::getsockname(impl.socket_, endpoint.data(), &addr_len, ec)) + return endpoint_type(); + endpoint.resize(addr_len); + return endpoint; + } + + // Get the remote endpoint. + endpoint_type remote_endpoint(const implementation_type& impl, + asio::error_code& ec) const + { + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return endpoint_type(); + } + + endpoint_type endpoint; + socket_addr_len_type addr_len = endpoint.capacity(); + if (socket_ops::getpeername(impl.socket_, endpoint.data(), &addr_len, ec)) + return endpoint_type(); + endpoint.resize(addr_len); + return endpoint; + } + + /// Disable sends or receives on the socket. + asio::error_code shutdown(implementation_type& impl, + socket_base::shutdown_type what, asio::error_code& ec) + { + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return ec; + } + + socket_ops::shutdown(impl.socket_, what, ec); + return ec; + } + + // Send the given data to the peer. + template + size_t send(implementation_type& impl, const ConstBufferSequence& buffers, + socket_base::message_flags flags, asio::error_code& ec) + { + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return 0; + } + + // Copy buffers into array. + socket_ops::buf bufs[max_buffers]; + typename ConstBufferSequence::const_iterator iter = buffers.begin(); + typename ConstBufferSequence::const_iterator end = buffers.end(); + size_t i = 0; + size_t total_buffer_size = 0; + for (; iter != end && i < max_buffers; ++iter, ++i) + { + asio::const_buffer buffer(*iter); + socket_ops::init_buf(bufs[i], + asio::buffer_cast(buffer), + asio::buffer_size(buffer)); + total_buffer_size += asio::buffer_size(buffer); + } + + // A request to receive 0 bytes on a stream socket is a no-op. + if (impl.protocol_.type() == SOCK_STREAM && total_buffer_size == 0) + { + ec = asio::error_code(); + return 0; + } + + // Send the data. + for (;;) + { + // Try to complete the operation without blocking. + int bytes_sent = socket_ops::send(impl.socket_, bufs, i, flags, ec); + + // Check if operation succeeded. + if (bytes_sent >= 0) + return bytes_sent; + + // Operation failed. + if ((impl.flags_ & implementation_type::user_set_non_blocking) + || (ec != asio::error::would_block + && ec != asio::error::try_again)) + return 0; + + // Wait for socket to become ready. + if (socket_ops::poll_write(impl.socket_, ec) < 0) + return 0; + } + } + + template + class send_handler + { + public: + send_handler(socket_type socket, asio::io_service& io_service, + const ConstBufferSequence& buffers, socket_base::message_flags flags, + Handler handler) + : socket_(socket), + io_service_(io_service), + work_(io_service), + buffers_(buffers), + flags_(flags), + handler_(handler) + { + } + + bool operator()(const asio::error_code& result) + { + // Check whether the operation was successful. + if (result) + { + io_service_.post(bind_handler(handler_, result, 0)); + return true; + } + + // Copy buffers into array. + socket_ops::buf bufs[max_buffers]; + typename ConstBufferSequence::const_iterator iter = buffers_.begin(); + typename ConstBufferSequence::const_iterator end = buffers_.end(); + size_t i = 0; + for (; iter != end && i < max_buffers; ++iter, ++i) + { + asio::const_buffer buffer(*iter); + socket_ops::init_buf(bufs[i], + asio::buffer_cast(buffer), + asio::buffer_size(buffer)); + } + + // Send the data. + asio::error_code ec; + int bytes = socket_ops::send(socket_, bufs, i, flags_, ec); + + // Check if we need to run the operation again. + if (ec == asio::error::would_block + || ec == asio::error::try_again) + return false; + + io_service_.post(bind_handler(handler_, ec, bytes < 0 ? 0 : bytes)); + return true; + } + + private: + socket_type socket_; + asio::io_service& io_service_; + asio::io_service::work work_; + ConstBufferSequence buffers_; + socket_base::message_flags flags_; + Handler handler_; + }; + + // Start an asynchronous send. The data being sent must be valid for the + // lifetime of the asynchronous operation. + template + void async_send(implementation_type& impl, const ConstBufferSequence& buffers, + socket_base::message_flags flags, Handler handler) + { + if (!is_open(impl)) + { + this->io_service().post(bind_handler(handler, + asio::error::bad_descriptor, 0)); + } + else + { + if (impl.protocol_.type() == SOCK_STREAM) + { + // Determine total size of buffers. + typename ConstBufferSequence::const_iterator iter = buffers.begin(); + typename ConstBufferSequence::const_iterator end = buffers.end(); + size_t i = 0; + size_t total_buffer_size = 0; + for (; iter != end && i < max_buffers; ++iter, ++i) + { + asio::const_buffer buffer(*iter); + total_buffer_size += asio::buffer_size(buffer); + } + + // A request to receive 0 bytes on a stream socket is a no-op. + if (total_buffer_size == 0) + { + this->io_service().post(bind_handler(handler, + asio::error_code(), 0)); + return; + } + } + + // Make socket non-blocking. + if (!(impl.flags_ & implementation_type::internal_non_blocking)) + { + ioctl_arg_type non_blocking = 1; + asio::error_code ec; + if (socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ec)) + { + this->io_service().post(bind_handler(handler, ec, 0)); + return; + } + impl.flags_ |= implementation_type::internal_non_blocking; + } + + reactor_.start_write_op(impl.socket_, + send_handler( + impl.socket_, this->io_service(), buffers, flags, handler)); + } + } + + // Send a datagram to the specified endpoint. Returns the number of bytes + // sent. + template + size_t send_to(implementation_type& impl, const ConstBufferSequence& buffers, + const endpoint_type& destination, socket_base::message_flags flags, + asio::error_code& ec) + { + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return 0; + } + + // Copy buffers into array. + socket_ops::buf bufs[max_buffers]; + typename ConstBufferSequence::const_iterator iter = buffers.begin(); + typename ConstBufferSequence::const_iterator end = buffers.end(); + size_t i = 0; + for (; iter != end && i < max_buffers; ++iter, ++i) + { + asio::const_buffer buffer(*iter); + socket_ops::init_buf(bufs[i], + asio::buffer_cast(buffer), + asio::buffer_size(buffer)); + } + + // Send the data. + for (;;) + { + // Try to complete the operation without blocking. + int bytes_sent = socket_ops::sendto(impl.socket_, bufs, i, flags, + destination.data(), destination.size(), ec); + + // Check if operation succeeded. + if (bytes_sent >= 0) + return bytes_sent; + + // Operation failed. + if ((impl.flags_ & implementation_type::user_set_non_blocking) + || (ec != asio::error::would_block + && ec != asio::error::try_again)) + return 0; + + // Wait for socket to become ready. + if (socket_ops::poll_write(impl.socket_, ec) < 0) + return 0; + } + } + + template + class send_to_handler + { + public: + send_to_handler(socket_type socket, asio::io_service& io_service, + const ConstBufferSequence& buffers, const endpoint_type& endpoint, + socket_base::message_flags flags, Handler handler) + : socket_(socket), + io_service_(io_service), + work_(io_service), + buffers_(buffers), + destination_(endpoint), + flags_(flags), + handler_(handler) + { + } + + bool operator()(const asio::error_code& result) + { + // Check whether the operation was successful. + if (result) + { + io_service_.post(bind_handler(handler_, result, 0)); + return true; + } + + // Copy buffers into array. + socket_ops::buf bufs[max_buffers]; + typename ConstBufferSequence::const_iterator iter = buffers_.begin(); + typename ConstBufferSequence::const_iterator end = buffers_.end(); + size_t i = 0; + for (; iter != end && i < max_buffers; ++iter, ++i) + { + asio::const_buffer buffer(*iter); + socket_ops::init_buf(bufs[i], + asio::buffer_cast(buffer), + asio::buffer_size(buffer)); + } + + // Send the data. + asio::error_code ec; + int bytes = socket_ops::sendto(socket_, bufs, i, flags_, + destination_.data(), destination_.size(), ec); + + // Check if we need to run the operation again. + if (ec == asio::error::would_block + || ec == asio::error::try_again) + return false; + + io_service_.post(bind_handler(handler_, ec, bytes < 0 ? 0 : bytes)); + return true; + } + + private: + socket_type socket_; + asio::io_service& io_service_; + asio::io_service::work work_; + ConstBufferSequence buffers_; + endpoint_type destination_; + socket_base::message_flags flags_; + Handler handler_; + }; + + // Start an asynchronous send. The data being sent must be valid for the + // lifetime of the asynchronous operation. + template + void async_send_to(implementation_type& impl, + const ConstBufferSequence& buffers, + const endpoint_type& destination, socket_base::message_flags flags, + Handler handler) + { + if (!is_open(impl)) + { + this->io_service().post(bind_handler(handler, + asio::error::bad_descriptor, 0)); + } + else + { + // Make socket non-blocking. + if (!(impl.flags_ & implementation_type::internal_non_blocking)) + { + ioctl_arg_type non_blocking = 1; + asio::error_code ec; + if (socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ec)) + { + this->io_service().post(bind_handler(handler, ec, 0)); + return; + } + impl.flags_ |= implementation_type::internal_non_blocking; + } + + reactor_.start_write_op(impl.socket_, + send_to_handler( + impl.socket_, this->io_service(), buffers, + destination, flags, handler)); + } + } + + // Receive some data from the peer. Returns the number of bytes received. + template + size_t receive(implementation_type& impl, + const MutableBufferSequence& buffers, + socket_base::message_flags flags, asio::error_code& ec) + { + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return 0; + } + + // Copy buffers into array. + socket_ops::buf bufs[max_buffers]; + typename MutableBufferSequence::const_iterator iter = buffers.begin(); + typename MutableBufferSequence::const_iterator end = buffers.end(); + size_t i = 0; + size_t total_buffer_size = 0; + for (; iter != end && i < max_buffers; ++iter, ++i) + { + asio::mutable_buffer buffer(*iter); + socket_ops::init_buf(bufs[i], + asio::buffer_cast(buffer), + asio::buffer_size(buffer)); + total_buffer_size += asio::buffer_size(buffer); + } + + // A request to receive 0 bytes on a stream socket is a no-op. + if (impl.protocol_.type() == SOCK_STREAM && total_buffer_size == 0) + { + ec = asio::error_code(); + return 0; + } + + // Receive some data. + for (;;) + { + // Try to complete the operation without blocking. + int bytes_recvd = socket_ops::recv(impl.socket_, bufs, i, flags, ec); + + // Check if operation succeeded. + if (bytes_recvd > 0) + return bytes_recvd; + + // Check for EOF. + if (bytes_recvd == 0) + { + ec = asio::error::eof; + return 0; + } + + // Operation failed. + if ((impl.flags_ & implementation_type::user_set_non_blocking) + || (ec != asio::error::would_block + && ec != asio::error::try_again)) + return 0; + + // Wait for socket to become ready. + if (socket_ops::poll_read(impl.socket_, ec) < 0) + return 0; + } + } + + template + class receive_handler + { + public: + receive_handler(socket_type socket, asio::io_service& io_service, + const MutableBufferSequence& buffers, socket_base::message_flags flags, + Handler handler) + : socket_(socket), + io_service_(io_service), + work_(io_service), + buffers_(buffers), + flags_(flags), + handler_(handler) + { + } + + bool operator()(const asio::error_code& result) + { + // Check whether the operation was successful. + if (result) + { + io_service_.post(bind_handler(handler_, result, 0)); + return true; + } + + // Copy buffers into array. + socket_ops::buf bufs[max_buffers]; + typename MutableBufferSequence::const_iterator iter = buffers_.begin(); + typename MutableBufferSequence::const_iterator end = buffers_.end(); + size_t i = 0; + for (; iter != end && i < max_buffers; ++iter, ++i) + { + asio::mutable_buffer buffer(*iter); + socket_ops::init_buf(bufs[i], + asio::buffer_cast(buffer), + asio::buffer_size(buffer)); + } + + // Receive some data. + asio::error_code ec; + int bytes = socket_ops::recv(socket_, bufs, i, flags_, ec); + if (bytes == 0) + ec = asio::error::eof; + + // Check if we need to run the operation again. + if (ec == asio::error::would_block + || ec == asio::error::try_again) + return false; + + io_service_.post(bind_handler(handler_, ec, bytes < 0 ? 0 : bytes)); + return true; + } + + private: + socket_type socket_; + asio::io_service& io_service_; + asio::io_service::work work_; + MutableBufferSequence buffers_; + socket_base::message_flags flags_; + Handler handler_; + }; + + // Start an asynchronous receive. The buffer for the data being received + // must be valid for the lifetime of the asynchronous operation. + template + void async_receive(implementation_type& impl, + const MutableBufferSequence& buffers, + socket_base::message_flags flags, Handler handler) + { + if (!is_open(impl)) + { + this->io_service().post(bind_handler(handler, + asio::error::bad_descriptor, 0)); + } + else + { + if (impl.protocol_.type() == SOCK_STREAM) + { + // Determine total size of buffers. + typename MutableBufferSequence::const_iterator iter = buffers.begin(); + typename MutableBufferSequence::const_iterator end = buffers.end(); + size_t i = 0; + size_t total_buffer_size = 0; + for (; iter != end && i < max_buffers; ++iter, ++i) + { + asio::mutable_buffer buffer(*iter); + total_buffer_size += asio::buffer_size(buffer); + } + + // A request to receive 0 bytes on a stream socket is a no-op. + if (total_buffer_size == 0) + { + this->io_service().post(bind_handler(handler, + asio::error_code(), 0)); + return; + } + } + + // Make socket non-blocking. + if (!(impl.flags_ & implementation_type::internal_non_blocking)) + { + ioctl_arg_type non_blocking = 1; + asio::error_code ec; + if (socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ec)) + { + this->io_service().post(bind_handler(handler, ec, 0)); + return; + } + impl.flags_ |= implementation_type::internal_non_blocking; + } + + if (flags & socket_base::message_out_of_band) + { + reactor_.start_except_op(impl.socket_, + receive_handler( + impl.socket_, this->io_service(), buffers, flags, handler)); + } + else + { + reactor_.start_read_op(impl.socket_, + receive_handler( + impl.socket_, this->io_service(), buffers, flags, handler)); + } + } + } + + // Receive a datagram with the endpoint of the sender. Returns the number of + // bytes received. + template + size_t receive_from(implementation_type& impl, + const MutableBufferSequence& buffers, + endpoint_type& sender_endpoint, socket_base::message_flags flags, + asio::error_code& ec) + { + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return 0; + } + + // Copy buffers into array. + socket_ops::buf bufs[max_buffers]; + typename MutableBufferSequence::const_iterator iter = buffers.begin(); + typename MutableBufferSequence::const_iterator end = buffers.end(); + size_t i = 0; + for (; iter != end && i < max_buffers; ++iter, ++i) + { + asio::mutable_buffer buffer(*iter); + socket_ops::init_buf(bufs[i], + asio::buffer_cast(buffer), + asio::buffer_size(buffer)); + } + + // Receive some data. + for (;;) + { + // Try to complete the operation without blocking. + socket_addr_len_type addr_len = sender_endpoint.capacity(); + int bytes_recvd = socket_ops::recvfrom(impl.socket_, bufs, i, flags, + sender_endpoint.data(), &addr_len, ec); + + // Check if operation succeeded. + if (bytes_recvd > 0) + { + sender_endpoint.resize(addr_len); + return bytes_recvd; + } + + // Check for EOF. + if (bytes_recvd == 0) + { + ec = asio::error::eof; + return 0; + } + + // Operation failed. + if ((impl.flags_ & implementation_type::user_set_non_blocking) + || (ec != asio::error::would_block + && ec != asio::error::try_again)) + return 0; + + // Wait for socket to become ready. + if (socket_ops::poll_read(impl.socket_, ec) < 0) + return 0; + } + } + + template + class receive_from_handler + { + public: + receive_from_handler(socket_type socket, + asio::io_service& io_service, + const MutableBufferSequence& buffers, endpoint_type& endpoint, + socket_base::message_flags flags, Handler handler) + : socket_(socket), + io_service_(io_service), + work_(io_service), + buffers_(buffers), + sender_endpoint_(endpoint), + flags_(flags), + handler_(handler) + { + } + + bool operator()(const asio::error_code& result) + { + // Check whether the operation was successful. + if (result != 0) + { + io_service_.post(bind_handler(handler_, result, 0)); + return true; + } + + // Copy buffers into array. + socket_ops::buf bufs[max_buffers]; + typename MutableBufferSequence::const_iterator iter = buffers_.begin(); + typename MutableBufferSequence::const_iterator end = buffers_.end(); + size_t i = 0; + for (; iter != end && i < max_buffers; ++iter, ++i) + { + asio::mutable_buffer buffer(*iter); + socket_ops::init_buf(bufs[i], + asio::buffer_cast(buffer), + asio::buffer_size(buffer)); + } + + // Receive some data. + socket_addr_len_type addr_len = sender_endpoint_.capacity(); + asio::error_code ec; + int bytes = socket_ops::recvfrom(socket_, bufs, i, flags_, + sender_endpoint_.data(), &addr_len, ec); + if (bytes == 0) + ec = asio::error::eof; + + // Check if we need to run the operation again. + if (ec == asio::error::would_block + || ec == asio::error::try_again) + return false; + + sender_endpoint_.resize(addr_len); + io_service_.post(bind_handler(handler_, ec, bytes < 0 ? 0 : bytes)); + return true; + } + + private: + socket_type socket_; + asio::io_service& io_service_; + asio::io_service::work work_; + MutableBufferSequence buffers_; + endpoint_type& sender_endpoint_; + socket_base::message_flags flags_; + Handler handler_; + }; + + // Start an asynchronous receive. The buffer for the data being received and + // the sender_endpoint object must both be valid for the lifetime of the + // asynchronous operation. + template + void async_receive_from(implementation_type& impl, + const MutableBufferSequence& buffers, endpoint_type& sender_endpoint, + socket_base::message_flags flags, Handler handler) + { + if (!is_open(impl)) + { + this->io_service().post(bind_handler(handler, + asio::error::bad_descriptor, 0)); + } + else + { + // Make socket non-blocking. + if (!(impl.flags_ & implementation_type::internal_non_blocking)) + { + ioctl_arg_type non_blocking = 1; + asio::error_code ec; + if (socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ec)) + { + this->io_service().post(bind_handler(handler, ec, 0)); + return; + } + impl.flags_ |= implementation_type::internal_non_blocking; + } + + reactor_.start_read_op(impl.socket_, + receive_from_handler( + impl.socket_, this->io_service(), buffers, + sender_endpoint, flags, handler)); + } + } + + // Accept a new connection. + template + asio::error_code accept(implementation_type& impl, + Socket& peer, endpoint_type* peer_endpoint, asio::error_code& ec) + { + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return ec; + } + + // We cannot accept a socket that is already open. + if (peer.is_open()) + { + ec = asio::error::already_open; + return ec; + } + + // Accept a socket. + for (;;) + { + // Try to complete the operation without blocking. + asio::error_code ec; + socket_holder new_socket; + socket_addr_len_type addr_len = 0; + if (peer_endpoint) + { + addr_len = peer_endpoint->capacity(); + new_socket.reset(socket_ops::accept(impl.socket_, + peer_endpoint->data(), &addr_len, ec)); + } + else + { + new_socket.reset(socket_ops::accept(impl.socket_, 0, 0, ec)); + } + + // Check if operation succeeded. + if (new_socket.get() >= 0) + { + if (peer_endpoint) + peer_endpoint->resize(addr_len); + peer.assign(impl.protocol_, new_socket.get(), ec); + if (!ec) + new_socket.release(); + return ec; + } + + // Operation failed. + if (ec == asio::error::would_block + || ec == asio::error::try_again) + { + if (impl.flags_ & implementation_type::user_set_non_blocking) + return ec; + // Fall through to retry operation. + } + else if (ec == asio::error::connection_aborted) + { + if (impl.flags_ & implementation_type::enable_connection_aborted) + return ec; + // Fall through to retry operation. + } + else + return ec; + + // Wait for socket to become ready. + if (socket_ops::poll_read(impl.socket_, ec) < 0) + return ec; + } + } + + template + class accept_handler + { + public: + accept_handler(socket_type socket, asio::io_service& io_service, + Socket& peer, const protocol_type& protocol, + endpoint_type* peer_endpoint, bool enable_connection_aborted, + Handler handler) + : socket_(socket), + io_service_(io_service), + work_(io_service), + peer_(peer), + protocol_(protocol), + peer_endpoint_(peer_endpoint), + enable_connection_aborted_(enable_connection_aborted), + handler_(handler) + { + } + + bool operator()(const asio::error_code& result) + { + // Check whether the operation was successful. + if (result) + { + io_service_.post(bind_handler(handler_, result)); + return true; + } + + // Accept the waiting connection. + asio::error_code ec; + socket_holder new_socket; + socket_addr_len_type addr_len = 0; + if (peer_endpoint_) + { + addr_len = peer_endpoint_->capacity(); + new_socket.reset(socket_ops::accept(socket_, + peer_endpoint_->data(), &addr_len, ec)); + } + else + { + new_socket.reset(socket_ops::accept(socket_, 0, 0, ec)); + } + + // Check if we need to run the operation again. + if (ec == asio::error::would_block + || ec == asio::error::try_again) + return false; + if (ec == asio::error::connection_aborted + && !enable_connection_aborted_) + return false; + + // Transfer ownership of the new socket to the peer object. + if (!ec) + { + if (peer_endpoint_) + peer_endpoint_->resize(addr_len); + peer_.assign(protocol_, new_socket.get(), ec); + if (!ec) + new_socket.release(); + } + + io_service_.post(bind_handler(handler_, ec)); + return true; + } + + private: + socket_type socket_; + asio::io_service& io_service_; + asio::io_service::work work_; + Socket& peer_; + protocol_type protocol_; + endpoint_type* peer_endpoint_; + bool enable_connection_aborted_; + Handler handler_; + }; + + // Start an asynchronous accept. The peer and peer_endpoint objects + // must be valid until the accept's handler is invoked. + template + void async_accept(implementation_type& impl, Socket& peer, + endpoint_type* peer_endpoint, Handler handler) + { + if (!is_open(impl)) + { + this->io_service().post(bind_handler(handler, + asio::error::bad_descriptor)); + } + else if (peer.is_open()) + { + this->io_service().post(bind_handler(handler, + asio::error::already_open)); + } + else + { + // Make socket non-blocking. + if (!(impl.flags_ & implementation_type::internal_non_blocking)) + { + ioctl_arg_type non_blocking = 1; + asio::error_code ec; + if (socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ec)) + { + this->io_service().post(bind_handler(handler, ec)); + return; + } + impl.flags_ |= implementation_type::internal_non_blocking; + } + + reactor_.start_read_op(impl.socket_, + accept_handler( + impl.socket_, this->io_service(), + peer, impl.protocol_, peer_endpoint, + (impl.flags_ & implementation_type::enable_connection_aborted) != 0, + handler)); + } + } + + // Connect the socket to the specified endpoint. + asio::error_code connect(implementation_type& impl, + const endpoint_type& peer_endpoint, asio::error_code& ec) + { + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return ec; + } + + if (impl.flags_ & implementation_type::internal_non_blocking) + { + // Mark the socket as blocking while we perform the connect. + ioctl_arg_type non_blocking = 0; + if (socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ec)) + return ec; + impl.flags_ &= ~implementation_type::internal_non_blocking; + } + + // Perform the connect operation. + socket_ops::connect(impl.socket_, + peer_endpoint.data(), peer_endpoint.size(), ec); + return ec; + } + + template + class connect_handler + { + public: + connect_handler(socket_type socket, boost::shared_ptr completed, + asio::io_service& io_service, Reactor& reactor, Handler handler) + : socket_(socket), + completed_(completed), + io_service_(io_service), + work_(io_service), + reactor_(reactor), + handler_(handler) + { + } + + bool operator()(const asio::error_code& result) + { + // Check whether a handler has already been called for the connection. + // If it has, then we don't want to do anything in this handler. + if (*completed_) + return true; + + // Cancel the other reactor operation for the connection. + *completed_ = true; + reactor_.enqueue_cancel_ops_unlocked(socket_); + + // Check whether the operation was successful. + if (result) + { + io_service_.post(bind_handler(handler_, result)); + return true; + } + + // Get the error code from the connect operation. + int connect_error = 0; + size_t connect_error_len = sizeof(connect_error); + asio::error_code ec; + if (socket_ops::getsockopt(socket_, SOL_SOCKET, SO_ERROR, + &connect_error, &connect_error_len, ec) == socket_error_retval) + { + io_service_.post(bind_handler(handler_, ec)); + return true; + } + + // If connection failed then post the handler with the error code. + if (connect_error) + { + ec = asio::error_code(connect_error, + asio::native_ecat); + io_service_.post(bind_handler(handler_, ec)); + return true; + } + + // Post the result of the successful connection operation. + io_service_.post(bind_handler(handler_, ec)); + return true; + } + + private: + socket_type socket_; + boost::shared_ptr completed_; + asio::io_service& io_service_; + asio::io_service::work work_; + Reactor& reactor_; + Handler handler_; + }; + + // Start an asynchronous connect. + template + void async_connect(implementation_type& impl, + const endpoint_type& peer_endpoint, Handler handler) + { + if (!is_open(impl)) + { + this->io_service().post(bind_handler(handler, + asio::error::bad_descriptor)); + return; + } + + // Make socket non-blocking. + if (!(impl.flags_ & implementation_type::internal_non_blocking)) + { + ioctl_arg_type non_blocking = 1; + asio::error_code ec; + if (socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ec)) + { + this->io_service().post(bind_handler(handler, ec)); + return; + } + impl.flags_ |= implementation_type::internal_non_blocking; + } + + // Start the connect operation. The socket is already marked as non-blocking + // so the connection will take place asynchronously. + asio::error_code ec; + if (socket_ops::connect(impl.socket_, peer_endpoint.data(), + peer_endpoint.size(), ec) == 0) + { + // The connect operation has finished successfully so we need to post the + // handler immediately. + this->io_service().post(bind_handler(handler, + asio::error_code())); + } + else if (ec == asio::error::in_progress + || ec == asio::error::would_block) + { + // The connection is happening in the background, and we need to wait + // until the socket becomes writeable. + boost::shared_ptr completed(new bool(false)); + reactor_.start_write_and_except_ops(impl.socket_, + connect_handler( + impl.socket_, completed, this->io_service(), reactor_, handler)); + } + else + { + // The connect operation has failed, so post the handler immediately. + this->io_service().post(bind_handler(handler, ec)); + } + } + +private: + // The selector that performs event demultiplexing for the service. + Reactor& reactor_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_REACTIVE_SOCKET_SERVICE_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/detail/reactor_op_queue.hpp b/encryption/libtorrent/include/libtorrent/asio/detail/reactor_op_queue.hpp new file mode 100644 index 000000000..b2d1054c6 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/detail/reactor_op_queue.hpp @@ -0,0 +1,384 @@ +// +// reactor_op_queue.hpp +// ~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_REACTOR_OP_QUEUE_HPP +#define ASIO_DETAIL_REACTOR_OP_QUEUE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/error.hpp" +#include "asio/detail/hash_map.hpp" +#include "asio/detail/noncopyable.hpp" + +namespace asio { +namespace detail { + +template +class reactor_op_queue + : private noncopyable +{ +public: + // Constructor. + reactor_op_queue() + : operations_(), + cancelled_operations_(0), + cleanup_operations_(0) + { + } + + // Add a new operation to the queue. Returns true if this is the only + // operation for the given descriptor, in which case the reactor's event + // demultiplexing function call may need to be interrupted and restarted. + template + bool enqueue_operation(Descriptor descriptor, Handler handler) + { + op_base* new_op = new op(descriptor, handler); + + typedef typename operation_map::iterator iterator; + typedef typename operation_map::value_type value_type; + std::pair entry = + operations_.insert(value_type(descriptor, new_op)); + if (entry.second) + return true; + + op_base* current_op = entry.first->second; + while (current_op->next_) + current_op = current_op->next_; + current_op->next_ = new_op; + + return false; + } + + // Cancel all operations associated with the descriptor. Any operations + // pending for the descriptor will be notified that they have been cancelled + // next time dispatch_cancellations is called. Returns true if any operations + // were cancelled, in which case the reactor's event demultiplexing function + // may need to be interrupted and restarted. + bool cancel_operations(Descriptor descriptor) + { + typename operation_map::iterator i = operations_.find(descriptor); + if (i != operations_.end()) + { + op_base* last_op = i->second; + while (last_op->next_) + last_op = last_op->next_; + last_op->next_ = cancelled_operations_; + cancelled_operations_ = i->second; + operations_.erase(i); + return true; + } + + return false; + } + + // Whether there are no operations in the queue. + bool empty() const + { + return operations_.empty(); + } + + // Determine whether there are any operations associated with the descriptor. + bool has_operation(Descriptor descriptor) const + { + return operations_.find(descriptor) != operations_.end(); + } + + // Dispatch the first operation corresponding to the descriptor. Returns true + // if there are more operations queued for the descriptor. + bool dispatch_operation(Descriptor descriptor, + const asio::error_code& result) + { + typename operation_map::iterator i = operations_.find(descriptor); + if (i != operations_.end()) + { + op_base* this_op = i->second; + i->second = this_op->next_; + this_op->next_ = cleanup_operations_; + cleanup_operations_ = this_op; + bool done = this_op->invoke(result); + if (done) + { + // Operation has finished. + if (i->second) + { + return true; + } + else + { + operations_.erase(i); + return false; + } + } + else + { + // Operation wants to be called again. Leave it at the front of the + // queue for this descriptor, and remove from the cleanup list. + cleanup_operations_ = this_op->next_; + this_op->next_ = i->second; + i->second = this_op; + return true; + } + } + return false; + } + + // Dispatch all operations corresponding to the descriptor. + void dispatch_all_operations(Descriptor descriptor, + const asio::error_code& result) + { + typename operation_map::iterator i = operations_.find(descriptor); + if (i != operations_.end()) + { + while (i->second) + { + op_base* this_op = i->second; + i->second = this_op->next_; + this_op->next_ = cleanup_operations_; + cleanup_operations_ = this_op; + bool done = this_op->invoke(result); + if (!done) + { + // Operation has not finished yet, so leave at front of queue, and + // remove from the cleanup list. + cleanup_operations_ = this_op->next_; + this_op->next_ = i->second; + i->second = this_op; + return; + } + } + operations_.erase(i); + } + } + + // Fill a descriptor set with the descriptors corresponding to each active + // operation. + template + void get_descriptors(Descriptor_Set& descriptors) + { + typename operation_map::iterator i = operations_.begin(); + while (i != operations_.end()) + { + descriptors.set(i->first); + ++i; + } + } + + // Dispatch the operations corresponding to the ready file descriptors + // contained in the given descriptor set. + template + void dispatch_descriptors(const Descriptor_Set& descriptors, + const asio::error_code& result) + { + typename operation_map::iterator i = operations_.begin(); + while (i != operations_.end()) + { + typename operation_map::iterator op_iter = i++; + if (descriptors.is_set(op_iter->first)) + { + op_base* this_op = op_iter->second; + op_iter->second = this_op->next_; + this_op->next_ = cleanup_operations_; + cleanup_operations_ = this_op; + bool done = this_op->invoke(result); + if (done) + { + if (!op_iter->second) + operations_.erase(op_iter); + } + else + { + // Operation has not finished yet, so leave at front of queue, and + // remove from the cleanup list. + cleanup_operations_ = this_op->next_; + this_op->next_ = op_iter->second; + op_iter->second = this_op; + } + } + } + } + + // Dispatch any pending cancels for operations. + void dispatch_cancellations() + { + while (cancelled_operations_) + { + op_base* this_op = cancelled_operations_; + cancelled_operations_ = this_op->next_; + this_op->next_ = cleanup_operations_; + cleanup_operations_ = this_op; + this_op->invoke(asio::error::operation_aborted); + } + } + + // Destroy operations that are waiting to be cleaned up. + void cleanup_operations() + { + while (cleanup_operations_) + { + op_base* next_op = cleanup_operations_->next_; + cleanup_operations_->next_ = 0; + cleanup_operations_->destroy(); + cleanup_operations_ = next_op; + } + } + + // Destroy all operations owned by the queue. + void destroy_operations() + { + while (cancelled_operations_) + { + op_base* next_op = cancelled_operations_->next_; + cancelled_operations_->next_ = 0; + cancelled_operations_->destroy(); + cancelled_operations_ = next_op; + } + + while (cleanup_operations_) + { + op_base* next_op = cleanup_operations_->next_; + cleanup_operations_->next_ = 0; + cleanup_operations_->destroy(); + cleanup_operations_ = next_op; + } + + typename operation_map::iterator i = operations_.begin(); + while (i != operations_.end()) + { + typename operation_map::iterator op_iter = i++; + op_base* curr_op = op_iter->second; + operations_.erase(op_iter); + while (curr_op) + { + op_base* next_op = curr_op->next_; + curr_op->next_ = 0; + curr_op->destroy(); + curr_op = next_op; + } + } + } + +private: + // Base class for reactor operations. A function pointer is used instead of + // virtual functions to avoid the associated overhead. + class op_base + { + public: + // Get the descriptor associated with the operation. + Descriptor descriptor() const + { + return descriptor_; + } + + // Perform the operation. + bool invoke(const asio::error_code& result) + { + return invoke_func_(this, result); + } + + // Destroy the operation. + void destroy() + { + return destroy_func_(this); + } + + protected: + typedef bool (*invoke_func_type)(op_base*, + const asio::error_code&); + typedef void (*destroy_func_type)(op_base*); + + // Construct an operation for the given descriptor. + op_base(invoke_func_type invoke_func, + destroy_func_type destroy_func, Descriptor descriptor) + : invoke_func_(invoke_func), + destroy_func_(destroy_func), + descriptor_(descriptor), + next_(0) + { + } + + // Prevent deletion through this type. + ~op_base() + { + } + + private: + friend class reactor_op_queue; + + // The function to be called to dispatch the handler. + invoke_func_type invoke_func_; + + // The function to be called to delete the handler. + destroy_func_type destroy_func_; + + // The descriptor associated with the operation. + Descriptor descriptor_; + + // The next operation for the same file descriptor. + op_base* next_; + }; + + // Adaptor class template for using handlers in operations. + template + class op + : public op_base + { + public: + // Constructor. + op(Descriptor descriptor, Handler handler) + : op_base(&op::invoke_handler, + &op::destroy_handler, descriptor), + handler_(handler) + { + } + + // Invoke the handler. + static bool invoke_handler(op_base* base, + const asio::error_code& result) + { + return static_cast*>(base)->handler_(result); + } + + // Delete the handler. + static void destroy_handler(op_base* base) + { + delete static_cast*>(base); + } + + private: + Handler handler_; + }; + + // The type for a map of operations. + typedef hash_map operation_map; + + // The operations that are currently executing asynchronously. + operation_map operations_; + + // The list of operations that have been cancelled. + op_base* cancelled_operations_; + + // The list of operations to be destroyed. + op_base* cleanup_operations_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_REACTOR_OP_QUEUE_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/detail/resolver_service.hpp b/encryption/libtorrent/include/libtorrent/asio/detail/resolver_service.hpp new file mode 100644 index 000000000..c820b75f3 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/detail/resolver_service.hpp @@ -0,0 +1,357 @@ +// +// resolver_service.hpp +// ~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_RESOLVER_SERVICE_HPP +#define ASIO_DETAIL_RESOLVER_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/error.hpp" +#include "asio/io_service.hpp" +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/mutex.hpp" +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/service_base.hpp" +#include "asio/detail/socket_ops.hpp" +#include "asio/detail/socket_types.hpp" +#include "asio/detail/thread.hpp" + +namespace asio { +namespace detail { + +template +class resolver_service + : public asio::detail::service_base > +{ +private: + // Helper class to perform exception-safe cleanup of addrinfo objects. + class auto_addrinfo + : private asio::detail::noncopyable + { + public: + explicit auto_addrinfo(asio::detail::addrinfo_type* ai) + : ai_(ai) + { + } + + ~auto_addrinfo() + { + if (ai_) + socket_ops::freeaddrinfo(ai_); + } + + operator asio::detail::addrinfo_type*() + { + return ai_; + } + + private: + asio::detail::addrinfo_type* ai_; + }; + +public: + // The implementation type of the resolver. The shared pointer is used as a + // cancellation token to indicate to the background thread that the operation + // has been cancelled. + typedef boost::shared_ptr implementation_type; + struct noop_deleter { void operator()(void*) {} }; + + // The endpoint type. + typedef typename Protocol::endpoint endpoint_type; + + // The query type. + typedef typename Protocol::resolver_query query_type; + + // The iterator type. + typedef typename Protocol::resolver_iterator iterator_type; + + // Constructor. + resolver_service(asio::io_service& io_service) + : asio::detail::service_base< + resolver_service >(io_service), + mutex_(), + work_io_service_(new asio::io_service), + work_(new asio::io_service::work(*work_io_service_)), + work_thread_(0) + { + } + + // Destructor. + ~resolver_service() + { + shutdown_service(); + } + + // Destroy all user-defined handler objects owned by the service. + void shutdown_service() + { + work_.reset(); + if (work_io_service_) + { + work_io_service_->stop(); + if (work_thread_) + { + work_thread_->join(); + work_thread_.reset(); + } + work_io_service_.reset(); + } + } + + // Construct a new resolver implementation. + void construct(implementation_type& impl) + { + impl.reset(static_cast(0), noop_deleter()); + } + + // Destroy a resolver implementation. + void destroy(implementation_type&) + { + } + + // Cancel pending asynchronous operations. + void cancel(implementation_type& impl) + { + impl.reset(static_cast(0), noop_deleter()); + } + + // Resolve a query to a list of entries. + iterator_type resolve(implementation_type&, const query_type& query, + asio::error_code& ec) + { + asio::detail::addrinfo_type* address_info = 0; + std::string host_name = query.host_name(); + std::string service_name = query.service_name(); + asio::detail::addrinfo_type hints = query.hints(); + + socket_ops::getaddrinfo(host_name.length() ? host_name.c_str() : 0, + service_name.c_str(), &hints, &address_info, ec); + auto_addrinfo auto_address_info(address_info); + + if (ec) + return iterator_type(); + + return iterator_type::create(address_info, host_name, service_name); + } + + template + class resolve_query_handler + { + public: + resolve_query_handler(implementation_type impl, const query_type& query, + asio::io_service& io_service, Handler handler) + : impl_(impl), + query_(query), + io_service_(io_service), + work_(io_service), + handler_(handler) + { + } + + void operator()() + { + // Check if the operation has been cancelled. + if (impl_.expired()) + { + iterator_type iterator; + io_service_.post(asio::detail::bind_handler(handler_, + asio::error::operation_aborted, iterator)); + return; + } + + // Perform the blocking host resolution operation. + asio::detail::addrinfo_type* address_info = 0; + std::string host_name = query_.host_name(); + std::string service_name = query_.service_name(); + asio::detail::addrinfo_type hints = query_.hints(); + asio::error_code ec; + socket_ops::getaddrinfo(host_name.length() ? host_name.c_str() : 0, + service_name.c_str(), &hints, &address_info, ec); + auto_addrinfo auto_address_info(address_info); + + // Invoke the handler and pass the result. + iterator_type iterator; + if (!ec) + iterator = iterator_type::create(address_info, host_name, service_name); + io_service_.post(asio::detail::bind_handler( + handler_, ec, iterator)); + } + + private: + boost::weak_ptr impl_; + query_type query_; + asio::io_service& io_service_; + asio::io_service::work work_; + Handler handler_; + }; + + // Asynchronously resolve a query to a list of entries. + template + void async_resolve(implementation_type& impl, const query_type& query, + Handler handler) + { + if (work_io_service_) + { + start_work_thread(); + work_io_service_->post( + resolve_query_handler( + impl, query, this->io_service(), handler)); + } + } + + // Resolve an endpoint to a list of entries. + iterator_type resolve(implementation_type&, + const endpoint_type& endpoint, asio::error_code& ec) + { + // First try resolving with the service name. If that fails try resolving + // but allow the service to be returned as a number. + char host_name[NI_MAXHOST]; + char service_name[NI_MAXSERV]; + int flags = endpoint.protocol().type() == SOCK_DGRAM ? NI_DGRAM : 0; + socket_ops::getnameinfo(endpoint.data(), endpoint.size(), + host_name, NI_MAXHOST, service_name, NI_MAXSERV, flags, ec); + if (ec) + { + flags |= NI_NUMERICSERV; + socket_ops::getnameinfo(endpoint.data(), endpoint.size(), + host_name, NI_MAXHOST, service_name, NI_MAXSERV, flags, ec); + } + + if (ec) + return iterator_type(); + + return iterator_type::create(endpoint, host_name, service_name); + } + + template + class resolve_endpoint_handler + { + public: + resolve_endpoint_handler(implementation_type impl, + const endpoint_type& endpoint, asio::io_service& io_service, + Handler handler) + : impl_(impl), + endpoint_(endpoint), + io_service_(io_service), + work_(io_service), + handler_(handler) + { + } + + void operator()() + { + // Check if the operation has been cancelled. + if (impl_.expired()) + { + iterator_type iterator; + io_service_.post(asio::detail::bind_handler(handler_, + asio::error::operation_aborted, iterator)); + return; + } + + + // First try resolving with the service name. If that fails try resolving + // but allow the service to be returned as a number. + char host_name[NI_MAXHOST]; + char service_name[NI_MAXSERV]; + int flags = endpoint_.protocol().type() == SOCK_DGRAM ? NI_DGRAM : 0; + asio::error_code ec; + socket_ops::getnameinfo(endpoint_.data(), endpoint_.size(), + host_name, NI_MAXHOST, service_name, NI_MAXSERV, flags, ec); + if (ec) + { + flags |= NI_NUMERICSERV; + socket_ops::getnameinfo(endpoint_.data(), endpoint_.size(), + host_name, NI_MAXHOST, service_name, NI_MAXSERV, flags, ec); + } + + // Invoke the handler and pass the result. + iterator_type iterator; + if (!ec) + iterator = iterator_type::create(endpoint_, host_name, service_name); + io_service_.post(asio::detail::bind_handler( + handler_, ec, iterator)); + } + + private: + boost::weak_ptr impl_; + endpoint_type endpoint_; + asio::io_service& io_service_; + asio::io_service::work work_; + Handler handler_; + }; + + // Asynchronously resolve an endpoint to a list of entries. + template + void async_resolve(implementation_type& impl, const endpoint_type& endpoint, + Handler handler) + { + if (work_io_service_) + { + start_work_thread(); + work_io_service_->post( + resolve_endpoint_handler( + impl, endpoint, this->io_service(), handler)); + } + } + +private: + // Helper class to run the work io_service in a thread. + class work_io_service_runner + { + public: + work_io_service_runner(asio::io_service& io_service) + : io_service_(io_service) {} + void operator()() { io_service_.run(); } + private: + asio::io_service& io_service_; + }; + + // Start the work thread if it's not already running. + void start_work_thread() + { + asio::detail::mutex::scoped_lock lock(mutex_); + if (work_thread_ == 0) + { + work_thread_.reset(new asio::detail::thread( + work_io_service_runner(*work_io_service_))); + } + } + + // Mutex to protect access to internal data. + asio::detail::mutex mutex_; + + // Private io_service used for performing asynchronous host resolution. + boost::scoped_ptr work_io_service_; + + // Work for the private io_service to perform. + boost::scoped_ptr work_; + + // Thread used for running the work io_service's run loop. + boost::scoped_ptr work_thread_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_RESOLVER_SERVICE_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/detail/scoped_lock.hpp b/encryption/libtorrent/include/libtorrent/asio/detail/scoped_lock.hpp new file mode 100644 index 000000000..64c77cbab --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/detail/scoped_lock.hpp @@ -0,0 +1,79 @@ +// +// scoped_lock.hpp +// ~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_SCOPED_LOCK_HPP +#define ASIO_DETAIL_SCOPED_LOCK_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/noncopyable.hpp" + +namespace asio { +namespace detail { + +// Helper class to lock and unlock a mutex automatically. +template +class scoped_lock + : private noncopyable +{ +public: + // Constructor acquires the lock. + scoped_lock(Mutex& m) + : mutex_(m) + { + mutex_.lock(); + locked_ = true; + } + + // Destructor releases the lock. + ~scoped_lock() + { + if (locked_) + mutex_.unlock(); + } + + // Explicitly acquire the lock. + void lock() + { + if (!locked_) + { + mutex_.lock(); + locked_ = true; + } + } + + // Explicitly release the lock. + void unlock() + { + if (locked_) + { + mutex_.unlock(); + locked_ = false; + } + } + +private: + // The underlying mutex. + Mutex& mutex_; + + // Whether the mutex is currently locked or unlocked. + bool locked_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_SCOPED_LOCK_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/detail/select_interrupter.hpp b/encryption/libtorrent/include/libtorrent/asio/detail/select_interrupter.hpp new file mode 100644 index 000000000..de7fc9611 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/detail/select_interrupter.hpp @@ -0,0 +1,41 @@ +// +// select_interrupter.hpp +// ~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_SELECT_INTERRUPTER_HPP +#define ASIO_DETAIL_SELECT_INTERRUPTER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/detail/pipe_select_interrupter.hpp" +#include "asio/detail/socket_select_interrupter.hpp" + +namespace asio { +namespace detail { + +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) +typedef socket_select_interrupter select_interrupter; +#else +typedef pipe_select_interrupter select_interrupter; +#endif + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_SELECT_INTERRUPTER_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/detail/select_reactor.hpp b/encryption/libtorrent/include/libtorrent/asio/detail/select_reactor.hpp new file mode 100644 index 000000000..83f093ae6 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/detail/select_reactor.hpp @@ -0,0 +1,456 @@ +// +// select_reactor.hpp +// ~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_SELECT_REACTOR_HPP +#define ASIO_DETAIL_SELECT_REACTOR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/socket_types.hpp" // Must come before posix_time. + +#include "asio/detail/push_options.hpp" +#include +#include +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/io_service.hpp" +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/fd_set_adapter.hpp" +#include "asio/detail/mutex.hpp" +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/reactor_op_queue.hpp" +#include "asio/detail/select_interrupter.hpp" +#include "asio/detail/select_reactor_fwd.hpp" +#include "asio/detail/service_base.hpp" +#include "asio/detail/signal_blocker.hpp" +#include "asio/detail/socket_ops.hpp" +#include "asio/detail/socket_types.hpp" +#include "asio/detail/task_io_service.hpp" +#include "asio/detail/thread.hpp" +#include "asio/detail/timer_queue.hpp" + +namespace asio { +namespace detail { + +template +class select_reactor + : public asio::detail::service_base > +{ +public: + // Constructor. + select_reactor(asio::io_service& io_service) + : asio::detail::service_base< + select_reactor >(io_service), + mutex_(), + select_in_progress_(false), + interrupter_(), + read_op_queue_(), + write_op_queue_(), + except_op_queue_(), + pending_cancellations_(), + stop_thread_(false), + thread_(0), + shutdown_(false) + { + if (Own_Thread) + { + asio::detail::signal_blocker sb; + thread_ = new asio::detail::thread( + bind_handler(&select_reactor::call_run_thread, this)); + } + } + + // Destructor. + ~select_reactor() + { + shutdown_service(); + } + + // Destroy all user-defined handler objects owned by the service. + void shutdown_service() + { + asio::detail::mutex::scoped_lock lock(mutex_); + shutdown_ = true; + stop_thread_ = true; + lock.unlock(); + + if (thread_) + { + interrupter_.interrupt(); + thread_->join(); + delete thread_; + thread_ = 0; + } + + read_op_queue_.destroy_operations(); + write_op_queue_.destroy_operations(); + except_op_queue_.destroy_operations(); + + for (std::size_t i = 0; i < timer_queues_.size(); ++i) + timer_queues_[i]->destroy_timers(); + timer_queues_.clear(); + } + + // Register a socket with the reactor. Returns 0 on success, system error + // code on failure. + int register_descriptor(socket_type descriptor) + { + return 0; + } + + // Start a new read operation. The handler object will be invoked when the + // given descriptor is ready to be read, or an error has occurred. + template + void start_read_op(socket_type descriptor, Handler handler) + { + asio::detail::mutex::scoped_lock lock(mutex_); + if (!shutdown_) + if (read_op_queue_.enqueue_operation(descriptor, handler)) + interrupter_.interrupt(); + } + + // Start a new write operation. The handler object will be invoked when the + // given descriptor is ready to be written, or an error has occurred. + template + void start_write_op(socket_type descriptor, Handler handler) + { + asio::detail::mutex::scoped_lock lock(mutex_); + if (!shutdown_) + if (write_op_queue_.enqueue_operation(descriptor, handler)) + interrupter_.interrupt(); + } + + // Start a new exception operation. The handler object will be invoked when + // the given descriptor has exception information, or an error has occurred. + template + void start_except_op(socket_type descriptor, Handler handler) + { + asio::detail::mutex::scoped_lock lock(mutex_); + if (!shutdown_) + if (except_op_queue_.enqueue_operation(descriptor, handler)) + interrupter_.interrupt(); + } + + // Start new write and exception operations. The handler object will be + // invoked when the given descriptor is ready for writing or has exception + // information available, or an error has occurred. + template + void start_write_and_except_ops(socket_type descriptor, Handler handler) + { + asio::detail::mutex::scoped_lock lock(mutex_); + if (!shutdown_) + { + bool interrupt = write_op_queue_.enqueue_operation(descriptor, handler); + interrupt = except_op_queue_.enqueue_operation(descriptor, handler) + || interrupt; + if (interrupt) + interrupter_.interrupt(); + } + } + + // Cancel all operations associated with the given descriptor. The + // handlers associated with the descriptor will be invoked with the + // operation_aborted error. + void cancel_ops(socket_type descriptor) + { + asio::detail::mutex::scoped_lock lock(mutex_); + cancel_ops_unlocked(descriptor); + } + + // Enqueue cancellation of all operations associated with the given + // descriptor. The handlers associated with the descriptor will be invoked + // with the operation_aborted error. This function does not acquire the + // select_reactor's mutex, and so should only be used from within a reactor + // handler. + void enqueue_cancel_ops_unlocked(socket_type descriptor) + { + pending_cancellations_.push_back(descriptor); + } + + // Cancel any operations that are running against the descriptor and remove + // its registration from the reactor. + void close_descriptor(socket_type descriptor) + { + asio::detail::mutex::scoped_lock lock(mutex_); + cancel_ops_unlocked(descriptor); + } + + // Add a new timer queue to the reactor. + template + void add_timer_queue(timer_queue& timer_queue) + { + asio::detail::mutex::scoped_lock lock(mutex_); + timer_queues_.push_back(&timer_queue); + } + + // Remove a timer queue from the reactor. + template + void remove_timer_queue(timer_queue& timer_queue) + { + asio::detail::mutex::scoped_lock lock(mutex_); + for (std::size_t i = 0; i < timer_queues_.size(); ++i) + { + if (timer_queues_[i] == &timer_queue) + { + timer_queues_.erase(timer_queues_.begin() + i); + return; + } + } + } + + // Schedule a timer in the given timer queue to expire at the specified + // absolute time. The handler object will be invoked when the timer expires. + template + void schedule_timer(timer_queue& timer_queue, + const typename Time_Traits::time_type& time, Handler handler, void* token) + { + asio::detail::mutex::scoped_lock lock(mutex_); + if (!shutdown_) + if (timer_queue.enqueue_timer(time, handler, token)) + interrupter_.interrupt(); + } + + // Cancel the timer associated with the given token. Returns the number of + // handlers that have been posted or dispatched. + template + std::size_t cancel_timer(timer_queue& timer_queue, void* token) + { + asio::detail::mutex::scoped_lock lock(mutex_); + return timer_queue.cancel_timer(token); + } + +private: + friend class task_io_service >; + + // Run select once until interrupted or events are ready to be dispatched. + void run(bool block) + { + asio::detail::mutex::scoped_lock lock(mutex_); + + // Dispatch any operation cancellations that were made while the select + // loop was not running. + read_op_queue_.dispatch_cancellations(); + write_op_queue_.dispatch_cancellations(); + except_op_queue_.dispatch_cancellations(); + + // Check if the thread is supposed to stop. + if (stop_thread_) + { + // Clean up operations. We must not hold the lock since the operations may + // make calls back into this reactor. + lock.unlock(); + read_op_queue_.cleanup_operations(); + write_op_queue_.cleanup_operations(); + except_op_queue_.cleanup_operations(); + return; + } + + // We can return immediately if there's no work to do and the reactor is + // not supposed to block. + if (!block && read_op_queue_.empty() && write_op_queue_.empty() + && except_op_queue_.empty() && all_timer_queues_are_empty()) + { + // Clean up operations. We must not hold the lock since the operations may + // make calls back into this reactor. + lock.unlock(); + read_op_queue_.cleanup_operations(); + write_op_queue_.cleanup_operations(); + except_op_queue_.cleanup_operations(); + return; + } + + // Set up the descriptor sets. + fd_set_adapter read_fds; + read_fds.set(interrupter_.read_descriptor()); + read_op_queue_.get_descriptors(read_fds); + fd_set_adapter write_fds; + write_op_queue_.get_descriptors(write_fds); + fd_set_adapter except_fds; + except_op_queue_.get_descriptors(except_fds); + socket_type max_fd = read_fds.max_descriptor(); + if (write_fds.max_descriptor() > max_fd) + max_fd = write_fds.max_descriptor(); + if (except_fds.max_descriptor() > max_fd) + max_fd = except_fds.max_descriptor(); + + // Block on the select call without holding the lock so that new + // operations can be started while the call is executing. + timeval tv_buf = { 0, 0 }; + timeval* tv = block ? get_timeout(tv_buf) : &tv_buf; + select_in_progress_ = true; + lock.unlock(); + asio::error_code ec; + int retval = socket_ops::select(static_cast(max_fd + 1), + read_fds, write_fds, except_fds, tv, ec); + lock.lock(); + select_in_progress_ = false; + + // Block signals while dispatching operations. + asio::detail::signal_blocker sb; + + // Reset the interrupter. + if (retval > 0 && read_fds.is_set(interrupter_.read_descriptor())) + interrupter_.reset(); + + // Dispatch all ready operations. + if (retval > 0) + { + // Exception operations must be processed first to ensure that any + // out-of-band data is read before normal data. + except_op_queue_.dispatch_descriptors(except_fds, + asio::error_code()); + read_op_queue_.dispatch_descriptors(read_fds, + asio::error_code()); + write_op_queue_.dispatch_descriptors(write_fds, + asio::error_code()); + except_op_queue_.dispatch_cancellations(); + read_op_queue_.dispatch_cancellations(); + write_op_queue_.dispatch_cancellations(); + } + for (std::size_t i = 0; i < timer_queues_.size(); ++i) + timer_queues_[i]->dispatch_timers(); + + // Issue any pending cancellations. + for (size_t i = 0; i < pending_cancellations_.size(); ++i) + cancel_ops_unlocked(pending_cancellations_[i]); + pending_cancellations_.clear(); + + // Clean up operations. We must not hold the lock since the operations may + // make calls back into this reactor. + lock.unlock(); + read_op_queue_.cleanup_operations(); + write_op_queue_.cleanup_operations(); + except_op_queue_.cleanup_operations(); + } + + // Run the select loop in the thread. + void run_thread() + { + asio::detail::mutex::scoped_lock lock(mutex_); + while (!stop_thread_) + { + lock.unlock(); + run(true); + lock.lock(); + } + } + + // Entry point for the select loop thread. + static void call_run_thread(select_reactor* reactor) + { + reactor->run_thread(); + } + + // Interrupt the select loop. + void interrupt() + { + interrupter_.interrupt(); + } + + // Check if all timer queues are empty. + bool all_timer_queues_are_empty() const + { + for (std::size_t i = 0; i < timer_queues_.size(); ++i) + if (!timer_queues_[i]->empty()) + return false; + return true; + } + + // Get the timeout value for the select call. + timeval* get_timeout(timeval& tv) + { + if (all_timer_queues_are_empty()) + return 0; + + // By default we will wait no longer than 5 minutes. This will ensure that + // any changes to the system clock are detected after no longer than this. + boost::posix_time::time_duration minimum_wait_duration + = boost::posix_time::minutes(5); + + for (std::size_t i = 0; i < timer_queues_.size(); ++i) + { + boost::posix_time::time_duration wait_duration + = timer_queues_[i]->wait_duration(); + if (wait_duration < minimum_wait_duration) + minimum_wait_duration = wait_duration; + } + + if (minimum_wait_duration > boost::posix_time::time_duration()) + { + tv.tv_sec = minimum_wait_duration.total_seconds(); + tv.tv_usec = minimum_wait_duration.total_microseconds() % 1000000; + } + else + { + tv.tv_sec = 0; + tv.tv_usec = 0; + } + + return &tv; + } + + // Cancel all operations associated with the given descriptor. The do_cancel + // function of the handler objects will be invoked. This function does not + // acquire the select_reactor's mutex. + void cancel_ops_unlocked(socket_type descriptor) + { + bool interrupt = read_op_queue_.cancel_operations(descriptor); + interrupt = write_op_queue_.cancel_operations(descriptor) || interrupt; + interrupt = except_op_queue_.cancel_operations(descriptor) || interrupt; + if (interrupt) + interrupter_.interrupt(); + } + + // Mutex to protect access to internal data. + asio::detail::mutex mutex_; + + // Whether the select loop is currently running or not. + bool select_in_progress_; + + // The interrupter is used to break a blocking select call. + select_interrupter interrupter_; + + // The queue of read operations. + reactor_op_queue read_op_queue_; + + // The queue of write operations. + reactor_op_queue write_op_queue_; + + // The queue of exception operations. + reactor_op_queue except_op_queue_; + + // The timer queues. + std::vector timer_queues_; + + // The descriptors that are pending cancellation. + std::vector pending_cancellations_; + + // Does the reactor loop thread need to stop. + bool stop_thread_; + + // The thread that is running the reactor loop. + asio::detail::thread* thread_; + + // Whether the service has been shut down. + bool shutdown_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_SELECT_REACTOR_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/detail/select_reactor_fwd.hpp b/encryption/libtorrent/include/libtorrent/asio/detail/select_reactor_fwd.hpp new file mode 100644 index 000000000..3e1687eae --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/detail/select_reactor_fwd.hpp @@ -0,0 +1,31 @@ +// +// select_reactor_fwd.hpp +// ~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_SELECT_REACTOR_FWD_HPP +#define ASIO_DETAIL_SELECT_REACTOR_FWD_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class select_reactor; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_SELECT_REACTOR_FWD_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/detail/service_base.hpp b/encryption/libtorrent/include/libtorrent/asio/detail/service_base.hpp new file mode 100644 index 000000000..4f375c921 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/detail/service_base.hpp @@ -0,0 +1,49 @@ +// +// service_base.hpp +// ~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_SERVICE_BASE_HPP +#define ASIO_DETAIL_SERVICE_BASE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/io_service.hpp" +#include "asio/detail/service_id.hpp" + +namespace asio { +namespace detail { + +// Special service base class to keep classes header-file only. +template +class service_base + : public asio::io_service::service +{ +public: + static asio::detail::service_id id; + + // Constructor. + service_base(asio::io_service& io_service) + : asio::io_service::service(io_service) + { + } +}; + +template +asio::detail::service_id service_base::id; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_SERVICE_BASE_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/detail/service_id.hpp b/encryption/libtorrent/include/libtorrent/asio/detail/service_id.hpp new file mode 100644 index 000000000..8ff19097b --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/detail/service_id.hpp @@ -0,0 +1,37 @@ +// +// service_id.hpp +// ~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_SERVICE_ID_HPP +#define ASIO_DETAIL_SERVICE_ID_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/io_service.hpp" + +namespace asio { +namespace detail { + +// Special derived service id type to keep classes header-file only. +template +class service_id + : public asio::io_service::id +{ +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_SERVICE_ID_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/detail/service_registry.hpp b/encryption/libtorrent/include/libtorrent/asio/detail/service_registry.hpp new file mode 100644 index 000000000..bd1c3ea5b --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/detail/service_registry.hpp @@ -0,0 +1,198 @@ +// +// service_registry.hpp +// ~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_SERVICE_REGISTRY_HPP +#define ASIO_DETAIL_SERVICE_REGISTRY_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/io_service.hpp" +#include "asio/detail/mutex.hpp" +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/service_id.hpp" + +namespace asio { +namespace detail { + +class service_registry + : private noncopyable +{ +public: + // Constructor. + service_registry(asio::io_service& o) + : owner_(o), + first_service_(0) + { + } + + // Destructor. + ~service_registry() + { + // Shutdown all services. This must be done in a separate loop before the + // services are destroyed since the destructors of user-defined handler + // objects may try to access other service objects. + asio::io_service::service* service = first_service_; + while (service) + { + service->shutdown_service(); + service = service->next_; + } + + // Destroy all services. + while (first_service_) + { + asio::io_service::service* next_service = first_service_->next_; + delete first_service_; + first_service_ = next_service; + } + } + + // Get the service object corresponding to the specified service type. Will + // create a new service object automatically if no such object already + // exists. Ownership of the service object is not transferred to the caller. + template + Service& use_service() + { + asio::detail::mutex::scoped_lock lock(mutex_); + + // First see if there is an existing service object for the given type. + asio::io_service::service* service = first_service_; + while (service) + { + if (service_id_matches(*service, Service::id)) + return *static_cast(service); + service = service->next_; + } + + // Create a new service object. The service registry's mutex is not locked + // at this time to allow for nested calls into this function from the new + // service's constructor. + lock.unlock(); + std::auto_ptr new_service(new Service(owner_)); + init_service_id(*new_service, Service::id); + Service& new_service_ref = *new_service; + lock.lock(); + + // Check that nobody else created another service object of the same type + // while the lock was released. + service = first_service_; + while (service) + { + if (service_id_matches(*service, Service::id)) + return *static_cast(service); + service = service->next_; + } + + // Service was successfully initialised, pass ownership to registry. + new_service->next_ = first_service_; + first_service_ = new_service.release(); + + return new_service_ref; + } + + // Add a service object. Returns false on error, in which case ownership of + // the object is retained by the caller. + template + bool add_service(Service* new_service) + { + asio::detail::mutex::scoped_lock lock(mutex_); + + // Check if there is an existing service object for the given type. + asio::io_service::service* service = first_service_; + while (service) + { + if (service_id_matches(*service, Service::id)) + return false; + service = service->next_; + } + + // Take ownership of the service object. + init_service_id(*new_service, Service::id); + new_service->next_ = first_service_; + first_service_ = new_service; + + return true; + } + + // Check whether a service object of the specified type already exists. + template + bool has_service() const + { + asio::detail::mutex::scoped_lock lock(mutex_); + + asio::io_service::service* service = first_service_; + while (service) + { + if (service_id_matches(*service, Service::id)) + return true; + service = service->next_; + } + + return false; + } + +private: + // Set a service's id. + void init_service_id(asio::io_service::service& service, + const asio::io_service::id& id) + { + service.type_info_ = 0; + service.id_ = &id; + } + + // Set a service's id. + template + void init_service_id(asio::io_service::service& service, + const asio::detail::service_id& /*id*/) + { + service.type_info_ = &typeid(Service); + service.id_ = 0; + } + + // Check if a service matches the given id. + bool service_id_matches(const asio::io_service::service& service, + const asio::io_service::id& id) + { + return service.id_ == &id; + } + + // Check if a service matches the given id. + template + bool service_id_matches(const asio::io_service::service& service, + const asio::detail::service_id& /*id*/) + { + return service.type_info_ != 0 && *service.type_info_ == typeid(Service); + } + + // Mutex to protect access to internal data. + mutable asio::detail::mutex mutex_; + + // The owner of this service registry and the services it contains. + asio::io_service& owner_; + + // The first service in the list of contained services. + asio::io_service::service* first_service_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_SERVICE_REGISTRY_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/detail/service_registry_fwd.hpp b/encryption/libtorrent/include/libtorrent/asio/detail/service_registry_fwd.hpp new file mode 100644 index 000000000..596c24be3 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/detail/service_registry_fwd.hpp @@ -0,0 +1,30 @@ +// +// service_registry_fwd.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_SERVICE_REGISTRY_FWD_HPP +#define ASIO_DETAIL_SERVICE_REGISTRY_FWD_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class service_registry; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_SERVICE_REGISTRY_FWD_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/detail/signal_blocker.hpp b/encryption/libtorrent/include/libtorrent/asio/detail/signal_blocker.hpp new file mode 100644 index 000000000..3eb3ba495 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/detail/signal_blocker.hpp @@ -0,0 +1,50 @@ +// +// signal_blocker.hpp +// ~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_SIGNAL_BLOCKER_HPP +#define ASIO_DETAIL_SIGNAL_BLOCKER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include "asio/detail/pop_options.hpp" + +#if !defined(BOOST_HAS_THREADS) +# include "asio/detail/null_signal_blocker.hpp" +#elif defined(BOOST_WINDOWS) || defined(__CYGWIN__) +# include "asio/detail/win_signal_blocker.hpp" +#elif defined(BOOST_HAS_PTHREADS) +# include "asio/detail/posix_signal_blocker.hpp" +#else +# error Only Windows and POSIX are supported! +#endif + +namespace asio { +namespace detail { + +#if !defined(BOOST_HAS_THREADS) +typedef null_signal_blocker signal_blocker; +#elif defined(BOOST_WINDOWS) || defined(__CYGWIN__) +typedef win_signal_blocker signal_blocker; +#elif defined(BOOST_HAS_PTHREADS) +typedef posix_signal_blocker signal_blocker; +#endif + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_SIGNAL_BLOCKER_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/detail/signal_init.hpp b/encryption/libtorrent/include/libtorrent/asio/detail/signal_init.hpp new file mode 100644 index 000000000..3df395dc3 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/detail/signal_init.hpp @@ -0,0 +1,51 @@ +// +// signal_init.hpp +// ~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_SIGNAL_INIT_HPP +#define ASIO_DETAIL_SIGNAL_INIT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include "asio/detail/pop_options.hpp" + +#if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) + +#include "asio/detail/push_options.hpp" +#include +#include "asio/detail/pop_options.hpp" + +namespace asio { +namespace detail { + +template +class signal_init +{ +public: + // Constructor. + signal_init() + { + std::signal(Signal, SIG_IGN); + } +}; + +} // namespace detail +} // namespace asio + +#endif // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_SIGNAL_INIT_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/detail/socket_holder.hpp b/encryption/libtorrent/include/libtorrent/asio/detail/socket_holder.hpp new file mode 100644 index 000000000..e45f4fd61 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/detail/socket_holder.hpp @@ -0,0 +1,95 @@ +// +// socket_holder.hpp +// ~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_SOCKET_HOLDER_HPP +#define ASIO_DETAIL_SOCKET_HOLDER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/socket_ops.hpp" + +namespace asio { +namespace detail { + +// Implement the resource acquisition is initialisation idiom for sockets. +class socket_holder + : private noncopyable +{ +public: + // Construct as an uninitialised socket. + socket_holder() + : socket_(invalid_socket) + { + } + + // Construct to take ownership of the specified socket. + explicit socket_holder(socket_type s) + : socket_(s) + { + } + + // Destructor. + ~socket_holder() + { + if (socket_ != invalid_socket) + { + asio::error_code ec; + socket_ops::close(socket_, ec); + } + } + + // Get the underlying socket. + socket_type get() const + { + return socket_; + } + + // Reset to an uninitialised socket. + void reset() + { + if (socket_ != invalid_socket) + { + asio::error_code ec; + socket_ops::close(socket_, ec); + socket_ = invalid_socket; + } + } + + // Reset to take ownership of the specified socket. + void reset(socket_type s) + { + reset(); + socket_ = s; + } + + // Release ownership of the socket. + socket_type release() + { + socket_type tmp = socket_; + socket_ = invalid_socket; + return tmp; + } + +private: + // The underlying socket. + socket_type socket_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_SOCKET_HOLDER_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/detail/socket_ops.hpp b/encryption/libtorrent/include/libtorrent/asio/detail/socket_ops.hpp new file mode 100644 index 000000000..166319bb1 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/detail/socket_ops.hpp @@ -0,0 +1,1592 @@ +// +// socket_ops.hpp +// ~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_SOCKET_OPS_HPP +#define ASIO_DETAIL_SOCKET_OPS_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#if defined(__MACH__) && defined(__APPLE__) +# include +#endif // defined(__MACH__) && defined(__APPLE__) +#include "asio/detail/pop_options.hpp" + +#include "asio/error.hpp" +#include "asio/detail/socket_types.hpp" + +namespace asio { +namespace detail { +namespace socket_ops { + +inline void clear_error(asio::error_code& ec) +{ + errno = 0; +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + WSASetLastError(0); +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + ec = asio::error_code(); +} + +template +inline ReturnType error_wrapper(ReturnType return_value, + asio::error_code& ec) +{ +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + ec = asio::error_code(WSAGetLastError(), asio::native_ecat); +#else + ec = asio::error_code(errno, asio::native_ecat); +#endif + return return_value; +} + +inline socket_type accept(socket_type s, socket_addr_type* addr, + socket_addr_len_type* addrlen, asio::error_code& ec) +{ + clear_error(ec); +#if defined(__MACH__) && defined(__APPLE__) + socket_type new_s = error_wrapper(::accept(s, addr, addrlen), ec); + if (new_s == invalid_socket) + return new_s; + + int optval = 1; + int result = error_wrapper(::setsockopt(new_s, + SOL_SOCKET, SO_NOSIGPIPE, &optval, sizeof(optval)), ec); + if (result != 0) + { + ::close(new_s); + return invalid_socket; + } + + return new_s; +#else + return error_wrapper(::accept(s, addr, addrlen), ec); +#endif +} + +inline int bind(socket_type s, const socket_addr_type* addr, + socket_addr_len_type addrlen, asio::error_code& ec) +{ + clear_error(ec); + return error_wrapper(::bind(s, addr, addrlen), ec); +} + +inline int close(socket_type s, asio::error_code& ec) +{ + clear_error(ec); +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + return error_wrapper(::closesocket(s), ec); +#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + return error_wrapper(::close(s), ec); +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) +} + +inline int shutdown(socket_type s, int what, asio::error_code& ec) +{ + clear_error(ec); + return error_wrapper(::shutdown(s, what), ec); +} + +inline int connect(socket_type s, const socket_addr_type* addr, + socket_addr_len_type addrlen, asio::error_code& ec) +{ + clear_error(ec); + return error_wrapper(::connect(s, addr, addrlen), ec); +} + +inline int listen(socket_type s, int backlog, asio::error_code& ec) +{ + clear_error(ec); + return error_wrapper(::listen(s, backlog), ec); +} + +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) +typedef WSABUF buf; +#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__) +typedef iovec buf; +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + +inline void init_buf(buf& b, void* data, size_t size) +{ +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + b.buf = static_cast(data); + b.len = static_cast(size); +#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + b.iov_base = data; + b.iov_len = size; +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) +} + +inline void init_buf(buf& b, const void* data, size_t size) +{ +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + b.buf = static_cast(const_cast(data)); + b.len = static_cast(size); +#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + b.iov_base = const_cast(data); + b.iov_len = size; +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) +} + +inline int recv(socket_type s, buf* bufs, size_t count, int flags, + asio::error_code& ec) +{ + clear_error(ec); +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + // Receive some data. + DWORD recv_buf_count = static_cast(count); + DWORD bytes_transferred = 0; + DWORD recv_flags = flags; + int result = error_wrapper(::WSARecv(s, bufs, + recv_buf_count, &bytes_transferred, &recv_flags, 0, 0), ec); + if (result != 0) + return -1; + return bytes_transferred; +#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + msghdr msg; + msg.msg_name = 0; + msg.msg_namelen = 0; + msg.msg_iov = bufs; + msg.msg_iovlen = count; + msg.msg_control = 0; + msg.msg_controllen = 0; + msg.msg_flags = 0; + return error_wrapper(::recvmsg(s, &msg, flags), ec); +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) +} + +inline int recvfrom(socket_type s, buf* bufs, size_t count, int flags, + socket_addr_type* addr, socket_addr_len_type* addrlen, + asio::error_code& ec) +{ + clear_error(ec); +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + // Receive some data. + DWORD recv_buf_count = static_cast(count); + DWORD bytes_transferred = 0; + DWORD recv_flags = flags; + int result = error_wrapper(::WSARecvFrom(s, bufs, recv_buf_count, + &bytes_transferred, &recv_flags, addr, addrlen, 0, 0), ec); + if (result != 0) + return -1; + return bytes_transferred; +#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + msghdr msg; +#if defined(__MACH__) && defined(__APPLE__) \ + && (MAC_OS_X_VERSION_MAX_ALLOWED < 1040) + msg.msg_name = reinterpret_cast(addr); +#else + msg.msg_name = addr; +#endif + msg.msg_namelen = *addrlen; + msg.msg_iov = bufs; + msg.msg_iovlen = count; + msg.msg_control = 0; + msg.msg_controllen = 0; + msg.msg_flags = 0; + int result = error_wrapper(::recvmsg(s, &msg, flags), ec); + *addrlen = msg.msg_namelen; + return result; +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) +} + +inline int send(socket_type s, const buf* bufs, size_t count, int flags, + asio::error_code& ec) +{ + clear_error(ec); +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + // Send the data. + DWORD send_buf_count = static_cast(count); + DWORD bytes_transferred = 0; + DWORD send_flags = flags; + int result = error_wrapper(::WSASend(s, const_cast(bufs), + send_buf_count, &bytes_transferred, send_flags, 0, 0), ec); + if (result != 0) + return -1; + return bytes_transferred; +#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + msghdr msg; + msg.msg_name = 0; + msg.msg_namelen = 0; + msg.msg_iov = const_cast(bufs); + msg.msg_iovlen = count; + msg.msg_control = 0; + msg.msg_controllen = 0; + msg.msg_flags = 0; +#if defined(__linux__) + flags |= MSG_NOSIGNAL; +#endif // defined(__linux__) + return error_wrapper(::sendmsg(s, &msg, flags), ec); +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) +} + +inline int sendto(socket_type s, const buf* bufs, size_t count, int flags, + const socket_addr_type* addr, socket_addr_len_type addrlen, + asio::error_code& ec) +{ + clear_error(ec); +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + // Send the data. + DWORD send_buf_count = static_cast(count); + DWORD bytes_transferred = 0; + int result = error_wrapper(::WSASendTo(s, const_cast(bufs), + send_buf_count, &bytes_transferred, flags, addr, addrlen, 0, 0), ec); + if (result != 0) + return -1; + return bytes_transferred; +#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + msghdr msg; +#if defined(__MACH__) && defined(__APPLE__) \ + && (MAC_OS_X_VERSION_MAX_ALLOWED < 1040) + msg.msg_name = reinterpret_cast(const_cast(addr)); +#else + msg.msg_name = const_cast(addr); +#endif + msg.msg_namelen = addrlen; + msg.msg_iov = const_cast(bufs); + msg.msg_iovlen = count; + msg.msg_control = 0; + msg.msg_controllen = 0; + msg.msg_flags = 0; +#if defined(__linux__) + flags |= MSG_NOSIGNAL; +#endif // defined(__linux__) + return error_wrapper(::sendmsg(s, &msg, flags), ec); +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) +} + +inline socket_type socket(int af, int type, int protocol, + asio::error_code& ec) +{ + clear_error(ec); +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + socket_type s = error_wrapper(::WSASocket(af, type, protocol, 0, 0, + WSA_FLAG_OVERLAPPED), ec); + if (s == invalid_socket) + return s; + + if (af == AF_INET6) + { + // Try to enable the POSIX default behaviour of having IPV6_V6ONLY set to + // false. This will only succeed on Windows Vista and later versions of + // Windows, where a dual-stack IPv4/v6 implementation is available. + DWORD optval = 0; + ::setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, + reinterpret_cast(&optval), sizeof(optval)); + } + + return s; +#elif defined(__MACH__) && defined(__APPLE__) + socket_type s = error_wrapper(::socket(af, type, protocol), ec); + if (s == invalid_socket) + return s; + + int optval = 1; + int result = error_wrapper(::setsockopt(s, + SOL_SOCKET, SO_NOSIGPIPE, &optval, sizeof(optval)), ec); + if (result != 0) + { + ::close(s); + return invalid_socket; + } + + return s; +#else + return error_wrapper(::socket(af, type, protocol), ec); +#endif +} + +inline int setsockopt(socket_type s, int level, int optname, + const void* optval, size_t optlen, asio::error_code& ec) +{ + clear_error(ec); +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + return error_wrapper(::setsockopt(s, level, optname, + reinterpret_cast(optval), static_cast(optlen)), ec); +#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + return error_wrapper(::setsockopt(s, level, optname, optval, + static_cast(optlen)), ec); +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) +} + +inline int getsockopt(socket_type s, int level, int optname, void* optval, + size_t* optlen, asio::error_code& ec) +{ + clear_error(ec); +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + int tmp_optlen = static_cast(*optlen); + int result = error_wrapper(::getsockopt(s, level, optname, + reinterpret_cast(optval), &tmp_optlen), ec); + *optlen = static_cast(tmp_optlen); + if (result != 0 && level == IPPROTO_IPV6 && optname == IPV6_V6ONLY + && ec.value() == WSAENOPROTOOPT && *optlen == sizeof(DWORD)) + { + // Dual-stack IPv4/v6 sockets, and the IPV6_V6ONLY socket option, are only + // supported on Windows Vista and later. To simplify program logic we will + // fake success of getting this option and specify that the value is + // non-zero (i.e. true). This corresponds to the behavior of IPv6 sockets + // on Windows platforms pre-Vista. + *static_cast(optval) = 1; + clear_error(ec); + } + return result; +#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + socklen_t tmp_optlen = static_cast(*optlen); + int result = error_wrapper(::getsockopt(s, level, optname, + optval, &tmp_optlen), ec); + *optlen = static_cast(tmp_optlen); +#if defined(__linux__) + if (result == 0 && level == SOL_SOCKET && *optlen == sizeof(int) + && (optname == SO_SNDBUF || optname == SO_RCVBUF)) + { + // On Linux, setting SO_SNDBUF or SO_RCVBUF to N actually causes the kernel + // to set the buffer size to N*2. Linux puts additional stuff into the + // buffers so that only about half is actually available to the application. + // The retrieved value is divided by 2 here to make it appear as though the + // correct value has been set. + *static_cast(optval) /= 2; + } +#endif // defined(__linux__) + return result; +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) +} + +inline int getpeername(socket_type s, socket_addr_type* addr, + socket_addr_len_type* addrlen, asio::error_code& ec) +{ + clear_error(ec); + return error_wrapper(::getpeername(s, addr, addrlen), ec); +} + +inline int getsockname(socket_type s, socket_addr_type* addr, + socket_addr_len_type* addrlen, asio::error_code& ec) +{ + clear_error(ec); + return error_wrapper(::getsockname(s, addr, addrlen), ec); +} + +inline int ioctl(socket_type s, long cmd, ioctl_arg_type* arg, + asio::error_code& ec) +{ + clear_error(ec); +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + return error_wrapper(::ioctlsocket(s, cmd, arg), ec); +#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + return error_wrapper(::ioctl(s, cmd, arg), ec); +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) +} + +inline int select(int nfds, fd_set* readfds, fd_set* writefds, + fd_set* exceptfds, timeval* timeout, asio::error_code& ec) +{ + clear_error(ec); +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + if (!readfds && !writefds && !exceptfds && timeout) + { + DWORD milliseconds = timeout->tv_sec * 1000 + timeout->tv_usec / 1000; + if (milliseconds == 0) + milliseconds = 1; // Force context switch. + ::Sleep(milliseconds); + ec = asio::error_code(); + return 0; + } + + // The select() call allows timeout values measured in microseconds, but the + // system clock (as wrapped by boost::posix_time::microsec_clock) typically + // has a resolution of 10 milliseconds. This can lead to a spinning select + // reactor, meaning increased CPU usage, when waiting for the earliest + // scheduled timeout if it's less than 10 milliseconds away. To avoid a tight + // spin we'll use a minimum timeout of 1 millisecond. + if (timeout && timeout->tv_sec == 0 + && timeout->tv_usec > 0 && timeout->tv_usec < 1000) + timeout->tv_usec = 1000; +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + return error_wrapper(::select(nfds, readfds, + writefds, exceptfds, timeout), ec); +} + +inline int poll_read(socket_type s, asio::error_code& ec) +{ +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + FD_SET fds; + FD_ZERO(&fds); + FD_SET(s, &fds); + clear_error(ec); + return error_wrapper(::select(s, &fds, 0, 0, 0), ec); +#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + pollfd fds; + fds.fd = s; + fds.events = POLLIN; + fds.revents = 0; + clear_error(ec); + return error_wrapper(::poll(&fds, 1, -1), ec); +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) +} + +inline int poll_write(socket_type s, asio::error_code& ec) +{ +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + FD_SET fds; + FD_ZERO(&fds); + FD_SET(s, &fds); + clear_error(ec); + return error_wrapper(::select(s, 0, &fds, 0, 0), ec); +#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + pollfd fds; + fds.fd = s; + fds.events = POLLOUT; + fds.revents = 0; + clear_error(ec); + return error_wrapper(::poll(&fds, 1, -1), ec); +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) +} + +inline const char* inet_ntop(int af, const void* src, char* dest, size_t length, + unsigned long scope_id, asio::error_code& ec) +{ + clear_error(ec); +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + using namespace std; // For memcpy. + + if (af != AF_INET && af != AF_INET6) + { + ec = asio::error::address_family_not_supported; + return 0; + } + + sockaddr_storage_type address; + DWORD address_length; + if (af == AF_INET) + { + address_length = sizeof(sockaddr_in4_type); + sockaddr_in4_type* ipv4_address = + reinterpret_cast(&address); + ipv4_address->sin_family = AF_INET; + ipv4_address->sin_port = 0; + memcpy(&ipv4_address->sin_addr, src, sizeof(in4_addr_type)); + } + else // AF_INET6 + { + address_length = sizeof(sockaddr_in6_type); + sockaddr_in6_type* ipv6_address = + reinterpret_cast(&address); + ipv6_address->sin6_family = AF_INET6; + ipv6_address->sin6_port = 0; + ipv6_address->sin6_flowinfo = 0; + ipv6_address->sin6_scope_id = scope_id; + memcpy(&ipv6_address->sin6_addr, src, sizeof(in6_addr_type)); + } + + DWORD string_length = static_cast(length); + int result = error_wrapper(::WSAAddressToStringA( + reinterpret_cast(&address), + address_length, 0, dest, &string_length), ec); + + // Windows may set error code on success. + if (result != socket_error_retval) + clear_error(ec); + + // Windows may not set an error code on failure. + else if (result == socket_error_retval && !ec) + ec = asio::error::invalid_argument; + + return result == socket_error_retval ? 0 : dest; +#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + const char* result = error_wrapper(::inet_ntop(af, src, dest, length), ec); + if (result == 0 && !ec) + ec = asio::error::invalid_argument; + if (result != 0 && af == AF_INET6 && scope_id != 0) + { + using namespace std; // For strcat and sprintf. + char if_name[IF_NAMESIZE + 1] = "%"; + const in6_addr_type* ipv6_address = static_cast(src); + bool is_link_local = IN6_IS_ADDR_LINKLOCAL(ipv6_address); + if (!is_link_local || if_indextoname(scope_id, if_name + 1) == 0) + sprintf(if_name + 1, "%lu", scope_id); + strcat(dest, if_name); + } + return result; +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) +} + +inline int inet_pton(int af, const char* src, void* dest, + unsigned long* scope_id, asio::error_code& ec) +{ + clear_error(ec); +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + using namespace std; // For memcpy and strcmp. + + if (af != AF_INET && af != AF_INET6) + { + ec = asio::error::address_family_not_supported; + return -1; + } + + sockaddr_storage_type address; + int address_length = sizeof(sockaddr_storage_type); + int result = error_wrapper(::WSAStringToAddressA( + const_cast(src), af, 0, + reinterpret_cast(&address), + &address_length), ec); + + if (af == AF_INET) + { + if (result != socket_error_retval) + { + sockaddr_in4_type* ipv4_address = + reinterpret_cast(&address); + memcpy(dest, &ipv4_address->sin_addr, sizeof(in4_addr_type)); + clear_error(ec); + } + else if (strcmp(src, "255.255.255.255") == 0) + { + static_cast(dest)->s_addr = INADDR_NONE; + clear_error(ec); + } + } + else // AF_INET6 + { + if (result != socket_error_retval) + { + sockaddr_in6_type* ipv6_address = + reinterpret_cast(&address); + memcpy(dest, &ipv6_address->sin6_addr, sizeof(in6_addr_type)); + if (scope_id) + *scope_id = ipv6_address->sin6_scope_id; + clear_error(ec); + } + } + + // Windows may not set an error code on failure. + if (result == socket_error_retval && !ec) + ec = asio::error::invalid_argument; + + return result == socket_error_retval ? -1 : 1; +#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + int result = error_wrapper(::inet_pton(af, src, dest), ec); + if (result <= 0 && !ec) + ec = asio::error::invalid_argument; + if (result > 0 && af == AF_INET6 && scope_id) + { + using namespace std; // For strchr and atoi. + *scope_id = 0; + if (const char* if_name = strchr(src, '%')) + { + in6_addr_type* ipv6_address = static_cast(dest); + bool is_link_local = IN6_IS_ADDR_LINKLOCAL(ipv6_address); + if (is_link_local) + *scope_id = if_nametoindex(if_name + 1); + if (*scope_id == 0) + *scope_id = atoi(if_name + 1); + } + } + return result; +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) +} + +inline int gethostname(char* name, int namelen, asio::error_code& ec) +{ + clear_error(ec); + return error_wrapper(::gethostname(name, namelen), ec); +} + +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) \ + || defined(__MACH__) && defined(__APPLE__) + +// The following functions are only needed for emulation of getaddrinfo and +// getnameinfo. + +inline asio::error_code translate_netdb_error(int error) +{ + switch (error) + { + case 0: + return asio::error_code(); + case HOST_NOT_FOUND: + return asio::error::host_not_found; + case TRY_AGAIN: + return asio::error::host_not_found_try_again; + case NO_RECOVERY: + return asio::error::no_recovery; + case NO_DATA: + return asio::error::no_data; + default: + BOOST_ASSERT(false); + return asio::error::invalid_argument; + } +} + +inline hostent* gethostbyaddr(const char* addr, int length, int af, + hostent* result, char* buffer, int buflength, asio::error_code& ec) +{ + clear_error(ec); +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + (void)(buffer); + (void)(buflength); + hostent* retval = error_wrapper(::gethostbyaddr(addr, length, af), ec); + if (!retval) + return 0; + *result = *retval; + return retval; +#elif defined(__sun) || defined(__QNX__) + int error = 0; + hostent* retval = error_wrapper(::gethostbyaddr_r(addr, length, af, result, + buffer, buflength, &error), ec); + if (error) + ec = translate_netdb_error(error); + return retval; +#elif defined(__MACH__) && defined(__APPLE__) + (void)(buffer); + (void)(buflength); + int error = 0; + hostent* retval = error_wrapper(::getipnodebyaddr( + addr, length, af, &error), ec); + if (error) + ec = translate_netdb_error(error); + if (!retval) + return 0; + *result = *retval; + return retval; +#else + hostent* retval = 0; + int error = 0; + error_wrapper(::gethostbyaddr_r(addr, length, af, result, buffer, + buflength, &retval, &error), ec); + if (error) + ec = translate_netdb_error(error); + return retval; +#endif +} + +inline hostent* gethostbyname(const char* name, int af, struct hostent* result, + char* buffer, int buflength, int ai_flags, asio::error_code& ec) +{ + clear_error(ec); +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + (void)(buffer); + (void)(buflength); + (void)(ai_flags); + if (af != AF_INET) + { + ec = asio::error::address_family_not_supported; + return 0; + } + hostent* retval = error_wrapper(::gethostbyname(name), ec); + if (!retval) + return 0; + *result = *retval; + return result; +#elif defined(__sun) || defined(__QNX__) + (void)(ai_flags); + if (af != AF_INET) + { + ec = asio::error::address_family_not_supported; + return 0; + } + int error = 0; + hostent* retval = error_wrapper(::gethostbyname_r(name, result, buffer, + buflength, &error), ec); + if (error) + ec = translate_netdb_error(error); + return retval; +#elif defined(__MACH__) && defined(__APPLE__) + (void)(buffer); + (void)(buflength); + int error = 0; + hostent* retval = error_wrapper(::getipnodebyname( + name, af, ai_flags, &error), ec); + if (error) + ec = translate_netdb_error(error); + if (!retval) + return 0; + *result = *retval; + return retval; +#else + (void)(ai_flags); + if (af != AF_INET) + { + ec = asio::error::address_family_not_supported; + return 0; + } + hostent* retval = 0; + int error = 0; + error_wrapper(::gethostbyname_r(name, result, + buffer, buflength, &retval, &error), ec); + if (error) + ec = translate_netdb_error(error); + return retval; +#endif +} + +inline void freehostent(hostent* h) +{ +#if defined(__MACH__) && defined(__APPLE__) + if (h) + ::freehostent(h); +#else + (void)(h); +#endif +} + +// Emulation of getaddrinfo based on implementation in: +// Stevens, W. R., UNIX Network Programming Vol. 1, 2nd Ed., Prentice-Hall 1998. + +struct gai_search +{ + const char* host; + int family; +}; + +inline int gai_nsearch(const char* host, + const addrinfo_type* hints, gai_search (&search)[2]) +{ + int search_count = 0; + if (host == 0 || host[0] == '\0') + { + if (hints->ai_flags & AI_PASSIVE) + { + // No host and AI_PASSIVE implies wildcard bind. + switch (hints->ai_family) + { + case AF_INET: + search[search_count].host = "0.0.0.0"; + search[search_count].family = AF_INET; + ++search_count; + break; + case AF_INET6: + search[search_count].host = "0::0"; + search[search_count].family = AF_INET6; + ++search_count; + break; + case AF_UNSPEC: + search[search_count].host = "0::0"; + search[search_count].family = AF_INET6; + ++search_count; + search[search_count].host = "0.0.0.0"; + search[search_count].family = AF_INET; + ++search_count; + break; + default: + break; + } + } + else + { + // No host and not AI_PASSIVE means connect to local host. + switch (hints->ai_family) + { + case AF_INET: + search[search_count].host = "localhost"; + search[search_count].family = AF_INET; + ++search_count; + break; + case AF_INET6: + search[search_count].host = "localhost"; + search[search_count].family = AF_INET6; + ++search_count; + break; + case AF_UNSPEC: + search[search_count].host = "localhost"; + search[search_count].family = AF_INET6; + ++search_count; + search[search_count].host = "localhost"; + search[search_count].family = AF_INET; + ++search_count; + break; + default: + break; + } + } + } + else + { + // Host is specified. + switch (hints->ai_family) + { + case AF_INET: + search[search_count].host = host; + search[search_count].family = AF_INET; + ++search_count; + break; + case AF_INET6: + search[search_count].host = host; + search[search_count].family = AF_INET6; + ++search_count; + break; + case AF_UNSPEC: + search[search_count].host = host; + search[search_count].family = AF_INET6; + ++search_count; + search[search_count].host = host; + search[search_count].family = AF_INET; + ++search_count; + break; + default: + break; + } + } + return search_count; +} + +template +inline T* gai_alloc(std::size_t size = sizeof(T)) +{ + using namespace std; + T* p = static_cast(::operator new(size, std::nothrow)); + if (p) + memset(p, 0, size); + return p; +} + +inline void gai_free(void* p) +{ + ::operator delete(p); +} + +enum { gai_clone_flag = 1 << 30 }; + +inline int gai_aistruct(addrinfo_type*** next, const addrinfo_type* hints, + const void* addr, int family) +{ + using namespace std; + + addrinfo_type* ai = gai_alloc(); + if (ai == 0) + return EAI_MEMORY; + + ai->ai_next = 0; + **next = ai; + *next = &ai->ai_next; + + ai->ai_canonname = 0; + ai->ai_socktype = hints->ai_socktype; + if (ai->ai_socktype == 0) + ai->ai_flags |= gai_clone_flag; + ai->ai_protocol = hints->ai_protocol; + ai->ai_family = family; + + switch (ai->ai_family) + { + case AF_INET: + { + sockaddr_in4_type* sinptr = gai_alloc(); + if (sinptr == 0) + return EAI_MEMORY; + sinptr->sin_family = AF_INET; + memcpy(&sinptr->sin_addr, addr, sizeof(in4_addr_type)); + ai->ai_addr = reinterpret_cast(sinptr); + ai->ai_addrlen = sizeof(sockaddr_in4_type); + break; + } + case AF_INET6: + { + sockaddr_in6_type* sin6ptr = gai_alloc(); + if (sin6ptr == 0) + return EAI_MEMORY; + sin6ptr->sin6_family = AF_INET6; + memcpy(&sin6ptr->sin6_addr, addr, sizeof(in6_addr_type)); + ai->ai_addr = reinterpret_cast(sin6ptr); + ai->ai_addrlen = sizeof(sockaddr_in6_type); + break; + } + default: + break; + } + + return 0; +} + +inline addrinfo_type* gai_clone(addrinfo_type* ai) +{ + using namespace std; + + addrinfo_type* new_ai = gai_alloc(); + if (new_ai == 0) + return new_ai; + + new_ai->ai_next = ai->ai_next; + ai->ai_next = new_ai; + + new_ai->ai_flags = 0; + new_ai->ai_family = ai->ai_family; + new_ai->ai_socktype = ai->ai_socktype; + new_ai->ai_protocol = ai->ai_protocol; + new_ai->ai_canonname = 0; + new_ai->ai_addrlen = ai->ai_addrlen; + new_ai->ai_addr = gai_alloc(ai->ai_addrlen); + memcpy(new_ai->ai_addr, ai->ai_addr, ai->ai_addrlen); + + return new_ai; +} + +inline int gai_port(addrinfo_type* aihead, int port, int socktype) +{ + int num_found = 0; + + for (addrinfo_type* ai = aihead; ai; ai = ai->ai_next) + { + if (ai->ai_flags & gai_clone_flag) + { + if (ai->ai_socktype != 0) + { + ai = gai_clone(ai); + if (ai == 0) + return -1; + // ai now points to newly cloned entry. + } + } + else if (ai->ai_socktype != socktype) + { + // Ignore if mismatch on socket type. + continue; + } + + ai->ai_socktype = socktype; + + switch (ai->ai_family) + { + case AF_INET: + { + sockaddr_in4_type* sinptr = + reinterpret_cast(ai->ai_addr); + sinptr->sin_port = port; + ++num_found; + break; + } + case AF_INET6: + { + sockaddr_in6_type* sin6ptr = + reinterpret_cast(ai->ai_addr); + sin6ptr->sin6_port = port; + ++num_found; + break; + } + default: + break; + } + } + + return num_found; +} + +inline int gai_serv(addrinfo_type* aihead, + const addrinfo_type* hints, const char* serv) +{ + using namespace std; + + int num_found = 0; + + if ( +#if defined(AI_NUMERICSERV) + (hints->ai_flags & AI_NUMERICSERV) || +#endif + isdigit(serv[0])) + { + int port = htons(atoi(serv)); + if (hints->ai_socktype) + { + // Caller specifies socket type. + int rc = gai_port(aihead, port, hints->ai_socktype); + if (rc < 0) + return EAI_MEMORY; + num_found += rc; + } + else + { + // Caller does not specify socket type. + int rc = gai_port(aihead, port, SOCK_STREAM); + if (rc < 0) + return EAI_MEMORY; + num_found += rc; + rc = gai_port(aihead, port, SOCK_DGRAM); + if (rc < 0) + return EAI_MEMORY; + num_found += rc; + } + } + else + { + // Try service name with TCP first, then UDP. + if (hints->ai_socktype == 0 || hints->ai_socktype == SOCK_STREAM) + { + servent* sptr = getservbyname(serv, "tcp"); + if (sptr != 0) + { + int rc = gai_port(aihead, sptr->s_port, SOCK_STREAM); + if (rc < 0) + return EAI_MEMORY; + num_found += rc; + } + } + if (hints->ai_socktype == 0 || hints->ai_socktype == SOCK_DGRAM) + { + servent* sptr = getservbyname(serv, "udp"); + if (sptr != 0) + { + int rc = gai_port(aihead, sptr->s_port, SOCK_DGRAM); + if (rc < 0) + return EAI_MEMORY; + num_found += rc; + } + } + } + + if (num_found == 0) + { + if (hints->ai_socktype == 0) + { + // All calls to getservbyname() failed. + return EAI_NONAME; + } + else + { + // Service not supported for socket type. + return EAI_SERVICE; + } + } + + return 0; +} + +inline int gai_echeck(const char* host, const char* service, + int flags, int family, int socktype, int protocol) +{ + (void)(flags); + (void)(protocol); + + // Host or service must be specified. + if (host == 0 || host[0] == '\0') + if (service == 0 || service[0] == '\0') + return EAI_NONAME; + + // Check combination of family and socket type. + switch (family) + { + case AF_UNSPEC: + break; + case AF_INET: + case AF_INET6: + if (socktype != 0 && socktype != SOCK_STREAM && socktype != SOCK_DGRAM) + return EAI_SOCKTYPE; + break; + default: + return EAI_FAMILY; + } + + return 0; +} + +inline void freeaddrinfo_emulation(addrinfo_type* aihead) +{ + addrinfo_type* ai = aihead; + while (ai) + { + gai_free(ai->ai_addr); + gai_free(ai->ai_canonname); + addrinfo_type* ainext = ai->ai_next; + gai_free(ai); + ai = ainext; + } +} + +inline int getaddrinfo_emulation(const char* host, const char* service, + const addrinfo_type* hintsp, addrinfo_type** result) +{ + // Set up linked list of addrinfo structures. + addrinfo_type* aihead = 0; + addrinfo_type** ainext = &aihead; + char* canon = 0; + + // Supply default hints if not specified by caller. + addrinfo_type hints = addrinfo_type(); + hints.ai_family = AF_UNSPEC; + if (hintsp) + hints = *hintsp; + + // If the resolution is not specifically for AF_INET6, remove the AI_V4MAPPED + // and AI_ALL flags. +#if defined(AI_V4MAPPED) + if (hints.ai_family != AF_INET6) + hints.ai_flags &= ~AI_V4MAPPED; +#endif +#if defined(AI_ALL) + if (hints.ai_family != AF_INET6) + hints.ai_flags &= ~AI_ALL; +#endif + + // Basic error checking. + int rc = gai_echeck(host, service, hints.ai_flags, hints.ai_family, + hints.ai_socktype, hints.ai_protocol); + if (rc != 0) + { + freeaddrinfo_emulation(aihead); + return rc; + } + + gai_search search[2]; + int search_count = gai_nsearch(host, &hints, search); + for (gai_search* sptr = search; sptr < search + search_count; ++sptr) + { + // Check for IPv4 dotted decimal string. + in4_addr_type inaddr; + asio::error_code ec; + if (socket_ops::inet_pton(AF_INET, sptr->host, &inaddr, 0, ec) == 1) + { + if (hints.ai_family != AF_UNSPEC && hints.ai_family != AF_INET) + { + freeaddrinfo_emulation(aihead); + gai_free(canon); + return EAI_FAMILY; + } + if (sptr->family == AF_INET) + { + rc = gai_aistruct(&ainext, &hints, &inaddr, AF_INET); + if (rc != 0) + { + freeaddrinfo_emulation(aihead); + gai_free(canon); + return rc; + } + } + continue; + } + + // Check for IPv6 hex string. + in6_addr_type in6addr; + if (socket_ops::inet_pton(AF_INET6, sptr->host, &in6addr, 0, ec) == 1) + { + if (hints.ai_family != AF_UNSPEC && hints.ai_family != AF_INET6) + { + freeaddrinfo_emulation(aihead); + gai_free(canon); + return EAI_FAMILY; + } + if (sptr->family == AF_INET6) + { + rc = gai_aistruct(&ainext, &hints, &in6addr, AF_INET6); + if (rc != 0) + { + freeaddrinfo_emulation(aihead); + gai_free(canon); + return rc; + } + } + continue; + } + + // Look up hostname. + hostent hent; + char hbuf[8192] = ""; + hostent* hptr = socket_ops::gethostbyname(sptr->host, + sptr->family, &hent, hbuf, sizeof(hbuf), hints.ai_flags, ec); + if (hptr == 0) + { + if (search_count == 2) + { + // Failure is OK if there are multiple searches. + continue; + } + freeaddrinfo_emulation(aihead); + gai_free(canon); + if (ec == asio::error::host_not_found) + return EAI_NONAME; + if (ec == asio::error::host_not_found_try_again) + return EAI_AGAIN; + if (ec == asio::error::no_recovery) + return EAI_FAIL; + if (ec == asio::error::no_data) + return EAI_NONAME; + return EAI_NONAME; + } + + // Check for address family mismatch if one was specified. + if (hints.ai_family != AF_UNSPEC && hints.ai_family != hptr->h_addrtype) + { + freeaddrinfo_emulation(aihead); + gai_free(canon); + socket_ops::freehostent(hptr); + return EAI_FAMILY; + } + + // Save canonical name first time. + if (host != 0 && host[0] != '\0' && hptr->h_name && hptr->h_name[0] + && (hints.ai_flags & AI_CANONNAME) && canon == 0) + { + canon = gai_alloc(strlen(hptr->h_name) + 1); + if (canon == 0) + { + freeaddrinfo_emulation(aihead); + socket_ops::freehostent(hptr); + return EAI_MEMORY; + } + strcpy(canon, hptr->h_name); + } + + // Create an addrinfo structure for each returned address. + for (char** ap = hptr->h_addr_list; *ap; ++ap) + { + rc = gai_aistruct(&ainext, &hints, *ap, hptr->h_addrtype); + if (rc != 0) + { + freeaddrinfo_emulation(aihead); + gai_free(canon); + socket_ops::freehostent(hptr); + return EAI_FAMILY; + } + } + + socket_ops::freehostent(hptr); + } + + // Check if we found anything. + if (aihead == 0) + { + gai_free(canon); + return EAI_NONAME; + } + + // Return canonical name in first entry. + if (host != 0 && host[0] != '\0' && (hints.ai_flags & AI_CANONNAME)) + { + if (canon) + { + aihead->ai_canonname = canon; + canon = 0; + } + else + { + aihead->ai_canonname = gai_alloc(strlen(search[0].host) + 1); + if (aihead->ai_canonname == 0) + { + freeaddrinfo_emulation(aihead); + return EAI_MEMORY; + } + strcpy(aihead->ai_canonname, search[0].host); + } + } + gai_free(canon); + + // Process the service name. + if (service != 0 && service[0] != '\0') + { + rc = gai_serv(aihead, &hints, service); + if (rc != 0) + { + freeaddrinfo_emulation(aihead); + return rc; + } + } + + // Return result to caller. + *result = aihead; + return 0; +} + +inline asio::error_code getnameinfo_emulation( + const socket_addr_type* sa, socket_addr_len_type salen, char* host, + std::size_t hostlen, char* serv, std::size_t servlen, int flags, + asio::error_code& ec) +{ + using namespace std; + + const char* addr; + size_t addr_len; + unsigned short port; + switch (sa->sa_family) + { + case AF_INET: + if (salen != sizeof(sockaddr_in4_type)) + { + return ec = asio::error::invalid_argument; + } + addr = reinterpret_cast( + &reinterpret_cast(sa)->sin_addr); + addr_len = sizeof(in4_addr_type); + port = reinterpret_cast(sa)->sin_port; + break; + case AF_INET6: + if (salen != sizeof(sockaddr_in6_type)) + { + return ec = asio::error::invalid_argument; + } + addr = reinterpret_cast( + &reinterpret_cast(sa)->sin6_addr); + addr_len = sizeof(in6_addr_type); + port = reinterpret_cast(sa)->sin6_port; + break; + default: + return ec = asio::error::address_family_not_supported; + } + + if (host && hostlen > 0) + { + if (flags & NI_NUMERICHOST) + { + if (socket_ops::inet_ntop(sa->sa_family, addr, host, hostlen, 0, ec) == 0) + { + return ec; + } + } + else + { + hostent hent; + char hbuf[8192] = ""; + hostent* hptr = socket_ops::gethostbyaddr(addr, + static_cast(addr_len), sa->sa_family, + &hent, hbuf, sizeof(hbuf), ec); + if (hptr && hptr->h_name && hptr->h_name[0] != '\0') + { + if (flags & NI_NOFQDN) + { + char* dot = strchr(hptr->h_name, '.'); + if (dot) + { + *dot = 0; + } + } + *host = '\0'; + strncat(host, hptr->h_name, hostlen); + socket_ops::freehostent(hptr); + } + else + { + socket_ops::freehostent(hptr); + if (flags & NI_NAMEREQD) + { + return ec = asio::error::host_not_found; + } + if (socket_ops::inet_ntop(sa->sa_family, + addr, host, hostlen, 0, ec) == 0) + { + return ec; + } + } + } + } + + if (serv && servlen > 0) + { + if (flags & NI_NUMERICSERV) + { + if (servlen < 6) + { + return ec = asio::error::no_buffer_space; + } + sprintf(serv, "%u", ntohs(port)); + } + else + { +#if defined(BOOST_HAS_THREADS) && defined(BOOST_HAS_PTHREADS) + static ::pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; + ::pthread_mutex_lock(&mutex); +#endif // defined(BOOST_HAS_THREADS) && defined(BOOST_HAS_PTHREADS) + servent* sptr = ::getservbyport(port, (flags & NI_DGRAM) ? "udp" : 0); + if (sptr && sptr->s_name && sptr->s_name[0] != '\0') + { + *serv = '\0'; + strncat(serv, sptr->s_name, servlen); + } + else + { + if (servlen < 6) + { + return ec = asio::error::no_buffer_space; + } + sprintf(serv, "%u", ntohs(port)); + } +#if defined(BOOST_HAS_THREADS) && defined(BOOST_HAS_PTHREADS) + ::pthread_mutex_unlock(&mutex); +#endif // defined(BOOST_HAS_THREADS) && defined(BOOST_HAS_PTHREADS) + } + } + + clear_error(ec); + return ec; +} + +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + // || defined(__MACH__) && defined(__APPLE__) + +inline asio::error_code translate_addrinfo_error(int error) +{ + switch (error) + { + case 0: + return asio::error_code(); + case EAI_AGAIN: + return asio::error::host_not_found_try_again; + case EAI_BADFLAGS: + return asio::error::invalid_argument; + case EAI_FAIL: + return asio::error::no_recovery; + case EAI_FAMILY: + return asio::error::address_family_not_supported; + case EAI_MEMORY: + return asio::error::no_memory; + case EAI_NONAME: + return asio::error::host_not_found; + case EAI_SERVICE: + return asio::error::service_not_found; + case EAI_SOCKTYPE: + return asio::error::socket_type_not_supported; + default: // Possibly the non-portable EAI_SYSTEM. +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + return asio::error_code( + WSAGetLastError(), asio::native_ecat); +#else + return asio::error_code( + errno, asio::native_ecat); +#endif + } +} + +inline asio::error_code getaddrinfo(const char* host, + const char* service, const addrinfo_type* hints, addrinfo_type** result, + asio::error_code& ec) +{ + clear_error(ec); +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) +# if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0501) + // Building for Windows XP, Windows Server 2003, or later. + int error = ::getaddrinfo(host, service, hints, result); + return ec = translate_addrinfo_error(error); +# else + // Building for Windows 2000 or earlier. + typedef int (WSAAPI *gai_t)(const char*, + const char*, const addrinfo_type*, addrinfo_type**); + if (HMODULE winsock_module = ::GetModuleHandleA("ws2_32")) + { + if (gai_t gai = (gai_t)::GetProcAddress(winsock_module, "getaddrinfo")) + { + int error = gai(host, service, hints, result); + return ec = translate_addrinfo_error(error); + } + } + int error = getaddrinfo_emulation(host, service, hints, result); + return ec = translate_addrinfo_error(error); +# endif +#elif defined(__MACH__) && defined(__APPLE__) + int error = getaddrinfo_emulation(host, service, hints, result); + return ec = translate_addrinfo_error(error); +#else + int error = ::getaddrinfo(host, service, hints, result); + return ec = translate_addrinfo_error(error); +#endif +} + +inline void freeaddrinfo(addrinfo_type* ai) +{ +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) +# if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0501) + // Building for Windows XP, Windows Server 2003, or later. + ::freeaddrinfo(ai); +# else + // Building for Windows 2000 or earlier. + typedef int (WSAAPI *fai_t)(addrinfo_type*); + if (HMODULE winsock_module = ::GetModuleHandleA("ws2_32")) + { + if (fai_t fai = (fai_t)::GetProcAddress(winsock_module, "freeaddrinfo")) + { + fai(ai); + return; + } + } + freeaddrinfo_emulation(ai); +# endif +#elif defined(__MACH__) && defined(__APPLE__) + freeaddrinfo_emulation(ai); +#else + ::freeaddrinfo(ai); +#endif +} + +inline asio::error_code getnameinfo(const socket_addr_type* addr, + socket_addr_len_type addrlen, char* host, std::size_t hostlen, + char* serv, std::size_t servlen, int flags, asio::error_code& ec) +{ +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) +# if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0501) + // Building for Windows XP, Windows Server 2003, or later. + clear_error(ec); + int error = ::getnameinfo(addr, addrlen, host, static_cast(hostlen), + serv, static_cast(servlen), flags); + return ec = translate_addrinfo_error(error); +# else + // Building for Windows 2000 or earlier. + typedef int (WSAAPI *gni_t)(const socket_addr_type*, + socket_addr_len_type, char*, std::size_t, char*, std::size_t, int); + if (HMODULE winsock_module = ::GetModuleHandleA("ws2_32")) + { + if (gni_t gni = (gni_t)::GetProcAddress(winsock_module, "getnameinfo")) + { + clear_error(ec); + int error = gni(addr, addrlen, host, hostlen, serv, servlen, flags); + return ec = translate_addrinfo_error(error); + } + } + clear_error(ec); + return getnameinfo_emulation(addr, addrlen, + host, hostlen, serv, servlen, flags, ec); +# endif +#elif defined(__MACH__) && defined(__APPLE__) + using namespace std; // For memcpy. + sockaddr_storage_type tmp_addr; + memcpy(&tmp_addr, addr, addrlen); + tmp_addr.ss_len = addrlen; + addr = reinterpret_cast(&tmp_addr); + clear_error(ec); + return getnameinfo_emulation(addr, addrlen, + host, hostlen, serv, servlen, flags, ec); +#else + clear_error(ec); + int error = ::getnameinfo(addr, addrlen, host, hostlen, serv, servlen, flags); + return ec = translate_addrinfo_error(error); +#endif +} + +inline u_long_type network_to_host_long(u_long_type value) +{ + return ntohl(value); +} + +inline u_long_type host_to_network_long(u_long_type value) +{ + return htonl(value); +} + +inline u_short_type network_to_host_short(u_short_type value) +{ + return ntohs(value); +} + +inline u_short_type host_to_network_short(u_short_type value) +{ + return htons(value); +} + +} // namespace socket_ops +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_SOCKET_OPS_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/detail/socket_option.hpp b/encryption/libtorrent/include/libtorrent/asio/detail/socket_option.hpp new file mode 100644 index 000000000..ee867e6b2 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/detail/socket_option.hpp @@ -0,0 +1,298 @@ +// +// socket_option.hpp +// ~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_SOCKET_OPTION_HPP +#define ASIO_DETAIL_SOCKET_OPTION_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/detail/socket_types.hpp" + +namespace asio { +namespace detail { +namespace socket_option { + +// Helper template for implementing boolean-based options. +template +class boolean +{ +public: + // Default constructor. + boolean() + : value_(0) + { + } + + // Construct with a specific option value. + explicit boolean(bool v) + : value_(v ? 1 : 0) + { + } + + // Set the current value of the boolean. + boolean& operator=(bool v) + { + value_ = v ? 1 : 0; + return *this; + } + + // Get the current value of the boolean. + bool value() const + { + return !!value_; + } + + // Convert to bool. + operator bool() const + { + return !!value_; + } + + // Test for false. + bool operator!() const + { + return !value_; + } + + // Get the level of the socket option. + template + int level(const Protocol&) const + { + return Level; + } + + // Get the name of the socket option. + template + int name(const Protocol&) const + { + return Name; + } + + // Get the address of the boolean data. + template + int* data(const Protocol&) + { + return &value_; + } + + // Get the address of the boolean data. + template + const int* data(const Protocol&) const + { + return &value_; + } + + // Get the size of the boolean data. + template + std::size_t size(const Protocol&) const + { + return sizeof(value_); + } + + // Set the size of the boolean data. + template + void resize(const Protocol&, std::size_t s) + { + if (s != sizeof(value_)) + throw std::length_error("boolean socket option resize"); + } + +private: + int value_; +}; + +// Helper template for implementing integer options. +template +class integer +{ +public: + // Default constructor. + integer() + : value_(0) + { + } + + // Construct with a specific option value. + explicit integer(int v) + : value_(v) + { + } + + // Set the value of the int option. + integer& operator=(int v) + { + value_ = v; + return *this; + } + + // Get the current value of the int option. + int value() const + { + return value_; + } + + // Get the level of the socket option. + template + int level(const Protocol&) const + { + return Level; + } + + // Get the name of the socket option. + template + int name(const Protocol&) const + { + return Name; + } + + // Get the address of the int data. + template + int* data(const Protocol&) + { + return &value_; + } + + // Get the address of the int data. + template + const int* data(const Protocol&) const + { + return &value_; + } + + // Get the size of the int data. + template + std::size_t size(const Protocol&) const + { + return sizeof(value_); + } + + // Set the size of the int data. + template + void resize(const Protocol&, std::size_t s) + { + if (s != sizeof(value_)) + throw std::length_error("integer socket option resize"); + } + +private: + int value_; +}; + +// Helper template for implementing linger options. +template +class linger +{ +public: + // Default constructor. + linger() + { + value_.l_onoff = 0; + value_.l_linger = 0; + } + + // Construct with specific option values. + linger(bool e, int t) + { + enabled(e); + timeout(t); + } + + // Set the value for whether linger is enabled. + void enabled(bool value) + { + value_.l_onoff = value ? 1 : 0; + } + + // Get the value for whether linger is enabled. + bool enabled() const + { + return value_.l_onoff != 0; + } + + // Set the value for the linger timeout. + void timeout(int value) + { +#if defined(WIN32) + value_.l_linger = static_cast(value); +#else + value_.l_linger = value; +#endif + } + + // Get the value for the linger timeout. + int timeout() const + { + return static_cast(value_.l_linger); + } + + // Get the level of the socket option. + template + int level(const Protocol&) const + { + return Level; + } + + // Get the name of the socket option. + template + int name(const Protocol&) const + { + return Name; + } + + // Get the address of the linger data. + template + ::linger* data(const Protocol&) + { + return &value_; + } + + // Get the address of the linger data. + template + const ::linger* data(const Protocol&) const + { + return &value_; + } + + // Get the size of the linger data. + template + std::size_t size(const Protocol&) const + { + return sizeof(value_); + } + + // Set the size of the int data. + template + void resize(const Protocol&, std::size_t s) + { + if (s != sizeof(value_)) + throw std::length_error("linger socket option resize"); + } + +private: + ::linger value_; +}; + +} // namespace socket_option +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_SOCKET_OPTION_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/detail/socket_select_interrupter.hpp b/encryption/libtorrent/include/libtorrent/asio/detail/socket_select_interrupter.hpp new file mode 100644 index 000000000..6117d309d --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/detail/socket_select_interrupter.hpp @@ -0,0 +1,184 @@ +// +// socket_select_interrupter.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_SOCKET_SELECT_INTERRUPTER_HPP +#define ASIO_DETAIL_SOCKET_SELECT_INTERRUPTER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/error.hpp" +#include "asio/system_error.hpp" +#include "asio/detail/socket_holder.hpp" +#include "asio/detail/socket_ops.hpp" +#include "asio/detail/socket_types.hpp" + +namespace asio { +namespace detail { + +class socket_select_interrupter +{ +public: + // Constructor. + socket_select_interrupter() + { + asio::error_code ec; + socket_holder acceptor(socket_ops::socket( + AF_INET, SOCK_STREAM, IPPROTO_TCP, ec)); + if (acceptor.get() == invalid_socket) + { + asio::system_error e(ec, "socket_select_interrupter"); + boost::throw_exception(e); + } + + int opt = 1; + socket_ops::setsockopt(acceptor.get(), + SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt), ec); + + sockaddr_in4_type addr; + socket_addr_len_type addr_len = sizeof(addr); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = inet_addr("127.0.0.1"); + addr.sin_port = 0; + if (socket_ops::bind(acceptor.get(), (const socket_addr_type*)&addr, + addr_len, ec) == socket_error_retval) + { + asio::system_error e(ec, "socket_select_interrupter"); + boost::throw_exception(e); + } + + if (socket_ops::getsockname(acceptor.get(), (socket_addr_type*)&addr, + &addr_len, ec) == socket_error_retval) + { + asio::system_error e(ec, "socket_select_interrupter"); + boost::throw_exception(e); + } + + if (socket_ops::listen(acceptor.get(), + SOMAXCONN, ec) == socket_error_retval) + { + asio::system_error e(ec, "socket_select_interrupter"); + boost::throw_exception(e); + } + + socket_holder client(socket_ops::socket( + AF_INET, SOCK_STREAM, IPPROTO_TCP, ec)); + if (client.get() == invalid_socket) + { + asio::system_error e(ec, "socket_select_interrupter"); + boost::throw_exception(e); + } + + if (socket_ops::connect(client.get(), (const socket_addr_type*)&addr, + addr_len, ec) == socket_error_retval) + { + asio::system_error e(ec, "socket_select_interrupter"); + boost::throw_exception(e); + } + + socket_holder server(socket_ops::accept(acceptor.get(), 0, 0, ec)); + if (server.get() == invalid_socket) + { + asio::system_error e(ec, "socket_select_interrupter"); + boost::throw_exception(e); + } + + ioctl_arg_type non_blocking = 1; + if (socket_ops::ioctl(client.get(), FIONBIO, &non_blocking, ec)) + { + asio::system_error e(ec, "socket_select_interrupter"); + boost::throw_exception(e); + } + + opt = 1; + socket_ops::setsockopt(client.get(), + IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt), ec); + + non_blocking = 1; + if (socket_ops::ioctl(server.get(), FIONBIO, &non_blocking, ec)) + { + asio::system_error e(ec, "socket_select_interrupter"); + boost::throw_exception(e); + } + + opt = 1; + socket_ops::setsockopt(server.get(), + IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt), ec); + + read_descriptor_ = server.release(); + write_descriptor_ = client.release(); + } + + // Destructor. + ~socket_select_interrupter() + { + asio::error_code ec; + if (read_descriptor_ != invalid_socket) + socket_ops::close(read_descriptor_, ec); + if (write_descriptor_ != invalid_socket) + socket_ops::close(write_descriptor_, ec); + } + + // Interrupt the select call. + void interrupt() + { + char byte = 0; + socket_ops::buf b; + socket_ops::init_buf(b, &byte, 1); + asio::error_code ec; + socket_ops::send(write_descriptor_, &b, 1, 0, ec); + } + + // Reset the select interrupt. Returns true if the call was interrupted. + bool reset() + { + char data[1024]; + socket_ops::buf b; + socket_ops::init_buf(b, data, sizeof(data)); + asio::error_code ec; + int bytes_read = socket_ops::recv(read_descriptor_, &b, 1, 0, ec); + bool was_interrupted = (bytes_read > 0); + while (bytes_read == sizeof(data)) + bytes_read = socket_ops::recv(read_descriptor_, &b, 1, 0, ec); + return was_interrupted; + } + + // Get the read descriptor to be passed to select. + socket_type read_descriptor() const + { + return read_descriptor_; + } + +private: + // The read end of a connection used to interrupt the select call. This file + // descriptor is passed to select such that when it is time to stop, a single + // byte will be written on the other end of the connection and this + // descriptor will become readable. + socket_type read_descriptor_; + + // The write end of a connection used to interrupt the select call. A single + // byte may be written to this to wake up the select which is waiting for the + // other end to become readable. + socket_type write_descriptor_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_SOCKET_SELECT_INTERRUPTER_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/detail/socket_types.hpp b/encryption/libtorrent/include/libtorrent/asio/detail/socket_types.hpp new file mode 100644 index 000000000..b2a03f2f1 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/detail/socket_types.hpp @@ -0,0 +1,178 @@ +// +// socket_types.hpp +// ~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_SOCKET_TYPES_HPP +#define ASIO_DETAIL_SOCKET_TYPES_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/detail/push_options.hpp" +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) +# if defined(_WINSOCKAPI_) && !defined(_WINSOCK2API_) +# error WinSock.h has already been included +# endif // defined(_WINSOCKAPI_) && !defined(_WINSOCK2API_) +# if !defined(_WIN32_WINNT) && !defined(_WIN32_WINDOWS) +# if defined(_MSC_VER) || defined(__BORLANDC__) +# pragma message("Please define _WIN32_WINNT or _WIN32_WINDOWS appropriately") +# pragma message("Assuming _WIN32_WINNT=0x0500 (i.e. Windows 2000 target)") +# else // defined(_MSC_VER) || defined(__BORLANDC__) +# warning Please define _WIN32_WINNT or _WIN32_WINDOWS appropriately +# warning Assuming _WIN32_WINNT=0x0500 (i.e. Windows 2000 target) +# endif // defined(_MSC_VER) || defined(__BORLANDC__) +# define _WIN32_WINNT 0x0500 +# endif // !defined(_WIN32_WINNT) && !defined(_WIN32_WINDOWS) +# if defined(_MSC_VER) +# if defined(_WIN32) && !defined(WIN32) +# if !defined(_WINSOCK2API_) +# define WIN32 // Needed for correct types in winsock2.h +# else // !defined(_WINSOCK2API_) +# error Please define the macro WIN32 in your compiler options +# endif // !defined(_WINSOCK2API_) +# endif // defined(_WIN32) && !defined(WIN32) +# endif // defined(_MSC_VER) +# if defined(__BORLANDC__) +# include // Needed for __errno +# if defined(__WIN32__) && !defined(WIN32) +# if !defined(_WINSOCK2API_) +# define WIN32 // Needed for correct types in winsock2.h +# else // !defined(_WINSOCK2API_) +# error Please define the macro WIN32 in your compiler options +# endif // !defined(_WINSOCK2API_) +# endif // defined(__WIN32__) && !defined(WIN32) +# if !defined(_WSPIAPI_H_) +# define _WSPIAPI_H_ +# define ASIO_WSPIAPI_H_DEFINED +# endif // !defined(_WSPIAPI_H_) +# endif // defined(__BORLANDC__) +# if !defined(ASIO_NO_WIN32_LEAN_AND_MEAN) +# if !defined(WIN32_LEAN_AND_MEAN) +# define WIN32_LEAN_AND_MEAN +# endif // !defined(WIN32_LEAN_AND_MEAN) +# endif // !defined(ASIO_NO_WIN32_LEAN_AND_MEAN) +# if defined(__CYGWIN__) +# if !defined(__USE_W32_SOCKETS) +# error You must add -D__USE_W32_SOCKETS to your compiler options. +# endif // !defined(__USE_W32_SOCKETS) +# if !defined(NOMINMAX) +# define NOMINMAX 1 +# endif // !defined(NOMINMAX) +# endif // defined(__CYGWIN__) +# include +# include +# include +# if defined(ASIO_WSPIAPI_H_DEFINED) +# undef _WSPIAPI_H_ +# undef ASIO_WSPIAPI_H_DEFINED +# endif // defined(ASIO_WSPIAPI_H_DEFINED) +# if !defined(ASIO_NO_DEFAULT_LINKED_LIBS) +# if defined(_MSC_VER) || defined(__BORLANDC__) +# pragma comment(lib, "ws2_32.lib") +# pragma comment(lib, "mswsock.lib") +# endif // defined(_MSC_VER) || defined(__BORLANDC__) +# endif // !defined(ASIO_NO_DEFAULT_LINKED_LIBS) +# include "asio/detail/old_win_sdk_compat.hpp" +#else +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# if defined(__sun) +# include +# include +# endif +#endif +#include "asio/detail/pop_options.hpp" + +namespace asio { +namespace detail { + +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) +typedef SOCKET socket_type; +const SOCKET invalid_socket = INVALID_SOCKET; +const int socket_error_retval = SOCKET_ERROR; +const int max_addr_v4_str_len = 256; +const int max_addr_v6_str_len = 256; +typedef sockaddr socket_addr_type; +typedef int socket_addr_len_type; +typedef in_addr in4_addr_type; +typedef ip_mreq in4_mreq_type; +typedef sockaddr_in sockaddr_in4_type; +# if defined(ASIO_HAS_OLD_WIN_SDK) +typedef in6_addr_emulation in6_addr_type; +typedef ipv6_mreq_emulation in6_mreq_type; +typedef sockaddr_in6_emulation sockaddr_in6_type; +typedef sockaddr_storage_emulation sockaddr_storage_type; +typedef addrinfo_emulation addrinfo_type; +# else +typedef in6_addr in6_addr_type; +typedef ipv6_mreq in6_mreq_type; +typedef sockaddr_in6 sockaddr_in6_type; +typedef sockaddr_storage sockaddr_storage_type; +typedef addrinfo addrinfo_type; +# endif +typedef unsigned long ioctl_arg_type; +typedef u_long u_long_type; +typedef u_short u_short_type; +const int shutdown_receive = SD_RECEIVE; +const int shutdown_send = SD_SEND; +const int shutdown_both = SD_BOTH; +const int message_peek = MSG_PEEK; +const int message_out_of_band = MSG_OOB; +const int message_do_not_route = MSG_DONTROUTE; +#else +typedef int socket_type; +const int invalid_socket = -1; +const int socket_error_retval = -1; +const int max_addr_v4_str_len = INET_ADDRSTRLEN; +const int max_addr_v6_str_len = INET6_ADDRSTRLEN + 1 + IF_NAMESIZE; +typedef sockaddr socket_addr_type; +typedef socklen_t socket_addr_len_type; +typedef in_addr in4_addr_type; +typedef ip_mreq in4_mreq_type; +typedef sockaddr_in sockaddr_in4_type; +typedef in6_addr in6_addr_type; +typedef ipv6_mreq in6_mreq_type; +typedef sockaddr_in6 sockaddr_in6_type; +typedef sockaddr_storage sockaddr_storage_type; +typedef addrinfo addrinfo_type; +typedef int ioctl_arg_type; +typedef uint32_t u_long_type; +typedef uint16_t u_short_type; +const int shutdown_receive = SHUT_RD; +const int shutdown_send = SHUT_WR; +const int shutdown_both = SHUT_RDWR; +const int message_peek = MSG_PEEK; +const int message_out_of_band = MSG_OOB; +const int message_do_not_route = MSG_DONTROUTE; +#endif +const int custom_socket_option_level = 0xA5100000; +const int enable_connection_aborted_option = 1; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_SOCKET_TYPES_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/detail/strand_service.hpp b/encryption/libtorrent/include/libtorrent/asio/detail/strand_service.hpp new file mode 100644 index 000000000..f10289090 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/detail/strand_service.hpp @@ -0,0 +1,526 @@ +// +// strand_service.hpp +// ~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_STRAND_SERVICE_HPP +#define ASIO_DETAIL_STRAND_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/io_service.hpp" +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/call_stack.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/mutex.hpp" +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/service_base.hpp" + +namespace asio { +namespace detail { + +// Default service implementation for a strand. +class strand_service + : public asio::detail::service_base +{ +public: + class handler_base; + class invoke_current_handler; + class post_next_waiter_on_exit; + + // The underlying implementation of a strand. + class strand_impl + { +#if defined (__BORLANDC__) + public: +#else + private: +#endif + void add_ref() + { + asio::detail::mutex::scoped_lock lock(mutex_); + ++ref_count_; + } + + void release() + { + asio::detail::mutex::scoped_lock lock(mutex_); + --ref_count_; + if (ref_count_ == 0) + { + lock.unlock(); + delete this; + } + } + + private: + // Only this service will have access to the internal values. + friend class strand_service; + friend class post_next_waiter_on_exit; + friend class invoke_current_handler; + + strand_impl(strand_service& owner) + : owner_(owner), + current_handler_(0), + first_waiter_(0), + last_waiter_(0), + ref_count_(0) + { + // Insert implementation into linked list of all implementations. + asio::detail::mutex::scoped_lock lock(owner_.mutex_); + next_ = owner_.impl_list_; + prev_ = 0; + if (owner_.impl_list_) + owner_.impl_list_->prev_ = this; + owner_.impl_list_ = this; + } + + ~strand_impl() + { + // Remove implementation from linked list of all implementations. + asio::detail::mutex::scoped_lock lock(owner_.mutex_); + if (owner_.impl_list_ == this) + owner_.impl_list_ = next_; + if (prev_) + prev_->next_ = next_; + if (next_) + next_->prev_= prev_; + next_ = 0; + prev_ = 0; + lock.unlock(); + + if (current_handler_) + { + current_handler_->destroy(); + } + + while (first_waiter_) + { + handler_base* next = first_waiter_->next_; + first_waiter_->destroy(); + first_waiter_ = next; + } + } + + // Mutex to protect access to internal data. + asio::detail::mutex mutex_; + + // The service that owns this implementation. + strand_service& owner_; + + // The handler that is ready to execute. If this pointer is non-null then it + // indicates that a handler holds the lock. + handler_base* current_handler_; + + // The start of the list of waiting handlers for the strand. + handler_base* first_waiter_; + + // The end of the list of waiting handlers for the strand. + handler_base* last_waiter_; + + // Storage for posted handlers. + typedef boost::aligned_storage<64> handler_storage_type; +#if defined(__BORLANDC__) + boost::aligned_storage<64> handler_storage_; +#else + handler_storage_type handler_storage_; +#endif + + // Pointers to adjacent socket implementations in linked list. + strand_impl* next_; + strand_impl* prev_; + + // The reference count on the strand implementation. + size_t ref_count_; + +#if !defined(__BORLANDC__) + friend void intrusive_ptr_add_ref(strand_impl* p) + { + p->add_ref(); + } + + friend void intrusive_ptr_release(strand_impl* p) + { + p->release(); + } +#endif + }; + + friend class strand_impl; + + typedef boost::intrusive_ptr implementation_type; + + // Base class for all handler types. + class handler_base + { + public: + typedef void (*invoke_func_type)(handler_base*, + strand_service&, implementation_type&); + typedef void (*destroy_func_type)(handler_base*); + + handler_base(invoke_func_type invoke_func, destroy_func_type destroy_func) + : next_(0), + invoke_func_(invoke_func), + destroy_func_(destroy_func) + { + } + + void invoke(strand_service& service_impl, implementation_type& impl) + { + invoke_func_(this, service_impl, impl); + } + + void destroy() + { + destroy_func_(this); + } + + protected: + ~handler_base() + { + } + + private: + friend class strand_service; + friend class strand_impl; + friend class post_next_waiter_on_exit; + handler_base* next_; + invoke_func_type invoke_func_; + destroy_func_type destroy_func_; + }; + + // Helper class to allow handlers to be dispatched. + class invoke_current_handler + { + public: + invoke_current_handler(strand_service& service_impl, + const implementation_type& impl) + : service_impl_(service_impl), + impl_(impl) + { + } + + void operator()() + { + impl_->current_handler_->invoke(service_impl_, impl_); + } + + friend void* asio_handler_allocate(std::size_t size, + invoke_current_handler* this_handler) + { + return this_handler->do_handler_allocate(size); + } + + friend void asio_handler_deallocate(void*, std::size_t, + invoke_current_handler*) + { + } + + void* do_handler_allocate(std::size_t size) + { +#if defined(__BORLANDC__) + BOOST_ASSERT(size <= boost::aligned_storage<64>::size); +#else + BOOST_ASSERT(size <= strand_impl::handler_storage_type::size); +#endif + return impl_->handler_storage_.address(); + } + + // The asio_handler_invoke hook is not defined here since the default one + // provides the correct behaviour, and including it here breaks MSVC 7.1 + // in some situations. + + private: + strand_service& service_impl_; + implementation_type impl_; + }; + + // Helper class to automatically enqueue next waiter on block exit. + class post_next_waiter_on_exit + { + public: + post_next_waiter_on_exit(strand_service& service_impl, + implementation_type& impl) + : service_impl_(service_impl), + impl_(impl), + cancelled_(false) + { + } + + ~post_next_waiter_on_exit() + { + if (!cancelled_) + { + asio::detail::mutex::scoped_lock lock(impl_->mutex_); + impl_->current_handler_ = impl_->first_waiter_; + if (impl_->current_handler_) + { + impl_->first_waiter_ = impl_->first_waiter_->next_; + if (impl_->first_waiter_ == 0) + impl_->last_waiter_ = 0; + lock.unlock(); + service_impl_.io_service().post( + invoke_current_handler(service_impl_, impl_)); + } + } + } + + void cancel() + { + cancelled_ = true; + } + + private: + strand_service& service_impl_; + implementation_type& impl_; + bool cancelled_; + }; + + // Class template for a waiter. + template + class handler_wrapper + : public handler_base + { + public: + handler_wrapper(Handler handler) + : handler_base(&handler_wrapper::do_invoke, + &handler_wrapper::do_destroy), + handler_(handler) + { + } + + static void do_invoke(handler_base* base, + strand_service& service_impl, implementation_type& impl) + { + // Take ownership of the handler object. + typedef handler_wrapper this_type; + this_type* h(static_cast(base)); + typedef handler_alloc_traits alloc_traits; + handler_ptr ptr(h->handler_, h); + + post_next_waiter_on_exit p1(service_impl, impl); + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. + Handler handler(h->handler_); + + // A handler object must still be valid when the next waiter is posted + // since destroying the last handler might cause the strand object to be + // destroyed. Therefore we create a second post_next_waiter_on_exit object + // that will be destroyed before the handler object. + p1.cancel(); + post_next_waiter_on_exit p2(service_impl, impl); + + // Free the memory associated with the handler. + ptr.reset(); + + // Indicate that this strand is executing on the current thread. + call_stack::context ctx(impl.get()); + + // Make the upcall. + asio_handler_invoke_helpers::invoke(handler, &handler); + } + + static void do_destroy(handler_base* base) + { + // Take ownership of the handler object. + typedef handler_wrapper this_type; + this_type* h(static_cast(base)); + typedef handler_alloc_traits alloc_traits; + handler_ptr ptr(h->handler_, h); + } + + private: + Handler handler_; + }; + + // Construct a new strand service for the specified io_service. + explicit strand_service(asio::io_service& io_service) + : asio::detail::service_base(io_service), + mutex_(), + impl_list_(0) + { + } + + // Destroy all user-defined handler objects owned by the service. + void shutdown_service() + { + // Construct a list of all handlers to be destroyed. + asio::detail::mutex::scoped_lock lock(mutex_); + strand_impl* impl = impl_list_; + handler_base* first_handler = 0; + while (impl) + { + if (impl->current_handler_) + { + impl->current_handler_->next_ = first_handler; + first_handler = impl->current_handler_; + impl->current_handler_ = 0; + } + if (impl->first_waiter_) + { + impl->last_waiter_->next_ = first_handler; + first_handler = impl->first_waiter_; + impl->first_waiter_ = 0; + impl->last_waiter_ = 0; + } + impl = impl->next_; + } + + // Destroy all handlers without holding the lock. + lock.unlock(); + while (first_handler) + { + handler_base* next = first_handler->next_; + first_handler->destroy(); + first_handler = next; + } + } + + // Construct a new strand implementation. + void construct(implementation_type& impl) + { + impl = implementation_type(new strand_impl(*this)); + } + + // Destroy a strand implementation. + void destroy(implementation_type& impl) + { + implementation_type().swap(impl); + } + + // Request the io_service to invoke the given handler. + template + void dispatch(implementation_type& impl, Handler handler) + { + if (call_stack::contains(impl.get())) + { + asio_handler_invoke_helpers::invoke(handler, &handler); + } + else + { + asio::detail::mutex::scoped_lock lock(impl->mutex_); + + // Allocate and construct an object to wrap the handler. + typedef handler_wrapper value_type; + typedef handler_alloc_traits alloc_traits; + raw_handler_ptr raw_ptr(handler); + handler_ptr ptr(raw_ptr, handler); + + if (impl->current_handler_ == 0) + { + // This handler now has the lock, so can be dispatched immediately. + impl->current_handler_ = ptr.get(); + lock.unlock(); + this->io_service().dispatch(invoke_current_handler(*this, impl)); + ptr.release(); + } + else + { + // Another handler already holds the lock, so this handler must join + // the list of waiters. The handler will be posted automatically when + // its turn comes. + if (impl->last_waiter_) + { + impl->last_waiter_->next_ = ptr.get(); + impl->last_waiter_ = impl->last_waiter_->next_; + } + else + { + impl->first_waiter_ = ptr.get(); + impl->last_waiter_ = ptr.get(); + } + ptr.release(); + } + } + } + + // Request the io_service to invoke the given handler and return immediately. + template + void post(implementation_type& impl, Handler handler) + { + asio::detail::mutex::scoped_lock lock(impl->mutex_); + + // Allocate and construct an object to wrap the handler. + typedef handler_wrapper value_type; + typedef handler_alloc_traits alloc_traits; + raw_handler_ptr raw_ptr(handler); + handler_ptr ptr(raw_ptr, handler); + + if (impl->current_handler_ == 0) + { + // This handler now has the lock, so can be dispatched immediately. + impl->current_handler_ = ptr.get(); + lock.unlock(); + this->io_service().post(invoke_current_handler(*this, impl)); + ptr.release(); + } + else + { + // Another handler already holds the lock, so this handler must join the + // list of waiters. The handler will be posted automatically when its turn + // comes. + if (impl->last_waiter_) + { + impl->last_waiter_->next_ = ptr.get(); + impl->last_waiter_ = impl->last_waiter_->next_; + } + else + { + impl->first_waiter_ = ptr.get(); + impl->last_waiter_ = ptr.get(); + } + ptr.release(); + } + } + +private: + // Mutex to protect access to the linked list of implementations. + asio::detail::mutex mutex_; + + // The head of a linked list of all implementations. + strand_impl* impl_list_; +}; + +} // namespace detail +} // namespace asio + +#if defined(__BORLANDC__) + +namespace boost { + +inline void intrusive_ptr_add_ref( + asio::detail::strand_service::strand_impl* p) +{ + p->add_ref(); +} + +inline void intrusive_ptr_release( + asio::detail::strand_service::strand_impl* p) +{ + p->release(); +} + +} // namespace boost + +#endif // defined(__BORLANDC__) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_STRAND_SERVICE_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/detail/task_io_service.hpp b/encryption/libtorrent/include/libtorrent/asio/detail/task_io_service.hpp new file mode 100644 index 000000000..07df1c18a --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/detail/task_io_service.hpp @@ -0,0 +1,538 @@ +// +// task_io_service.hpp +// ~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_TASK_IO_SERVICE_HPP +#define ASIO_DETAIL_TASK_IO_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/error_code.hpp" +#include "asio/io_service.hpp" +#include "asio/detail/call_stack.hpp" +#include "asio/detail/event.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/mutex.hpp" +#include "asio/detail/service_base.hpp" +#include "asio/detail/task_io_service_fwd.hpp" + +namespace asio { +namespace detail { + +template +class task_io_service + : public asio::detail::service_base > +{ +public: + // Constructor. + task_io_service(asio::io_service& io_service) + : asio::detail::service_base >(io_service), + mutex_(), + task_(use_service(io_service)), + outstanding_work_(0), + handler_queue_(&task_handler_), + handler_queue_end_(&task_handler_), + stopped_(false), + shutdown_(false), + first_idle_thread_(0) + { + } + + void init(size_t /*concurrency_hint*/) + { + } + + // Destroy all user-defined handler objects owned by the service. + void shutdown_service() + { + asio::detail::mutex::scoped_lock lock(mutex_); + shutdown_ = true; + lock.unlock(); + + // Destroy handler objects. + while (handler_queue_) + { + handler_base* h = handler_queue_; + handler_queue_ = h->next_; + if (h != &task_handler_) + h->destroy(); + } + + // Reset handler queue to initial state. + handler_queue_ = &task_handler_; + handler_queue_end_ = &task_handler_; + } + + // Run the event loop until interrupted or no more work. + size_t run(asio::error_code& ec) + { + typename call_stack::context ctx(this); + + idle_thread_info this_idle_thread; + this_idle_thread.prev = &this_idle_thread; + this_idle_thread.next = &this_idle_thread; + + asio::detail::mutex::scoped_lock lock(mutex_); + + size_t n = 0; + while (do_one(lock, &this_idle_thread, ec)) + if (n != (std::numeric_limits::max)()) + ++n; + return n; + } + + // Run until interrupted or one operation is performed. + size_t run_one(asio::error_code& ec) + { + typename call_stack::context ctx(this); + + idle_thread_info this_idle_thread; + this_idle_thread.prev = &this_idle_thread; + this_idle_thread.next = &this_idle_thread; + + asio::detail::mutex::scoped_lock lock(mutex_); + + return do_one(lock, &this_idle_thread, ec); + } + + // Poll for operations without blocking. + size_t poll(asio::error_code& ec) + { + typename call_stack::context ctx(this); + + asio::detail::mutex::scoped_lock lock(mutex_); + + size_t n = 0; + while (do_one(lock, 0, ec)) + if (n != (std::numeric_limits::max)()) + ++n; + return n; + } + + // Poll for one operation without blocking. + size_t poll_one(asio::error_code& ec) + { + typename call_stack::context ctx(this); + + asio::detail::mutex::scoped_lock lock(mutex_); + + return do_one(lock, 0, ec); + } + + // Interrupt the event processing loop. + void stop() + { + asio::detail::mutex::scoped_lock lock(mutex_); + stop_all_threads(); + } + + // Reset in preparation for a subsequent run invocation. + void reset() + { + asio::detail::mutex::scoped_lock lock(mutex_); + stopped_ = false; + } + + // Notify that some work has started. + void work_started() + { + asio::detail::mutex::scoped_lock lock(mutex_); + ++outstanding_work_; + } + + // Notify that some work has finished. + void work_finished() + { + asio::detail::mutex::scoped_lock lock(mutex_); + if (--outstanding_work_ == 0) + stop_all_threads(); + } + + // Request invocation of the given handler. + template + void dispatch(Handler handler) + { + if (call_stack::contains(this)) + asio_handler_invoke_helpers::invoke(handler, &handler); + else + post(handler); + } + + // Request invocation of the given handler and return immediately. + template + void post(Handler handler) + { + // Allocate and construct an operation to wrap the handler. + typedef handler_wrapper value_type; + typedef handler_alloc_traits alloc_traits; + raw_handler_ptr raw_ptr(handler); + handler_ptr ptr(raw_ptr, handler); + + asio::detail::mutex::scoped_lock lock(mutex_); + + // If the service has been shut down we silently discard the handler. + if (shutdown_) + return; + + // Add the handler to the end of the queue. + if (handler_queue_end_) + { + handler_queue_end_->next_ = ptr.get(); + handler_queue_end_ = ptr.get(); + } + else + { + handler_queue_ = handler_queue_end_ = ptr.get(); + } + ptr.release(); + + // An undelivered handler is treated as unfinished work. + ++outstanding_work_; + + // Wake up a thread to execute the handler. + if (!interrupt_one_idle_thread()) + if (task_handler_.next_ == 0 && handler_queue_end_ != &task_handler_) + task_.interrupt(); + } + +private: + struct idle_thread_info; + + size_t do_one(asio::detail::mutex::scoped_lock& lock, + idle_thread_info* this_idle_thread, asio::error_code& ec) + { + if (outstanding_work_ == 0 && !stopped_) + { + stop_all_threads(); + ec = asio::error_code(); + return 0; + } + + bool polling = !this_idle_thread; + bool task_has_run = false; + while (!stopped_) + { + if (handler_queue_) + { + // Prepare to execute first handler from queue. + handler_base* h = handler_queue_; + handler_queue_ = h->next_; + if (handler_queue_ == 0) + handler_queue_end_ = 0; + bool more_handlers = (handler_queue_ != 0); + lock.unlock(); + + if (h == &task_handler_) + { + // If the task has already run and we're polling then we're done. + if (task_has_run && polling) + { + ec = asio::error_code(); + return 0; + } + task_has_run = true; + + task_cleanup c(lock, *this); + + // Run the task. May throw an exception. Only block if the handler + // queue is empty and we have an idle_thread_info object, otherwise + // we want to return as soon as possible. + task_.run(!more_handlers && !polling); + } + else + { + handler_cleanup c(lock, *this); + + // Invoke the handler. May throw an exception. + h->call(); // call() deletes the handler object + + ec = asio::error_code(); + return 1; + } + } + else if (this_idle_thread) + { + // Nothing to run right now, so just wait for work to do. + if (first_idle_thread_) + { + this_idle_thread->next = first_idle_thread_; + this_idle_thread->prev = first_idle_thread_->prev; + first_idle_thread_->prev->next = this_idle_thread; + first_idle_thread_->prev = this_idle_thread; + } + first_idle_thread_ = this_idle_thread; + this_idle_thread->wakeup_event.clear(); + lock.unlock(); + this_idle_thread->wakeup_event.wait(); + lock.lock(); + if (this_idle_thread->next == this_idle_thread) + { + first_idle_thread_ = 0; + } + else + { + if (first_idle_thread_ == this_idle_thread) + first_idle_thread_ = this_idle_thread->next; + this_idle_thread->next->prev = this_idle_thread->prev; + this_idle_thread->prev->next = this_idle_thread->next; + this_idle_thread->next = this_idle_thread; + this_idle_thread->prev = this_idle_thread; + } + } + else + { + ec = asio::error_code(); + return 0; + } + } + + ec = asio::error_code(); + return 0; + } + + // Stop the task and all idle threads. + void stop_all_threads() + { + stopped_ = true; + interrupt_all_idle_threads(); + if (task_handler_.next_ == 0 && handler_queue_end_ != &task_handler_) + task_.interrupt(); + } + + // Interrupt a single idle thread. Returns true if a thread was interrupted, + // false if no running thread could be found to interrupt. + bool interrupt_one_idle_thread() + { + if (first_idle_thread_) + { + first_idle_thread_->wakeup_event.signal(); + first_idle_thread_ = first_idle_thread_->next; + return true; + } + return false; + } + + // Interrupt all idle threads. + void interrupt_all_idle_threads() + { + if (first_idle_thread_) + { + first_idle_thread_->wakeup_event.signal(); + idle_thread_info* current_idle_thread = first_idle_thread_->next; + while (current_idle_thread != first_idle_thread_) + { + current_idle_thread->wakeup_event.signal(); + current_idle_thread = current_idle_thread->next; + } + } + } + + class task_cleanup; + friend class task_cleanup; + + // The base class for all handler wrappers. A function pointer is used + // instead of virtual functions to avoid the associated overhead. + class handler_base + { + public: + typedef void (*call_func_type)(handler_base*); + typedef void (*destroy_func_type)(handler_base*); + + handler_base(call_func_type call_func, destroy_func_type destroy_func) + : next_(0), + call_func_(call_func), + destroy_func_(destroy_func) + { + } + + void call() + { + call_func_(this); + } + + void destroy() + { + destroy_func_(this); + } + + protected: + // Prevent deletion through this type. + ~handler_base() + { + } + + private: + friend class task_io_service; + friend class task_cleanup; + handler_base* next_; + call_func_type call_func_; + destroy_func_type destroy_func_; + }; + + // Template wrapper for handlers. + template + class handler_wrapper + : public handler_base + { + public: + handler_wrapper(Handler handler) + : handler_base(&handler_wrapper::do_call, + &handler_wrapper::do_destroy), + handler_(handler) + { + } + + static void do_call(handler_base* base) + { + // Take ownership of the handler object. + typedef handler_wrapper this_type; + this_type* h(static_cast(base)); + typedef handler_alloc_traits alloc_traits; + handler_ptr ptr(h->handler_, h); + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. + Handler handler(h->handler_); + + // Free the memory associated with the handler. + ptr.reset(); + + // Make the upcall. + asio_handler_invoke_helpers::invoke(handler, &handler); + } + + static void do_destroy(handler_base* base) + { + // Take ownership of the handler object. + typedef handler_wrapper this_type; + this_type* h(static_cast(base)); + typedef handler_alloc_traits alloc_traits; + handler_ptr ptr(h->handler_, h); + } + + private: + Handler handler_; + }; + + // Helper class to perform task-related operations on block exit. + class task_cleanup + { + public: + task_cleanup(asio::detail::mutex::scoped_lock& lock, + task_io_service& task_io_svc) + : lock_(lock), + task_io_service_(task_io_svc) + { + } + + ~task_cleanup() + { + // Reinsert the task at the end of the handler queue. + lock_.lock(); + task_io_service_.task_handler_.next_ = 0; + if (task_io_service_.handler_queue_end_) + { + task_io_service_.handler_queue_end_->next_ + = &task_io_service_.task_handler_; + task_io_service_.handler_queue_end_ + = &task_io_service_.task_handler_; + } + else + { + task_io_service_.handler_queue_ + = task_io_service_.handler_queue_end_ + = &task_io_service_.task_handler_; + } + } + + private: + asio::detail::mutex::scoped_lock& lock_; + task_io_service& task_io_service_; + }; + + // Helper class to perform handler-related operations on block exit. + class handler_cleanup; + friend class handler_cleanup; + class handler_cleanup + { + public: + handler_cleanup(asio::detail::mutex::scoped_lock& lock, + task_io_service& task_io_svc) + : lock_(lock), + task_io_service_(task_io_svc) + { + } + + ~handler_cleanup() + { + lock_.lock(); + if (--task_io_service_.outstanding_work_ == 0) + task_io_service_.stop_all_threads(); + } + + private: + asio::detail::mutex::scoped_lock& lock_; + task_io_service& task_io_service_; + }; + + // Mutex to protect access to internal data. + asio::detail::mutex mutex_; + + // The task to be run by this service. + Task& task_; + + // Handler object to represent the position of the task in the queue. + class task_handler + : public handler_base + { + public: + task_handler() + : handler_base(0, 0) + { + } + } task_handler_; + + // The count of unfinished work. + int outstanding_work_; + + // The start of a linked list of handlers that are ready to be delivered. + handler_base* handler_queue_; + + // The end of a linked list of handlers that are ready to be delivered. + handler_base* handler_queue_end_; + + // Flag to indicate that the dispatcher has been stopped. + bool stopped_; + + // Flag to indicate that the dispatcher has been shut down. + bool shutdown_; + + // Structure containing information about an idle thread. + struct idle_thread_info + { + event wakeup_event; + idle_thread_info* prev; + idle_thread_info* next; + }; + + // The number of threads that are currently idle. + idle_thread_info* first_idle_thread_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_TASK_IO_SERVICE_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/detail/task_io_service_fwd.hpp b/encryption/libtorrent/include/libtorrent/asio/detail/task_io_service_fwd.hpp new file mode 100644 index 000000000..208a9d3ca --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/detail/task_io_service_fwd.hpp @@ -0,0 +1,31 @@ +// +// task_io_service_fwd.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_TASK_IO_SERVICE_FWD_HPP +#define ASIO_DETAIL_TASK_IO_SERVICE_FWD_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class task_io_service; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_TASK_IO_SERVICE_FWD_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/detail/thread.hpp b/encryption/libtorrent/include/libtorrent/asio/detail/thread.hpp new file mode 100644 index 000000000..b334c38af --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/detail/thread.hpp @@ -0,0 +1,50 @@ +// +// thread.hpp +// ~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_THREAD_HPP +#define ASIO_DETAIL_THREAD_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include "asio/detail/pop_options.hpp" + +#if !defined(BOOST_HAS_THREADS) +# include "asio/detail/null_thread.hpp" +#elif defined(BOOST_WINDOWS) +# include "asio/detail/win_thread.hpp" +#elif defined(BOOST_HAS_PTHREADS) +# include "asio/detail/posix_thread.hpp" +#else +# error Only Windows and POSIX are supported! +#endif + +namespace asio { +namespace detail { + +#if !defined(BOOST_HAS_THREADS) +typedef null_thread thread; +#elif defined(BOOST_WINDOWS) +typedef win_thread thread; +#elif defined(BOOST_HAS_PTHREADS) +typedef posix_thread thread; +#endif + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_THREAD_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/detail/throw_error.hpp b/encryption/libtorrent/include/libtorrent/asio/detail/throw_error.hpp new file mode 100644 index 000000000..d2ad09a2e --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/detail/throw_error.hpp @@ -0,0 +1,44 @@ +// +// throw_error.hpp +// ~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_THROW_ERROR_HPP +#define ASIO_DETAIL_THROW_ERROR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/error_code.hpp" +#include "asio/system_error.hpp" + +namespace asio { +namespace detail { + +inline void throw_error(const asio::error_code& err) +{ + if (err) + { + asio::system_error e(err); + boost::throw_exception(e); + } +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_THROW_ERROR_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/detail/timer_queue.hpp b/encryption/libtorrent/include/libtorrent/asio/detail/timer_queue.hpp new file mode 100644 index 000000000..ab6eac263 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/detail/timer_queue.hpp @@ -0,0 +1,347 @@ +// +// timer_queue.hpp +// ~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_TIMER_QUEUE_HPP +#define ASIO_DETAIL_TIMER_QUEUE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include +#include +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/error.hpp" +#include "asio/detail/hash_map.hpp" +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/timer_queue_base.hpp" + +namespace asio { +namespace detail { + +template +class timer_queue + : public timer_queue_base +{ +public: + // The time type. + typedef typename Time_Traits::time_type time_type; + + // The duration type. + typedef typename Time_Traits::duration_type duration_type; + + // Constructor. + timer_queue() + : timers_(), + heap_() + { + } + + // Add a new timer to the queue. Returns true if this is the timer that is + // earliest in the queue, in which case the reactor's event demultiplexing + // function call may need to be interrupted and restarted. + template + bool enqueue_timer(const time_type& time, Handler handler, void* token) + { + // Ensure that there is space for the timer in the heap. We reserve here so + // that the push_back below will not throw due to a reallocation failure. + heap_.reserve(heap_.size() + 1); + + // Create a new timer object. + std::auto_ptr > new_timer( + new timer(time, handler, token)); + + // Insert the new timer into the hash. + typedef typename hash_map::iterator iterator; + typedef typename hash_map::value_type value_type; + std::pair result = + timers_.insert(value_type(token, new_timer.get())); + if (!result.second) + { + result.first->second->prev_ = new_timer.get(); + new_timer->next_ = result.first->second; + result.first->second = new_timer.get(); + } + + // Put the timer at the correct position in the heap. + new_timer->heap_index_ = heap_.size(); + heap_.push_back(new_timer.get()); + up_heap(heap_.size() - 1); + bool is_first = (heap_[0] == new_timer.get()); + + // Ownership of the timer is transferred to the timer queue. + new_timer.release(); + + return is_first; + } + + // Whether there are no timers in the queue. + virtual bool empty() const + { + return heap_.empty(); + } + + // Get the time for the timer that is earliest in the queue. + virtual boost::posix_time::time_duration wait_duration() const + { + return Time_Traits::to_posix_duration( + Time_Traits::subtract(heap_[0]->time_, Time_Traits::now())); + } + + // Dispatch the timers that are earlier than the specified time. + virtual void dispatch_timers() + { + const time_type now = Time_Traits::now(); + while (!heap_.empty() && !Time_Traits::less_than(now, heap_[0]->time_)) + { + timer_base* t = heap_[0]; + remove_timer(t); + t->invoke(asio::error_code()); + } + } + + // Cancel the timer with the given token. The handler will be invoked + // immediately with the result operation_aborted. + std::size_t cancel_timer(void* timer_token) + { + std::size_t num_cancelled = 0; + typedef typename hash_map::iterator iterator; + iterator it = timers_.find(timer_token); + if (it != timers_.end()) + { + timer_base* t = it->second; + while (t) + { + timer_base* next = t->next_; + remove_timer(t); + t->invoke(asio::error::operation_aborted); + t = next; + ++num_cancelled; + } + } + return num_cancelled; + } + + // Destroy all timers. + virtual void destroy_timers() + { + typename hash_map::iterator i = timers_.begin(); + typename hash_map::iterator end = timers_.end(); + while (i != end) + { + timer_base* t = i->second; + typename hash_map::iterator old_i = i++; + timers_.erase(old_i); + t->destroy(); + } + heap_.clear(); + timers_.clear(); + } + +private: + // Base class for timer operations. Function pointers are used instead of + // virtual functions to avoid the associated overhead. + class timer_base + { + public: + // Perform the timer operation and then destroy. + void invoke(const asio::error_code& result) + { + invoke_func_(this, result); + } + + // Destroy the timer operation. + void destroy() + { + destroy_func_(this); + } + + protected: + typedef void (*invoke_func_type)(timer_base*, + const asio::error_code&); + typedef void (*destroy_func_type)(timer_base*); + + // Constructor. + timer_base(invoke_func_type invoke_func, destroy_func_type destroy_func, + const time_type& time, void* token) + : invoke_func_(invoke_func), + destroy_func_(destroy_func), + time_(time), + token_(token), + next_(0), + prev_(0), + heap_index_( + std::numeric_limits::max BOOST_PREVENT_MACRO_SUBSTITUTION()) + { + } + + // Prevent deletion through this type. + ~timer_base() + { + } + + private: + friend class timer_queue; + + // The function to be called to dispatch the handler. + invoke_func_type invoke_func_; + + // The function to be called to destroy the handler. + destroy_func_type destroy_func_; + + // The time when the operation should fire. + time_type time_; + + // The token associated with the timer. + void* token_; + + // The next timer known to the queue. + timer_base* next_; + + // The previous timer known to the queue. + timer_base* prev_; + + // The index of the timer in the heap. + size_t heap_index_; + }; + + // Adaptor class template for using handlers in timers. + template + class timer + : public timer_base + { + public: + // Constructor. + timer(const time_type& time, Handler handler, void* token) + : timer_base(&timer::invoke_handler, + &timer::destroy_handler, time, token), + handler_(handler) + { + } + + // Invoke the handler and then destroy it. + static void invoke_handler(timer_base* base, + const asio::error_code& result) + { + std::auto_ptr > t(static_cast*>(base)); + t->handler_(result); + } + + // Destroy the handler. + static void destroy_handler(timer_base* base) + { + delete static_cast*>(base); + } + + private: + Handler handler_; + }; + + // Move the item at the given index up the heap to its correct position. + void up_heap(size_t index) + { + size_t parent = (index - 1) / 2; + while (index > 0 + && Time_Traits::less_than(heap_[index]->time_, heap_[parent]->time_)) + { + swap_heap(index, parent); + index = parent; + parent = (index - 1) / 2; + } + } + + // Move the item at the given index down the heap to its correct position. + void down_heap(size_t index) + { + size_t child = index * 2 + 1; + while (child < heap_.size()) + { + size_t min_child = (child + 1 == heap_.size() + || Time_Traits::less_than( + heap_[child]->time_, heap_[child + 1]->time_)) + ? child : child + 1; + if (Time_Traits::less_than(heap_[index]->time_, heap_[min_child]->time_)) + break; + swap_heap(index, min_child); + index = min_child; + child = index * 2 + 1; + } + } + + // Swap two entries in the heap. + void swap_heap(size_t index1, size_t index2) + { + timer_base* tmp = heap_[index1]; + heap_[index1] = heap_[index2]; + heap_[index2] = tmp; + heap_[index1]->heap_index_ = index1; + heap_[index2]->heap_index_ = index2; + } + + // Remove a timer from the heap and list of timers. + void remove_timer(timer_base* t) + { + // Remove the timer from the heap. + size_t index = t->heap_index_; + if (!heap_.empty() && index < heap_.size()) + { + if (index == heap_.size() - 1) + { + heap_.pop_back(); + } + else + { + swap_heap(index, heap_.size() - 1); + heap_.pop_back(); + size_t parent = (index - 1) / 2; + if (index > 0 && Time_Traits::less_than(t->time_, heap_[parent]->time_)) + up_heap(index); + else + down_heap(index); + } + } + + // Remove the timer from the hash. + typedef typename hash_map::iterator iterator; + iterator it = timers_.find(t->token_); + if (it != timers_.end()) + { + if (it->second == t) + it->second = t->next_; + if (t->prev_) + t->prev_->next_ = t->next_; + if (t->next_) + t->next_->prev_ = t->prev_; + if (it->second == 0) + timers_.erase(it); + } + } + + // A hash of timer token to linked lists of timers. + hash_map timers_; + + // The heap of timers, with the earliest timer at the front. + std::vector heap_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_TIMER_QUEUE_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/detail/timer_queue_base.hpp b/encryption/libtorrent/include/libtorrent/asio/detail/timer_queue_base.hpp new file mode 100644 index 000000000..c8be49748 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/detail/timer_queue_base.hpp @@ -0,0 +1,56 @@ +// +// timer_queue_base.hpp +// ~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_TIMER_QUEUE_BASE_HPP +#define ASIO_DETAIL_TIMER_QUEUE_BASE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/socket_types.hpp" // Must come before posix_time. + +#include "asio/detail/push_options.hpp" +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/detail/noncopyable.hpp" + +namespace asio { +namespace detail { + +class timer_queue_base + : private noncopyable +{ +public: + // Destructor. + virtual ~timer_queue_base() {} + + // Whether there are no timers in the queue. + virtual bool empty() const = 0; + + // Get the time to wait until the next timer. + virtual boost::posix_time::time_duration wait_duration() const = 0; + + // Dispatch all ready timers. + virtual void dispatch_timers() = 0; + + // Destroy all timers. + virtual void destroy_timers() = 0; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_TIMER_QUEUE_BASE_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/detail/tss_ptr.hpp b/encryption/libtorrent/include/libtorrent/asio/detail/tss_ptr.hpp new file mode 100644 index 000000000..8a860a68f --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/detail/tss_ptr.hpp @@ -0,0 +1,65 @@ +// +// tss_ptr.hpp +// ~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_TSS_PTR_HPP +#define ASIO_DETAIL_TSS_PTR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include "asio/detail/pop_options.hpp" + +#if !defined(BOOST_HAS_THREADS) +# include "asio/detail/null_tss_ptr.hpp" +#elif defined(BOOST_WINDOWS) +# include "asio/detail/win_tss_ptr.hpp" +#elif defined(BOOST_HAS_PTHREADS) +# include "asio/detail/posix_tss_ptr.hpp" +#else +# error Only Windows and POSIX are supported! +#endif + +namespace asio { +namespace detail { + +template +class tss_ptr +#if !defined(BOOST_HAS_THREADS) + : public null_tss_ptr +#elif defined(BOOST_WINDOWS) + : public win_tss_ptr +#elif defined(BOOST_HAS_PTHREADS) + : public posix_tss_ptr +#endif +{ +public: + void operator=(T* value) + { +#if !defined(BOOST_HAS_THREADS) + null_tss_ptr::operator=(value); +#elif defined(BOOST_WINDOWS) + win_tss_ptr::operator=(value); +#elif defined(BOOST_HAS_PTHREADS) + posix_tss_ptr::operator=(value); +#endif + } +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_TSS_PTR_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/detail/win_event.hpp b/encryption/libtorrent/include/libtorrent/asio/detail/win_event.hpp new file mode 100644 index 000000000..8de9383da --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/detail/win_event.hpp @@ -0,0 +1,90 @@ +// +// win_event.hpp +// ~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WIN_EVENT_HPP +#define ASIO_DETAIL_WIN_EVENT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include "asio/detail/pop_options.hpp" + +#if defined(BOOST_WINDOWS) + +#include "asio/system_error.hpp" +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/socket_types.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include "asio/detail/pop_options.hpp" + +namespace asio { +namespace detail { + +class win_event + : private noncopyable +{ +public: + // Constructor. + win_event() + : event_(::CreateEvent(0, true, false, 0)) + { + if (!event_) + { + DWORD last_error = ::GetLastError(); + asio::system_error e( + asio::error_code(last_error, asio::native_ecat), + "event"); + boost::throw_exception(e); + } + } + + // Destructor. + ~win_event() + { + ::CloseHandle(event_); + } + + // Signal the event. + void signal() + { + ::SetEvent(event_); + } + + // Reset the event. + void clear() + { + ::ResetEvent(event_); + } + + // Wait for the event to become signalled. + void wait() + { + ::WaitForSingleObject(event_, INFINITE); + } + +private: + HANDLE event_; +}; + +} // namespace detail +} // namespace asio + +#endif // defined(BOOST_WINDOWS) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_WIN_EVENT_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/detail/win_fd_set_adapter.hpp b/encryption/libtorrent/include/libtorrent/asio/detail/win_fd_set_adapter.hpp new file mode 100644 index 000000000..f2632c4d0 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/detail/win_fd_set_adapter.hpp @@ -0,0 +1,84 @@ +// +// win_fd_set_adapter.hpp +// ~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WIN_FD_SET_ADAPTER_HPP +#define ASIO_DETAIL_WIN_FD_SET_ADAPTER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/socket_types.hpp" + +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + +namespace asio { +namespace detail { + +// Adapts the FD_SET type to meet the Descriptor_Set concept's requirements. +class win_fd_set_adapter +{ +public: + enum { win_fd_set_size = 1024 }; + + win_fd_set_adapter() + : max_descriptor_(invalid_socket) + { + fd_set_.fd_count = 0; + } + + void set(socket_type descriptor) + { + for (u_int i = 0; i < fd_set_.fd_count; ++i) + if (fd_set_.fd_array[i] == descriptor) + return; + if (fd_set_.fd_count < win_fd_set_size) + fd_set_.fd_array[fd_set_.fd_count++] = descriptor; + } + + bool is_set(socket_type descriptor) const + { + return !!__WSAFDIsSet(descriptor, + const_cast(reinterpret_cast(&fd_set_))); + } + + operator fd_set*() + { + return reinterpret_cast(&fd_set_); + } + + socket_type max_descriptor() const + { + return max_descriptor_; + } + +private: + // This structure is defined to be compatible with the Windows API fd_set + // structure, but without being dependent on the value of FD_SETSIZE. + struct win_fd_set + { + u_int fd_count; + SOCKET fd_array[win_fd_set_size]; + }; + + win_fd_set fd_set_; + socket_type max_descriptor_; +}; + +} // namespace detail +} // namespace asio + +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_WIN_FD_SET_ADAPTER_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/detail/win_iocp_io_service.hpp b/encryption/libtorrent/include/libtorrent/asio/detail/win_iocp_io_service.hpp new file mode 100644 index 000000000..4957fb01a --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/detail/win_iocp_io_service.hpp @@ -0,0 +1,424 @@ +// +// win_iocp_io_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WIN_IOCP_IO_SERVICE_HPP +#define ASIO_DETAIL_WIN_IOCP_IO_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/win_iocp_io_service_fwd.hpp" + +#if defined(ASIO_HAS_IOCP) + +#include "asio/detail/push_options.hpp" +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/io_service.hpp" +#include "asio/system_error.hpp" +#include "asio/detail/call_stack.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/service_base.hpp" +#include "asio/detail/socket_types.hpp" +#include "asio/detail/win_iocp_operation.hpp" + +namespace asio { +namespace detail { + +class win_iocp_io_service + : public asio::detail::service_base +{ +public: + // Base class for all operations. + typedef win_iocp_operation operation; + + // Constructor. + win_iocp_io_service(asio::io_service& io_service) + : asio::detail::service_base(io_service), + iocp_(), + outstanding_work_(0), + stopped_(0), + shutdown_(0) + { + } + + void init(size_t concurrency_hint) + { + iocp_.handle = ::CreateIoCompletionPort(INVALID_HANDLE_VALUE, 0, 0, + static_cast((std::min)(concurrency_hint, DWORD(~0)))); + if (!iocp_.handle) + { + DWORD last_error = ::GetLastError(); + asio::system_error e( + asio::error_code(last_error, asio::native_ecat), + "iocp"); + boost::throw_exception(e); + } + } + + // Destroy all user-defined handler objects owned by the service. + void shutdown_service() + { + ::InterlockedExchange(&shutdown_, 1); + + for (;;) + { + DWORD bytes_transferred = 0; +#if (WINVER < 0x0500) + DWORD completion_key = 0; +#else + DWORD_PTR completion_key = 0; +#endif + LPOVERLAPPED overlapped = 0; + ::SetLastError(0); + BOOL ok = ::GetQueuedCompletionStatus(iocp_.handle, + &bytes_transferred, &completion_key, &overlapped, 0); + DWORD last_error = ::GetLastError(); + if (!ok && overlapped == 0 && last_error == WAIT_TIMEOUT) + break; + if (overlapped) + static_cast(overlapped)->destroy(); + } + } + + // Register a handle with the IO completion port. + void register_handle(HANDLE handle) + { + ::CreateIoCompletionPort(handle, iocp_.handle, 0, 0); + } + + // Run the event loop until stopped or no more work. + size_t run(asio::error_code& ec) + { + if (::InterlockedExchangeAdd(&outstanding_work_, 0) == 0) + { + ec = asio::error_code(); + return 0; + } + + call_stack::context ctx(this); + + size_t n = 0; + while (do_one(true, ec)) + if (n != (std::numeric_limits::max)()) + ++n; + return n; + } + + // Run until stopped or one operation is performed. + size_t run_one(asio::error_code& ec) + { + if (::InterlockedExchangeAdd(&outstanding_work_, 0) == 0) + { + ec = asio::error_code(); + return 0; + } + + call_stack::context ctx(this); + + return do_one(true, ec); + } + + // Poll for operations without blocking. + size_t poll(asio::error_code& ec) + { + if (::InterlockedExchangeAdd(&outstanding_work_, 0) == 0) + { + ec = asio::error_code(); + return 0; + } + + call_stack::context ctx(this); + + size_t n = 0; + while (do_one(false, ec)) + if (n != (std::numeric_limits::max)()) + ++n; + return n; + } + + // Poll for one operation without blocking. + size_t poll_one(asio::error_code& ec) + { + if (::InterlockedExchangeAdd(&outstanding_work_, 0) == 0) + { + ec = asio::error_code(); + return 0; + } + + call_stack::context ctx(this); + + return do_one(false, ec); + } + + // Stop the event processing loop. + void stop() + { + if (::InterlockedExchange(&stopped_, 1) == 0) + { + if (!::PostQueuedCompletionStatus(iocp_.handle, 0, 0, 0)) + { + DWORD last_error = ::GetLastError(); + asio::system_error e( + asio::error_code(last_error, asio::native_ecat), + "pqcs"); + boost::throw_exception(e); + } + } + } + + // Reset in preparation for a subsequent run invocation. + void reset() + { + ::InterlockedExchange(&stopped_, 0); + } + + // Notify that some work has started. + void work_started() + { + ::InterlockedIncrement(&outstanding_work_); + } + + // Notify that some work has finished. + void work_finished() + { + if (::InterlockedDecrement(&outstanding_work_) == 0) + stop(); + } + + // Request invocation of the given handler. + template + void dispatch(Handler handler) + { + if (call_stack::contains(this)) + asio_handler_invoke_helpers::invoke(handler, &handler); + else + post(handler); + } + + // Request invocation of the given handler and return immediately. + template + void post(Handler handler) + { + // If the service has been shut down we silently discard the handler. + if (::InterlockedExchangeAdd(&shutdown_, 0) != 0) + return; + + // Allocate and construct an operation to wrap the handler. + typedef handler_operation value_type; + typedef handler_alloc_traits alloc_traits; + raw_handler_ptr raw_ptr(handler); + handler_ptr ptr(raw_ptr, *this, handler); + + // Enqueue the operation on the I/O completion port. + if (!::PostQueuedCompletionStatus(iocp_.handle, 0, 0, ptr.get())) + { + DWORD last_error = ::GetLastError(); + asio::system_error e( + asio::error_code(last_error, asio::native_ecat), + "pqcs"); + boost::throw_exception(e); + } + + // Operation has been successfully posted. + ptr.release(); + } + + // Request invocation of the given OVERLAPPED-derived operation. + void post_completion(win_iocp_operation* op, DWORD op_last_error, + DWORD bytes_transferred) + { + // Enqueue the operation on the I/O completion port. + if (!::PostQueuedCompletionStatus(iocp_.handle, + bytes_transferred, op_last_error, op)) + { + DWORD last_error = ::GetLastError(); + asio::system_error e( + asio::error_code(last_error, asio::native_ecat), + "pqcs"); + boost::throw_exception(e); + } + } + +private: + // Dequeues at most one operation from the I/O completion port, and then + // executes it. Returns the number of operations that were dequeued (i.e. + // either 0 or 1). + size_t do_one(bool block, asio::error_code& ec) + { + for (;;) + { + // Get the next operation from the queue. + DWORD bytes_transferred = 0; +#if (WINVER < 0x0500) + DWORD completion_key = 0; +#else + DWORD_PTR completion_key = 0; +#endif + LPOVERLAPPED overlapped = 0; + ::SetLastError(0); + BOOL ok = ::GetQueuedCompletionStatus(iocp_.handle, &bytes_transferred, + &completion_key, &overlapped, block ? 1000 : 0); + DWORD last_error = ::GetLastError(); + + if (!ok && overlapped == 0) + { + if (block && last_error == WAIT_TIMEOUT) + continue; + ec = asio::error_code(); + return 0; + } + + if (overlapped) + { + // We may have been passed a last_error value in the completion_key. + if (last_error == 0) + { + last_error = completion_key; + } + + // Ensure that the io_service does not exit due to running out of work + // while we make the upcall. + auto_work work(*this); + + // Dispatch the operation. + operation* op = static_cast(overlapped); + op->do_completion(last_error, bytes_transferred); + + ec = asio::error_code(); + return 1; + } + else + { + // The stopped_ flag is always checked to ensure that any leftover + // interrupts from a previous run invocation are ignored. + if (::InterlockedExchangeAdd(&stopped_, 0) != 0) + { + // Wake up next thread that is blocked on GetQueuedCompletionStatus. + if (!::PostQueuedCompletionStatus(iocp_.handle, 0, 0, 0)) + { + DWORD last_error = ::GetLastError(); + ec = asio::error_code(last_error, + asio::native_ecat); + return 0; + } + + ec = asio::error_code(); + return 0; + } + } + } + } + + struct auto_work + { + auto_work(win_iocp_io_service& io_service) + : io_service_(io_service) + { + io_service_.work_started(); + } + + ~auto_work() + { + io_service_.work_finished(); + } + + private: + win_iocp_io_service& io_service_; + }; + + template + struct handler_operation + : public operation + { + handler_operation(win_iocp_io_service& io_service, + Handler handler) + : operation(&handler_operation::do_completion_impl, + &handler_operation::destroy_impl), + io_service_(io_service), + handler_(handler) + { + io_service_.work_started(); + } + + ~handler_operation() + { + io_service_.work_finished(); + } + + private: + // Prevent copying and assignment. + handler_operation(const handler_operation&); + void operator=(const handler_operation&); + + static void do_completion_impl(operation* op, DWORD, size_t) + { + // Take ownership of the operation object. + typedef handler_operation op_type; + op_type* handler_op(static_cast(op)); + typedef handler_alloc_traits alloc_traits; + handler_ptr ptr(handler_op->handler_, handler_op); + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. + Handler handler(handler_op->handler_); + + // Free the memory associated with the handler. + ptr.reset(); + + // Make the upcall. + asio_handler_invoke_helpers::invoke(handler, &handler); + } + + static void destroy_impl(operation* op) + { + // Take ownership of the operation object. + typedef handler_operation op_type; + op_type* handler_op(static_cast(op)); + typedef handler_alloc_traits alloc_traits; + handler_ptr ptr(handler_op->handler_, handler_op); + } + + win_iocp_io_service& io_service_; + Handler handler_; + }; + + // The IO completion port used for queueing operations. + struct iocp_holder + { + HANDLE handle; + iocp_holder() : handle(0) {} + ~iocp_holder() { if (handle) ::CloseHandle(handle); } + } iocp_; + + // The count of unfinished work. + long outstanding_work_; + + // Flag to indicate whether the event loop has been stopped. + long stopped_; + + // Flag to indicate whether the service has been shut down. + long shutdown_; +}; + +} // namespace detail +} // namespace asio + +#endif // defined(ASIO_HAS_IOCP) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_WIN_IOCP_IO_SERVICE_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/detail/win_iocp_io_service_fwd.hpp b/encryption/libtorrent/include/libtorrent/asio/detail/win_iocp_io_service_fwd.hpp new file mode 100644 index 000000000..184fdfa18 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/detail/win_iocp_io_service_fwd.hpp @@ -0,0 +1,46 @@ +// +// win_iocp_io_service_fwd.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WIN_IOCP_IO_SERVICE_FWD_HPP +#define ASIO_DETAIL_WIN_IOCP_IO_SERVICE_FWD_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include "asio/detail/pop_options.hpp" + +// This service is only supported on Win32 (NT4 and later). +#if !defined(ASIO_DISABLE_IOCP) +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) +#if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0400) + +// Define this to indicate that IOCP is supported on the target platform. +#define ASIO_HAS_IOCP 1 + +namespace asio { +namespace detail { + +class win_iocp_io_service; + +} // namespace detail +} // namespace asio + +#endif // defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0400) +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) +#endif // !defined(ASIO_DISABLE_IOCP) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_WIN_IOCP_IO_SERVICE_FWD_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/detail/win_iocp_operation.hpp b/encryption/libtorrent/include/libtorrent/asio/detail/win_iocp_operation.hpp new file mode 100644 index 000000000..34b0fad90 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/detail/win_iocp_operation.hpp @@ -0,0 +1,81 @@ +// +// win_iocp_operation.hpp +// ~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WIN_IOCP_OPERATION_HPP +#define ASIO_DETAIL_WIN_IOCP_OPERATION_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/win_iocp_io_service_fwd.hpp" + +#if defined(ASIO_HAS_IOCP) + +#include "asio/detail/socket_types.hpp" + +namespace asio { +namespace detail { + +// Base class for all IOCP operations. A function pointer is used instead of +// virtual functions to avoid the associated overhead. +// +// This class inherits from OVERLAPPED so that we can downcast to get back to +// the win_iocp_operation pointer from the LPOVERLAPPED out parameter of +// GetQueuedCompletionStatus. +struct win_iocp_operation + : public OVERLAPPED +{ + typedef void (*invoke_func_type)(win_iocp_operation*, DWORD, size_t); + typedef void (*destroy_func_type)(win_iocp_operation*); + + win_iocp_operation(invoke_func_type invoke_func, + destroy_func_type destroy_func) + : invoke_func_(invoke_func), + destroy_func_(destroy_func) + { + Internal = 0; + InternalHigh = 0; + Offset = 0; + OffsetHigh = 0; + hEvent = 0; + } + + void do_completion(DWORD last_error, size_t bytes_transferred) + { + invoke_func_(this, last_error, bytes_transferred); + } + + void destroy() + { + destroy_func_(this); + } + +protected: + // Prevent deletion through this type. + ~win_iocp_operation() + { + } + +private: + invoke_func_type invoke_func_; + destroy_func_type destroy_func_; +}; + +} // namespace detail +} // namespace asio + +#endif // defined(ASIO_HAS_IOCP) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_WIN_IOCP_OPERATION_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/detail/win_iocp_socket_service.hpp b/encryption/libtorrent/include/libtorrent/asio/detail/win_iocp_socket_service.hpp new file mode 100644 index 000000000..667553960 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/detail/win_iocp_socket_service.hpp @@ -0,0 +1,1963 @@ +// +// win_iocp_socket_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WIN_IOCP_SOCKET_SERVICE_HPP +#define ASIO_DETAIL_WIN_IOCP_SOCKET_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/win_iocp_io_service_fwd.hpp" + +#if defined(ASIO_HAS_IOCP) + +#include "asio/detail/push_options.hpp" +#include +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/buffer.hpp" +#include "asio/error.hpp" +#include "asio/io_service.hpp" +#include "asio/socket_base.hpp" +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/mutex.hpp" +#include "asio/detail/select_reactor.hpp" +#include "asio/detail/socket_holder.hpp" +#include "asio/detail/socket_ops.hpp" +#include "asio/detail/socket_types.hpp" +#include "asio/detail/win_iocp_io_service.hpp" + +namespace asio { +namespace detail { + +template +class win_iocp_socket_service + : public asio::detail::service_base > +{ +public: + // The protocol type. + typedef Protocol protocol_type; + + // The endpoint type. + typedef typename Protocol::endpoint endpoint_type; + + // Base class for all operations. + typedef win_iocp_operation operation; + + struct noop_deleter { void operator()(void*) {} }; + typedef boost::shared_ptr shared_cancel_token_type; + typedef boost::weak_ptr weak_cancel_token_type; + + // The native type of a socket. + class native_type + { + public: + native_type(socket_type s) + : socket_(s), + have_remote_endpoint_(false) + { + } + + native_type(socket_type s, const endpoint_type& ep) + : socket_(s), + have_remote_endpoint_(true), + remote_endpoint_(ep) + { + } + + void operator=(socket_type s) + { + socket_ = s; + have_remote_endpoint_ = false; + remote_endpoint_ = endpoint_type(); + } + + operator socket_type() const + { + return socket_; + } + + HANDLE as_handle() const + { + return reinterpret_cast(socket_); + } + + bool have_remote_endpoint() const + { + return have_remote_endpoint_; + } + + endpoint_type remote_endpoint() const + { + return remote_endpoint_; + } + + private: + socket_type socket_; + bool have_remote_endpoint_; + endpoint_type remote_endpoint_; + }; + + // The implementation type of the socket. + class implementation_type + { + public: + // Default constructor. + implementation_type() + : socket_(invalid_socket), + flags_(0), + cancel_token_(), + protocol_(endpoint_type().protocol()), + next_(0), + prev_(0) + { + } + + private: + // Only this service will have access to the internal values. + friend class win_iocp_socket_service; + + // The native socket representation. + native_type socket_; + + enum + { + enable_connection_aborted = 1, // User wants connection_aborted errors. + user_set_linger = 2 // The user set the linger option. + }; + + // Flags indicating the current state of the socket. + unsigned char flags_; + + // We use a shared pointer as a cancellation token here to work around the + // broken Windows support for cancellation. MSDN says that when you call + // closesocket any outstanding WSARecv or WSASend operations will complete + // with the error ERROR_OPERATION_ABORTED. In practice they complete with + // ERROR_NETNAME_DELETED, which means you can't tell the difference between + // a local cancellation and the socket being hard-closed by the peer. + shared_cancel_token_type cancel_token_; + + // The protocol associated with the socket. + protocol_type protocol_; + + // The ID of the thread from which it is safe to cancel asynchronous + // operations. 0 means no asynchronous operations have been started yet. + // ~0 means asynchronous operations have been started from more than one + // thread, and cancellation is not supported for the socket. + DWORD safe_cancellation_thread_id_; + + // Pointers to adjacent socket implementations in linked list. + implementation_type* next_; + implementation_type* prev_; + }; + + // The type of the reactor used for connect operations. + typedef detail::select_reactor reactor_type; + + // The maximum number of buffers to support in a single operation. + enum { max_buffers = 16 }; + + // Constructor. + win_iocp_socket_service(asio::io_service& io_service) + : asio::detail::service_base< + win_iocp_socket_service >(io_service), + iocp_service_(asio::use_service(io_service)), + reactor_(0), + mutex_(), + impl_list_(0) + { + } + + // Destroy all user-defined handler objects owned by the service. + void shutdown_service() + { + // Close all implementations, causing all operations to complete. + asio::detail::mutex::scoped_lock lock(mutex_); + implementation_type* impl = impl_list_; + while (impl) + { + asio::error_code ignored_ec; + close(*impl, ignored_ec); + impl = impl->next_; + } + } + + // Construct a new socket implementation. + void construct(implementation_type& impl) + { + impl.socket_ = invalid_socket; + impl.cancel_token_.reset(); + impl.safe_cancellation_thread_id_ = 0; + + // Insert implementation into linked list of all implementations. + asio::detail::mutex::scoped_lock lock(mutex_); + impl.next_ = impl_list_; + impl.prev_ = 0; + if (impl_list_) + impl_list_->prev_ = &impl; + impl_list_ = &impl; + } + + // Destroy a socket implementation. + void destroy(implementation_type& impl) + { + if (impl.socket_ != invalid_socket) + { + // Check if the reactor was created, in which case we need to close the + // socket on the reactor as well to cancel any operations that might be + // running there. + reactor_type* reactor = static_cast( + interlocked_compare_exchange_pointer( + reinterpret_cast(&reactor_), 0, 0)); + if (reactor) + reactor->close_descriptor(impl.socket_); + + if (impl.flags_ & implementation_type::user_set_linger) + { + ::linger opt; + opt.l_onoff = 0; + opt.l_linger = 0; + asio::error_code ignored_ec; + socket_ops::setsockopt(impl.socket_, + SOL_SOCKET, SO_LINGER, &opt, sizeof(opt), ignored_ec); + } + + asio::error_code ignored_ec; + socket_ops::close(impl.socket_, ignored_ec); + impl.socket_ = invalid_socket; + impl.cancel_token_.reset(); + impl.safe_cancellation_thread_id_ = 0; + } + + // Remove implementation from linked list of all implementations. + asio::detail::mutex::scoped_lock lock(mutex_); + if (impl_list_ == &impl) + impl_list_ = impl.next_; + if (impl.prev_) + impl.prev_->next_ = impl.next_; + if (impl.next_) + impl.next_->prev_= impl.prev_; + impl.next_ = 0; + impl.prev_ = 0; + } + + // Open a new socket implementation. + asio::error_code open(implementation_type& impl, + const protocol_type& protocol, asio::error_code& ec) + { + if (is_open(impl)) + { + ec = asio::error::already_open; + return ec; + } + + socket_holder sock(socket_ops::socket(protocol.family(), protocol.type(), + protocol.protocol(), ec)); + if (sock.get() == invalid_socket) + return ec; + + HANDLE sock_as_handle = reinterpret_cast(sock.get()); + iocp_service_.register_handle(sock_as_handle); + + impl.socket_ = sock.release(); + impl.cancel_token_.reset(static_cast(0), noop_deleter()); + impl.protocol_ = protocol; + ec = asio::error_code(); + return ec; + } + + // Assign a native socket to a socket implementation. + asio::error_code assign(implementation_type& impl, + const protocol_type& protocol, const native_type& native_socket, + asio::error_code& ec) + { + if (is_open(impl)) + { + ec = asio::error::already_open; + return ec; + } + + iocp_service_.register_handle(native_socket.as_handle()); + + impl.socket_ = native_socket; + impl.cancel_token_.reset(static_cast(0), noop_deleter()); + impl.protocol_ = protocol; + ec = asio::error_code(); + return ec; + } + + // Determine whether the socket is open. + bool is_open(const implementation_type& impl) const + { + return impl.socket_ != invalid_socket; + } + + // Destroy a socket implementation. + asio::error_code close(implementation_type& impl, + asio::error_code& ec) + { + if (is_open(impl)) + { + // Check if the reactor was created, in which case we need to close the + // socket on the reactor as well to cancel any operations that might be + // running there. + reactor_type* reactor = static_cast( + interlocked_compare_exchange_pointer( + reinterpret_cast(&reactor_), 0, 0)); + if (reactor) + reactor->close_descriptor(impl.socket_); + + if (socket_ops::close(impl.socket_, ec) == socket_error_retval) + return ec; + + impl.socket_ = invalid_socket; + impl.cancel_token_.reset(); + impl.safe_cancellation_thread_id_ = 0; + } + + ec = asio::error_code(); + return ec; + } + + // Get the native socket representation. + native_type native(implementation_type& impl) + { + return impl.socket_; + } + + // Cancel all operations associated with the socket. + asio::error_code cancel(implementation_type& impl, + asio::error_code& ec) + { + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + } + else if (impl.safe_cancellation_thread_id_ == 0) + { + // No operations have been started, so there's nothing to cancel. + ec = asio::error_code(); + } + else if (impl.safe_cancellation_thread_id_ == ::GetCurrentThreadId()) + { + // Asynchronous operations have been started from the current thread only, + // so it is safe to try to cancel them using CancelIo. + socket_type sock = impl.socket_; + HANDLE sock_as_handle = reinterpret_cast(sock); + if (!::CancelIo(sock_as_handle)) + { + DWORD last_error = ::GetLastError(); + ec = asio::error_code(last_error, asio::native_ecat); + } + else + { + ec = asio::error_code(); + } + } + else + { + // Asynchronous operations have been started from more than one thread, + // so cancellation is not safe. + ec = asio::error::operation_not_supported; + } + + return ec; + } + + // Determine whether the socket is at the out-of-band data mark. + bool at_mark(const implementation_type& impl, + asio::error_code& ec) const + { + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return false; + } + + asio::detail::ioctl_arg_type value = 0; + socket_ops::ioctl(impl.socket_, SIOCATMARK, &value, ec); + return ec ? false : value != 0; + } + + // Determine the number of bytes available for reading. + std::size_t available(const implementation_type& impl, + asio::error_code& ec) const + { + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return 0; + } + + asio::detail::ioctl_arg_type value = 0; + socket_ops::ioctl(impl.socket_, FIONREAD, &value, ec); + return ec ? static_cast(0) : static_cast(value); + } + + // Bind the socket to the specified local endpoint. + asio::error_code bind(implementation_type& impl, + const endpoint_type& endpoint, asio::error_code& ec) + { + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return ec; + } + + socket_ops::bind(impl.socket_, endpoint.data(), endpoint.size(), ec); + return ec; + } + + // Place the socket into the state where it will listen for new connections. + asio::error_code listen(implementation_type& impl, int backlog, + asio::error_code& ec) + { + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return ec; + } + + socket_ops::listen(impl.socket_, backlog, ec); + return ec; + } + + // Set a socket option. + template + asio::error_code set_option(implementation_type& impl, + const Option& option, asio::error_code& ec) + { + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return ec; + } + + if (option.level(impl.protocol_) == custom_socket_option_level + && option.name(impl.protocol_) == enable_connection_aborted_option) + { + if (option.size(impl.protocol_) != sizeof(int)) + { + ec = asio::error::invalid_argument; + } + else + { + if (*reinterpret_cast(option.data(impl.protocol_))) + impl.flags_ |= implementation_type::enable_connection_aborted; + else + impl.flags_ &= ~implementation_type::enable_connection_aborted; + ec = asio::error_code(); + } + return ec; + } + else + { + if (option.level(impl.protocol_) == SOL_SOCKET + && option.name(impl.protocol_) == SO_LINGER) + { + impl.flags_ |= implementation_type::user_set_linger; + } + + socket_ops::setsockopt(impl.socket_, + option.level(impl.protocol_), option.name(impl.protocol_), + option.data(impl.protocol_), option.size(impl.protocol_), ec); + return ec; + } + } + + // Set a socket option. + template + asio::error_code get_option(const implementation_type& impl, + Option& option, asio::error_code& ec) const + { + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return ec; + } + + if (option.level(impl.protocol_) == custom_socket_option_level + && option.name(impl.protocol_) == enable_connection_aborted_option) + { + if (option.size(impl.protocol_) != sizeof(int)) + { + ec = asio::error::invalid_argument; + } + else + { + int* target = reinterpret_cast(option.data(impl.protocol_)); + if (impl.flags_ & implementation_type::enable_connection_aborted) + *target = 1; + else + *target = 0; + option.resize(impl.protocol_, sizeof(int)); + ec = asio::error_code(); + } + return ec; + } + else + { + size_t size = option.size(impl.protocol_); + socket_ops::getsockopt(impl.socket_, + option.level(impl.protocol_), option.name(impl.protocol_), + option.data(impl.protocol_), &size, ec); + if (!ec) + option.resize(impl.protocol_, size); + return ec; + } + } + + // Perform an IO control command on the socket. + template + asio::error_code io_control(implementation_type& impl, + IO_Control_Command& command, asio::error_code& ec) + { + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return ec; + } + + socket_ops::ioctl(impl.socket_, command.name(), + static_cast(command.data()), ec); + return ec; + } + + // Get the local endpoint. + endpoint_type local_endpoint(const implementation_type& impl, + asio::error_code& ec) const + { + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return endpoint_type(); + } + + endpoint_type endpoint; + socket_addr_len_type addr_len = endpoint.capacity(); + if (socket_ops::getsockname(impl.socket_, endpoint.data(), &addr_len, ec)) + return endpoint_type(); + endpoint.resize(addr_len); + return endpoint; + } + + // Get the remote endpoint. + endpoint_type remote_endpoint(const implementation_type& impl, + asio::error_code& ec) const + { + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return endpoint_type(); + } + + if (impl.socket_.have_remote_endpoint()) + { + // Check if socket is still connected. + DWORD connect_time = 0; + size_t connect_time_len = sizeof(connect_time); + if (socket_ops::getsockopt(impl.socket_, SOL_SOCKET, SO_CONNECT_TIME, + &connect_time, &connect_time_len, ec) == socket_error_retval) + { + return endpoint_type(); + } + if (connect_time == 0xFFFFFFFF) + { + ec = asio::error::not_connected; + return endpoint_type(); + } + + ec = asio::error_code(); + return impl.socket_.remote_endpoint(); + } + else + { + endpoint_type endpoint; + socket_addr_len_type addr_len = endpoint.capacity(); + if (socket_ops::getpeername(impl.socket_, endpoint.data(), &addr_len, ec)) + return endpoint_type(); + endpoint.resize(addr_len); + return endpoint; + } + } + + /// Disable sends or receives on the socket. + asio::error_code shutdown(implementation_type& impl, + socket_base::shutdown_type what, asio::error_code& ec) + { + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return ec; + } + + socket_ops::shutdown(impl.socket_, what, ec); + return ec; + } + + // Send the given data to the peer. Returns the number of bytes sent. + template + size_t send(implementation_type& impl, const ConstBufferSequence& buffers, + socket_base::message_flags flags, asio::error_code& ec) + { + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return 0; + } + + // Copy buffers into WSABUF array. + ::WSABUF bufs[max_buffers]; + typename ConstBufferSequence::const_iterator iter = buffers.begin(); + typename ConstBufferSequence::const_iterator end = buffers.end(); + DWORD i = 0; + size_t total_buffer_size = 0; + for (; iter != end && i < max_buffers; ++iter, ++i) + { + asio::const_buffer buffer(*iter); + bufs[i].len = static_cast(asio::buffer_size(buffer)); + bufs[i].buf = const_cast( + asio::buffer_cast(buffer)); + total_buffer_size += asio::buffer_size(buffer); + } + + // A request to receive 0 bytes on a stream socket is a no-op. + if (impl.protocol_.type() == SOCK_STREAM && total_buffer_size == 0) + { + ec = asio::error_code(); + return 0; + } + + // Send the data. + DWORD bytes_transferred = 0; + int result = ::WSASend(impl.socket_, bufs, + i, &bytes_transferred, flags, 0, 0); + if (result != 0) + { + DWORD last_error = ::WSAGetLastError(); + if (last_error == ERROR_NETNAME_DELETED) + last_error = WSAECONNRESET; + else if (last_error == ERROR_PORT_UNREACHABLE) + last_error = WSAECONNREFUSED; + ec = asio::error_code(last_error, asio::native_ecat); + return 0; + } + + ec = asio::error_code(); + return bytes_transferred; + } + + template + class send_operation + : public operation + { + public: + send_operation(asio::io_service& io_service, + weak_cancel_token_type cancel_token, + const ConstBufferSequence& buffers, Handler handler) + : operation( + &send_operation::do_completion_impl, + &send_operation::destroy_impl), + work_(io_service), + cancel_token_(cancel_token), + buffers_(buffers), + handler_(handler) + { + } + + private: + static void do_completion_impl(operation* op, + DWORD last_error, size_t bytes_transferred) + { + // Take ownership of the operation object. + typedef send_operation op_type; + op_type* handler_op(static_cast(op)); + typedef handler_alloc_traits alloc_traits; + handler_ptr ptr(handler_op->handler_, handler_op); + +#if defined(ASIO_ENABLE_BUFFER_DEBUGGING) + // Check whether buffers are still valid. + typename ConstBufferSequence::const_iterator iter + = handler_op->buffers_.begin(); + typename ConstBufferSequence::const_iterator end + = handler_op->buffers_.end(); + while (iter != end) + { + asio::const_buffer buffer(*iter); + asio::buffer_cast(buffer); + ++iter; + } +#endif // defined(ASIO_ENABLE_BUFFER_DEBUGGING) + + // Map non-portable errors to their portable counterparts. + asio::error_code ec(last_error, asio::native_ecat); + if (ec.value() == ERROR_NETNAME_DELETED) + { + if (handler_op->cancel_token_.expired()) + ec = asio::error::operation_aborted; + else + ec = asio::error::connection_reset; + } + else if (ec.value() == ERROR_PORT_UNREACHABLE) + { + ec = asio::error::connection_refused; + } + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. + Handler handler(handler_op->handler_); + + // Free the memory associated with the handler. + ptr.reset(); + + // Call the handler. + asio_handler_invoke_helpers::invoke( + detail::bind_handler(handler, ec, bytes_transferred), &handler); + } + + static void destroy_impl(operation* op) + { + // Take ownership of the operation object. + typedef send_operation op_type; + op_type* handler_op(static_cast(op)); + typedef handler_alloc_traits alloc_traits; + handler_ptr ptr(handler_op->handler_, handler_op); + } + + asio::io_service::work work_; + weak_cancel_token_type cancel_token_; + ConstBufferSequence buffers_; + Handler handler_; + }; + + // Start an asynchronous send. The data being sent must be valid for the + // lifetime of the asynchronous operation. + template + void async_send(implementation_type& impl, const ConstBufferSequence& buffers, + socket_base::message_flags flags, Handler handler) + { + if (!is_open(impl)) + { + this->io_service().post(bind_handler(handler, + asio::error::bad_descriptor, 0)); + return; + } + + // Update the ID of the thread from which cancellation is safe. + if (impl.safe_cancellation_thread_id_ == 0) + impl.safe_cancellation_thread_id_ = ::GetCurrentThreadId(); + else if (impl.safe_cancellation_thread_id_ != ::GetCurrentThreadId()) + impl.safe_cancellation_thread_id_ = ~DWORD(0); + + // Allocate and construct an operation to wrap the handler. + typedef send_operation value_type; + typedef handler_alloc_traits alloc_traits; + raw_handler_ptr raw_ptr(handler); + handler_ptr ptr(raw_ptr, + this->io_service(), impl.cancel_token_, buffers, handler); + + // Copy buffers into WSABUF array. + ::WSABUF bufs[max_buffers]; + typename ConstBufferSequence::const_iterator iter = buffers.begin(); + typename ConstBufferSequence::const_iterator end = buffers.end(); + DWORD i = 0; + size_t total_buffer_size = 0; + for (; iter != end && i < max_buffers; ++iter, ++i) + { + asio::const_buffer buffer(*iter); + bufs[i].len = static_cast(asio::buffer_size(buffer)); + bufs[i].buf = const_cast( + asio::buffer_cast(buffer)); + total_buffer_size += asio::buffer_size(buffer); + } + + // A request to receive 0 bytes on a stream socket is a no-op. + if (impl.protocol_.type() == SOCK_STREAM && total_buffer_size == 0) + { + asio::io_service::work work(this->io_service()); + ptr.reset(); + asio::error_code error; + iocp_service_.post(bind_handler(handler, error, 0)); + return; + } + + // Send the data. + DWORD bytes_transferred = 0; + int result = ::WSASend(impl.socket_, bufs, i, + &bytes_transferred, flags, ptr.get(), 0); + DWORD last_error = ::WSAGetLastError(); + + // Check if the operation completed immediately. + if (result != 0 && last_error != WSA_IO_PENDING) + { + asio::io_service::work work(this->io_service()); + ptr.reset(); + asio::error_code ec(last_error, asio::native_ecat); + iocp_service_.post(bind_handler(handler, ec, bytes_transferred)); + } + else + { + ptr.release(); + } + } + + // Send a datagram to the specified endpoint. Returns the number of bytes + // sent. + template + size_t send_to(implementation_type& impl, const ConstBufferSequence& buffers, + const endpoint_type& destination, socket_base::message_flags flags, + asio::error_code& ec) + { + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return 0; + } + + // Copy buffers into WSABUF array. + ::WSABUF bufs[max_buffers]; + typename ConstBufferSequence::const_iterator iter = buffers.begin(); + typename ConstBufferSequence::const_iterator end = buffers.end(); + DWORD i = 0; + for (; iter != end && i < max_buffers; ++iter, ++i) + { + asio::const_buffer buffer(*iter); + bufs[i].len = static_cast(asio::buffer_size(buffer)); + bufs[i].buf = const_cast( + asio::buffer_cast(buffer)); + } + + // Send the data. + DWORD bytes_transferred = 0; + int result = ::WSASendTo(impl.socket_, bufs, i, &bytes_transferred, + flags, destination.data(), destination.size(), 0, 0); + if (result != 0) + { + DWORD last_error = ::WSAGetLastError(); + if (last_error == ERROR_PORT_UNREACHABLE) + last_error = WSAECONNREFUSED; + ec = asio::error_code(last_error, asio::native_ecat); + return 0; + } + + ec = asio::error_code(); + return bytes_transferred; + } + + template + class send_to_operation + : public operation + { + public: + send_to_operation(asio::io_service& io_service, + const ConstBufferSequence& buffers, Handler handler) + : operation( + &send_to_operation::do_completion_impl, + &send_to_operation::destroy_impl), + work_(io_service), + buffers_(buffers), + handler_(handler) + { + } + + private: + static void do_completion_impl(operation* op, + DWORD last_error, size_t bytes_transferred) + { + // Take ownership of the operation object. + typedef send_to_operation op_type; + op_type* handler_op(static_cast(op)); + typedef handler_alloc_traits alloc_traits; + handler_ptr ptr(handler_op->handler_, handler_op); + +#if defined(ASIO_ENABLE_BUFFER_DEBUGGING) + // Check whether buffers are still valid. + typename ConstBufferSequence::const_iterator iter + = handler_op->buffers_.begin(); + typename ConstBufferSequence::const_iterator end + = handler_op->buffers_.end(); + while (iter != end) + { + asio::const_buffer buffer(*iter); + asio::buffer_cast(buffer); + ++iter; + } +#endif // defined(ASIO_ENABLE_BUFFER_DEBUGGING) + + // Map non-portable errors to their portable counterparts. + asio::error_code ec(last_error, asio::native_ecat); + if (ec.value() == ERROR_PORT_UNREACHABLE) + { + ec = asio::error::connection_refused; + } + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. + Handler handler(handler_op->handler_); + + // Free the memory associated with the handler. + ptr.reset(); + + // Call the handler. + asio_handler_invoke_helpers::invoke( + detail::bind_handler(handler, ec, bytes_transferred), &handler); + } + + static void destroy_impl(operation* op) + { + // Take ownership of the operation object. + typedef send_to_operation op_type; + op_type* handler_op(static_cast(op)); + typedef handler_alloc_traits alloc_traits; + handler_ptr ptr(handler_op->handler_, handler_op); + } + + asio::io_service::work work_; + ConstBufferSequence buffers_; + Handler handler_; + }; + + // Start an asynchronous send. The data being sent must be valid for the + // lifetime of the asynchronous operation. + template + void async_send_to(implementation_type& impl, + const ConstBufferSequence& buffers, const endpoint_type& destination, + socket_base::message_flags flags, Handler handler) + { + if (!is_open(impl)) + { + this->io_service().post(bind_handler(handler, + asio::error::bad_descriptor, 0)); + return; + } + + // Update the ID of the thread from which cancellation is safe. + if (impl.safe_cancellation_thread_id_ == 0) + impl.safe_cancellation_thread_id_ = ::GetCurrentThreadId(); + else if (impl.safe_cancellation_thread_id_ != ::GetCurrentThreadId()) + impl.safe_cancellation_thread_id_ = ~DWORD(0); + + // Allocate and construct an operation to wrap the handler. + typedef send_to_operation value_type; + typedef handler_alloc_traits alloc_traits; + raw_handler_ptr raw_ptr(handler); + handler_ptr ptr(raw_ptr, + this->io_service(), buffers, handler); + + // Copy buffers into WSABUF array. + ::WSABUF bufs[max_buffers]; + typename ConstBufferSequence::const_iterator iter = buffers.begin(); + typename ConstBufferSequence::const_iterator end = buffers.end(); + DWORD i = 0; + for (; iter != end && i < max_buffers; ++iter, ++i) + { + asio::const_buffer buffer(*iter); + bufs[i].len = static_cast(asio::buffer_size(buffer)); + bufs[i].buf = const_cast( + asio::buffer_cast(buffer)); + } + + // Send the data. + DWORD bytes_transferred = 0; + int result = ::WSASendTo(impl.socket_, bufs, i, &bytes_transferred, + flags, destination.data(), destination.size(), ptr.get(), 0); + DWORD last_error = ::WSAGetLastError(); + + // Check if the operation completed immediately. + if (result != 0 && last_error != WSA_IO_PENDING) + { + asio::io_service::work work(this->io_service()); + ptr.reset(); + asio::error_code ec(last_error, asio::native_ecat); + iocp_service_.post(bind_handler(handler, ec, bytes_transferred)); + } + else + { + ptr.release(); + } + } + + // Receive some data from the peer. Returns the number of bytes received. + template + size_t receive(implementation_type& impl, + const MutableBufferSequence& buffers, + socket_base::message_flags flags, asio::error_code& ec) + { + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return 0; + } + + // Copy buffers into WSABUF array. + ::WSABUF bufs[max_buffers]; + typename MutableBufferSequence::const_iterator iter = buffers.begin(); + typename MutableBufferSequence::const_iterator end = buffers.end(); + DWORD i = 0; + size_t total_buffer_size = 0; + for (; iter != end && i < max_buffers; ++iter, ++i) + { + asio::mutable_buffer buffer(*iter); + bufs[i].len = static_cast(asio::buffer_size(buffer)); + bufs[i].buf = asio::buffer_cast(buffer); + total_buffer_size += asio::buffer_size(buffer); + } + + // A request to receive 0 bytes on a stream socket is a no-op. + if (impl.protocol_.type() == SOCK_STREAM && total_buffer_size == 0) + { + ec = asio::error_code(); + return 0; + } + + // Receive some data. + DWORD bytes_transferred = 0; + DWORD recv_flags = flags; + int result = ::WSARecv(impl.socket_, bufs, i, + &bytes_transferred, &recv_flags, 0, 0); + if (result != 0) + { + DWORD last_error = ::WSAGetLastError(); + if (last_error == ERROR_NETNAME_DELETED) + last_error = WSAECONNRESET; + else if (last_error == ERROR_PORT_UNREACHABLE) + last_error = WSAECONNREFUSED; + ec = asio::error_code(last_error, asio::native_ecat); + return 0; + } + if (bytes_transferred == 0) + { + ec = asio::error::eof; + return 0; + } + + ec = asio::error_code(); + return bytes_transferred; + } + + template + class receive_operation + : public operation + { + public: + receive_operation(asio::io_service& io_service, + weak_cancel_token_type cancel_token, + const MutableBufferSequence& buffers, Handler handler) + : operation( + &receive_operation< + MutableBufferSequence, Handler>::do_completion_impl, + &receive_operation< + MutableBufferSequence, Handler>::destroy_impl), + work_(io_service), + cancel_token_(cancel_token), + buffers_(buffers), + handler_(handler) + { + } + + private: + static void do_completion_impl(operation* op, + DWORD last_error, size_t bytes_transferred) + { + // Take ownership of the operation object. + typedef receive_operation op_type; + op_type* handler_op(static_cast(op)); + typedef handler_alloc_traits alloc_traits; + handler_ptr ptr(handler_op->handler_, handler_op); + +#if defined(ASIO_ENABLE_BUFFER_DEBUGGING) + // Check whether buffers are still valid. + typename MutableBufferSequence::const_iterator iter + = handler_op->buffers_.begin(); + typename MutableBufferSequence::const_iterator end + = handler_op->buffers_.end(); + while (iter != end) + { + asio::mutable_buffer buffer(*iter); + asio::buffer_cast(buffer); + ++iter; + } +#endif // defined(ASIO_ENABLE_BUFFER_DEBUGGING) + + // Map non-portable errors to their portable counterparts. + asio::error_code ec(last_error, asio::native_ecat); + if (ec.value() == ERROR_NETNAME_DELETED) + { + if (handler_op->cancel_token_.expired()) + ec = asio::error::operation_aborted; + else + ec = asio::error::connection_reset; + } + else if (ec.value() == ERROR_PORT_UNREACHABLE) + { + ec = asio::error::connection_refused; + } + + // Check for connection closed. + else if (!ec && bytes_transferred == 0) + { + ec = asio::error::eof; + } + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. + Handler handler(handler_op->handler_); + + // Free the memory associated with the handler. + ptr.reset(); + + // Call the handler. + asio_handler_invoke_helpers::invoke( + detail::bind_handler(handler, ec, bytes_transferred), &handler); + } + + static void destroy_impl(operation* op) + { + // Take ownership of the operation object. + typedef receive_operation op_type; + op_type* handler_op(static_cast(op)); + typedef handler_alloc_traits alloc_traits; + handler_ptr ptr(handler_op->handler_, handler_op); + } + + asio::io_service::work work_; + weak_cancel_token_type cancel_token_; + MutableBufferSequence buffers_; + Handler handler_; + }; + + // Start an asynchronous receive. The buffer for the data being received + // must be valid for the lifetime of the asynchronous operation. + template + void async_receive(implementation_type& impl, + const MutableBufferSequence& buffers, + socket_base::message_flags flags, Handler handler) + { + if (!is_open(impl)) + { + this->io_service().post(bind_handler(handler, + asio::error::bad_descriptor, 0)); + return; + } + + // Update the ID of the thread from which cancellation is safe. + if (impl.safe_cancellation_thread_id_ == 0) + impl.safe_cancellation_thread_id_ = ::GetCurrentThreadId(); + else if (impl.safe_cancellation_thread_id_ != ::GetCurrentThreadId()) + impl.safe_cancellation_thread_id_ = ~DWORD(0); + + // Allocate and construct an operation to wrap the handler. + typedef receive_operation value_type; + typedef handler_alloc_traits alloc_traits; + raw_handler_ptr raw_ptr(handler); + handler_ptr ptr(raw_ptr, + this->io_service(), impl.cancel_token_, buffers, handler); + + // Copy buffers into WSABUF array. + ::WSABUF bufs[max_buffers]; + typename MutableBufferSequence::const_iterator iter = buffers.begin(); + typename MutableBufferSequence::const_iterator end = buffers.end(); + DWORD i = 0; + size_t total_buffer_size = 0; + for (; iter != end && i < max_buffers; ++iter, ++i) + { + asio::mutable_buffer buffer(*iter); + bufs[i].len = static_cast(asio::buffer_size(buffer)); + bufs[i].buf = asio::buffer_cast(buffer); + total_buffer_size += asio::buffer_size(buffer); + } + + // A request to receive 0 bytes on a stream socket is a no-op. + if (impl.protocol_.type() == SOCK_STREAM && total_buffer_size == 0) + { + asio::io_service::work work(this->io_service()); + ptr.reset(); + asio::error_code error; + iocp_service_.post(bind_handler(handler, error, 0)); + return; + } + + // Receive some data. + DWORD bytes_transferred = 0; + DWORD recv_flags = flags; + int result = ::WSARecv(impl.socket_, bufs, i, + &bytes_transferred, &recv_flags, ptr.get(), 0); + DWORD last_error = ::WSAGetLastError(); + if (result != 0 && last_error != WSA_IO_PENDING) + { + asio::io_service::work work(this->io_service()); + ptr.reset(); + asio::error_code ec(last_error, asio::native_ecat); + iocp_service_.post(bind_handler(handler, ec, bytes_transferred)); + } + else + { + ptr.release(); + } + } + + // Receive a datagram with the endpoint of the sender. Returns the number of + // bytes received. + template + size_t receive_from(implementation_type& impl, + const MutableBufferSequence& buffers, + endpoint_type& sender_endpoint, socket_base::message_flags flags, + asio::error_code& ec) + { + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return 0; + } + + // Copy buffers into WSABUF array. + ::WSABUF bufs[max_buffers]; + typename MutableBufferSequence::const_iterator iter = buffers.begin(); + typename MutableBufferSequence::const_iterator end = buffers.end(); + DWORD i = 0; + for (; iter != end && i < max_buffers; ++iter, ++i) + { + asio::mutable_buffer buffer(*iter); + bufs[i].len = static_cast(asio::buffer_size(buffer)); + bufs[i].buf = asio::buffer_cast(buffer); + } + + // Receive some data. + DWORD bytes_transferred = 0; + DWORD recv_flags = flags; + int endpoint_size = sender_endpoint.capacity(); + int result = ::WSARecvFrom(impl.socket_, bufs, i, &bytes_transferred, + &recv_flags, sender_endpoint.data(), &endpoint_size, 0, 0); + if (result != 0) + { + DWORD last_error = ::WSAGetLastError(); + if (last_error == ERROR_PORT_UNREACHABLE) + last_error = WSAECONNREFUSED; + ec = asio::error_code(last_error, asio::native_ecat); + return 0; + } + if (bytes_transferred == 0) + { + ec = asio::error::eof; + return 0; + } + + sender_endpoint.resize(endpoint_size); + + ec = asio::error_code(); + return bytes_transferred; + } + + template + class receive_from_operation + : public operation + { + public: + receive_from_operation(asio::io_service& io_service, + endpoint_type& endpoint, const MutableBufferSequence& buffers, + Handler handler) + : operation( + &receive_from_operation< + MutableBufferSequence, Handler>::do_completion_impl, + &receive_from_operation< + MutableBufferSequence, Handler>::destroy_impl), + endpoint_(endpoint), + endpoint_size_(endpoint.capacity()), + work_(io_service), + buffers_(buffers), + handler_(handler) + { + } + + int& endpoint_size() + { + return endpoint_size_; + } + + private: + static void do_completion_impl(operation* op, + DWORD last_error, size_t bytes_transferred) + { + // Take ownership of the operation object. + typedef receive_from_operation op_type; + op_type* handler_op(static_cast(op)); + typedef handler_alloc_traits alloc_traits; + handler_ptr ptr(handler_op->handler_, handler_op); + +#if defined(ASIO_ENABLE_BUFFER_DEBUGGING) + // Check whether buffers are still valid. + typename MutableBufferSequence::const_iterator iter + = handler_op->buffers_.begin(); + typename MutableBufferSequence::const_iterator end + = handler_op->buffers_.end(); + while (iter != end) + { + asio::mutable_buffer buffer(*iter); + asio::buffer_cast(buffer); + ++iter; + } +#endif // defined(ASIO_ENABLE_BUFFER_DEBUGGING) + + // Map non-portable errors to their portable counterparts. + asio::error_code ec(last_error, asio::native_ecat); + if (ec.value() == ERROR_PORT_UNREACHABLE) + { + ec = asio::error::connection_refused; + } + + // Check for connection closed. + if (!ec && bytes_transferred == 0) + { + ec = asio::error::eof; + } + + // Record the size of the endpoint returned by the operation. + handler_op->endpoint_.resize(handler_op->endpoint_size_); + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. + Handler handler(handler_op->handler_); + + // Free the memory associated with the handler. + ptr.reset(); + + // Call the handler. + asio_handler_invoke_helpers::invoke( + detail::bind_handler(handler, ec, bytes_transferred), &handler); + } + + static void destroy_impl(operation* op) + { + // Take ownership of the operation object. + typedef receive_from_operation op_type; + op_type* handler_op(static_cast(op)); + typedef handler_alloc_traits alloc_traits; + handler_ptr ptr(handler_op->handler_, handler_op); + } + + endpoint_type& endpoint_; + int endpoint_size_; + asio::io_service::work work_; + MutableBufferSequence buffers_; + Handler handler_; + }; + + // Start an asynchronous receive. The buffer for the data being received and + // the sender_endpoint object must both be valid for the lifetime of the + // asynchronous operation. + template + void async_receive_from(implementation_type& impl, + const MutableBufferSequence& buffers, endpoint_type& sender_endp, + socket_base::message_flags flags, Handler handler) + { + if (!is_open(impl)) + { + this->io_service().post(bind_handler(handler, + asio::error::bad_descriptor, 0)); + return; + } + + // Update the ID of the thread from which cancellation is safe. + if (impl.safe_cancellation_thread_id_ == 0) + impl.safe_cancellation_thread_id_ = ::GetCurrentThreadId(); + else if (impl.safe_cancellation_thread_id_ != ::GetCurrentThreadId()) + impl.safe_cancellation_thread_id_ = ~DWORD(0); + + // Allocate and construct an operation to wrap the handler. + typedef receive_from_operation value_type; + typedef handler_alloc_traits alloc_traits; + raw_handler_ptr raw_ptr(handler); + handler_ptr ptr(raw_ptr, + this->io_service(), sender_endp, buffers, handler); + + // Copy buffers into WSABUF array. + ::WSABUF bufs[max_buffers]; + typename MutableBufferSequence::const_iterator iter = buffers.begin(); + typename MutableBufferSequence::const_iterator end = buffers.end(); + DWORD i = 0; + for (; iter != end && i < max_buffers; ++iter, ++i) + { + asio::mutable_buffer buffer(*iter); + bufs[i].len = static_cast(asio::buffer_size(buffer)); + bufs[i].buf = asio::buffer_cast(buffer); + } + + // Receive some data. + DWORD bytes_transferred = 0; + DWORD recv_flags = flags; + int result = ::WSARecvFrom(impl.socket_, bufs, i, &bytes_transferred, + &recv_flags, sender_endp.data(), &ptr.get()->endpoint_size(), + ptr.get(), 0); + DWORD last_error = ::WSAGetLastError(); + if (result != 0 && last_error != WSA_IO_PENDING) + { + asio::io_service::work work(this->io_service()); + ptr.reset(); + asio::error_code ec(last_error, asio::native_ecat); + iocp_service_.post(bind_handler(handler, ec, bytes_transferred)); + } + else + { + ptr.release(); + } + } + + // Accept a new connection. + template + asio::error_code accept(implementation_type& impl, Socket& peer, + endpoint_type* peer_endpoint, asio::error_code& ec) + { + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return ec; + } + + // We cannot accept a socket that is already open. + if (peer.is_open()) + { + ec = asio::error::already_open; + return ec; + } + + for (;;) + { + asio::error_code ec; + socket_holder new_socket; + socket_addr_len_type addr_len = 0; + if (peer_endpoint) + { + addr_len = peer_endpoint->capacity(); + new_socket.reset(socket_ops::accept(impl.socket_, + peer_endpoint->data(), &addr_len, ec)); + } + else + { + new_socket.reset(socket_ops::accept(impl.socket_, 0, 0, ec)); + } + + if (ec) + { + if (ec == asio::error::connection_aborted + && !(impl.flags_ & implementation_type::enable_connection_aborted)) + { + // Retry accept operation. + continue; + } + else + { + return ec; + } + } + + if (peer_endpoint) + peer_endpoint->resize(addr_len); + + peer.assign(impl.protocol_, new_socket.get(), ec); + if (!ec) + new_socket.release(); + return ec; + } + } + + template + class accept_operation + : public operation + { + public: + accept_operation(win_iocp_io_service& io_service, + socket_type socket, socket_type new_socket, Socket& peer, + const protocol_type& protocol, endpoint_type* peer_endpoint, + bool enable_connection_aborted, Handler handler) + : operation( + &accept_operation::do_completion_impl, + &accept_operation::destroy_impl), + io_service_(io_service), + socket_(socket), + new_socket_(new_socket), + peer_(peer), + protocol_(protocol), + peer_endpoint_(peer_endpoint), + work_(io_service.io_service()), + enable_connection_aborted_(enable_connection_aborted), + handler_(handler) + { + } + + socket_type new_socket() + { + return new_socket_.get(); + } + + void* output_buffer() + { + return output_buffer_; + } + + DWORD address_length() + { + return sizeof(sockaddr_storage_type) + 16; + } + + private: + static void do_completion_impl(operation* op, + DWORD last_error, size_t bytes_transferred) + { + // Take ownership of the operation object. + typedef accept_operation op_type; + op_type* handler_op(static_cast(op)); + typedef handler_alloc_traits alloc_traits; + handler_ptr ptr(handler_op->handler_, handler_op); + + // Map Windows error ERROR_NETNAME_DELETED to connection_aborted. + if (last_error == ERROR_NETNAME_DELETED) + { + last_error = WSAECONNABORTED; + } + + // Restart the accept operation if we got the connection_aborted error + // and the enable_connection_aborted socket option is not set. + if (last_error == WSAECONNABORTED + && !ptr.get()->enable_connection_aborted_) + { + // Reset OVERLAPPED structure. + ptr.get()->Internal = 0; + ptr.get()->InternalHigh = 0; + ptr.get()->Offset = 0; + ptr.get()->OffsetHigh = 0; + ptr.get()->hEvent = 0; + + // Create a new socket for the next connection, since the AcceptEx call + // fails with WSAEINVAL if we try to reuse the same socket. + asio::error_code ec; + ptr.get()->new_socket_.reset(); + ptr.get()->new_socket_.reset(socket_ops::socket( + ptr.get()->protocol_.family(), ptr.get()->protocol_.type(), + ptr.get()->protocol_.protocol(), ec)); + if (ptr.get()->new_socket() != invalid_socket) + { + // Accept a connection. + DWORD bytes_read = 0; + BOOL result = ::AcceptEx(ptr.get()->socket_, ptr.get()->new_socket(), + ptr.get()->output_buffer(), 0, ptr.get()->address_length(), + ptr.get()->address_length(), &bytes_read, ptr.get()); + last_error = ::WSAGetLastError(); + + // Check if the operation completed immediately. + if (!result && last_error != WSA_IO_PENDING) + { + if (last_error == ERROR_NETNAME_DELETED + || last_error == WSAECONNABORTED) + { + // Post this handler so that operation will be restarted again. + ptr.get()->io_service_.post_completion(ptr.get(), last_error, 0); + ptr.release(); + return; + } + else + { + // Operation already complete. Continue with rest of this handler. + } + } + else + { + // Asynchronous operation has been successfully restarted. + ptr.release(); + return; + } + } + } + + // Get the address of the peer. + endpoint_type peer_endpoint; + if (last_error == 0) + { + LPSOCKADDR local_addr = 0; + int local_addr_length = 0; + LPSOCKADDR remote_addr = 0; + int remote_addr_length = 0; + GetAcceptExSockaddrs(handler_op->output_buffer(), 0, + handler_op->address_length(), handler_op->address_length(), + &local_addr, &local_addr_length, &remote_addr, &remote_addr_length); + if (remote_addr_length > peer_endpoint.capacity()) + { + last_error = WSAEINVAL; + } + else + { + using namespace std; // For memcpy. + memcpy(peer_endpoint.data(), remote_addr, remote_addr_length); + peer_endpoint.resize(remote_addr_length); + } + } + + // Need to set the SO_UPDATE_ACCEPT_CONTEXT option so that getsockname + // and getpeername will work on the accepted socket. + if (last_error == 0) + { + SOCKET update_ctx_param = handler_op->socket_; + asio::error_code ec; + if (socket_ops::setsockopt(handler_op->new_socket_.get(), + SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT, + &update_ctx_param, sizeof(SOCKET), ec) != 0) + { + last_error = ec.value(); + } + } + + // If the socket was successfully accepted, transfer ownership of the + // socket to the peer object. + if (last_error == 0) + { + asio::error_code ec; + handler_op->peer_.assign(handler_op->protocol_, + native_type(handler_op->new_socket_.get(), peer_endpoint), ec); + if (ec) + last_error = ec.value(); + else + handler_op->new_socket_.release(); + } + + // Pass endpoint back to caller. + if (handler_op->peer_endpoint_) + *handler_op->peer_endpoint_ = peer_endpoint; + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. + Handler handler(handler_op->handler_); + + // Free the memory associated with the handler. + ptr.reset(); + + // Call the handler. + asio::error_code ec(last_error, asio::native_ecat); + asio_handler_invoke_helpers::invoke( + detail::bind_handler(handler, ec), &handler); + } + + static void destroy_impl(operation* op) + { + // Take ownership of the operation object. + typedef accept_operation op_type; + op_type* handler_op(static_cast(op)); + typedef handler_alloc_traits alloc_traits; + handler_ptr ptr(handler_op->handler_, handler_op); + } + + win_iocp_io_service& io_service_; + socket_type socket_; + socket_holder new_socket_; + Socket& peer_; + protocol_type protocol_; + endpoint_type* peer_endpoint_; + asio::io_service::work work_; + unsigned char output_buffer_[(sizeof(sockaddr_storage_type) + 16) * 2]; + bool enable_connection_aborted_; + Handler handler_; + }; + + // Start an asynchronous accept. The peer and peer_endpoint objects + // must be valid until the accept's handler is invoked. + template + void async_accept(implementation_type& impl, Socket& peer, + endpoint_type* peer_endpoint, Handler handler) + { + // Check whether acceptor has been initialised. + if (!is_open(impl)) + { + this->io_service().post(bind_handler(handler, + asio::error::bad_descriptor)); + return; + } + + // Check that peer socket has not already been opened. + if (peer.is_open()) + { + this->io_service().post(bind_handler(handler, + asio::error::already_open)); + return; + } + + // Update the ID of the thread from which cancellation is safe. + if (impl.safe_cancellation_thread_id_ == 0) + impl.safe_cancellation_thread_id_ = ::GetCurrentThreadId(); + else if (impl.safe_cancellation_thread_id_ != ::GetCurrentThreadId()) + impl.safe_cancellation_thread_id_ = ~DWORD(0); + + // Create a new socket for the connection. + asio::error_code ec; + socket_holder sock(socket_ops::socket(impl.protocol_.family(), + impl.protocol_.type(), impl.protocol_.protocol(), ec)); + if (sock.get() == invalid_socket) + { + this->io_service().post(bind_handler(handler, ec)); + return; + } + + // Allocate and construct an operation to wrap the handler. + typedef accept_operation value_type; + typedef handler_alloc_traits alloc_traits; + raw_handler_ptr raw_ptr(handler); + socket_type new_socket = sock.get(); + bool enable_connection_aborted = + (impl.flags_ & implementation_type::enable_connection_aborted); + handler_ptr ptr(raw_ptr, + iocp_service_, impl.socket_, new_socket, peer, impl.protocol_, + peer_endpoint, enable_connection_aborted, handler); + sock.release(); + + // Accept a connection. + DWORD bytes_read = 0; + BOOL result = ::AcceptEx(impl.socket_, ptr.get()->new_socket(), + ptr.get()->output_buffer(), 0, ptr.get()->address_length(), + ptr.get()->address_length(), &bytes_read, ptr.get()); + DWORD last_error = ::WSAGetLastError(); + + // Check if the operation completed immediately. + if (!result && last_error != WSA_IO_PENDING) + { + if (!enable_connection_aborted + && (last_error == ERROR_NETNAME_DELETED + || last_error == WSAECONNABORTED)) + { + // Post handler so that operation will be restarted again. We do not + // perform the AcceptEx again here to avoid the possibility of starving + // other handlers. + iocp_service_.post_completion(ptr.get(), last_error, 0); + ptr.release(); + } + else + { + asio::io_service::work work(this->io_service()); + ptr.reset(); + asio::error_code ec(last_error, asio::native_ecat); + iocp_service_.post(bind_handler(handler, ec)); + } + } + else + { + ptr.release(); + } + } + + // Connect the socket to the specified endpoint. + asio::error_code connect(implementation_type& impl, + const endpoint_type& peer_endpoint, asio::error_code& ec) + { + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return ec; + } + + // Perform the connect operation. + socket_ops::connect(impl.socket_, + peer_endpoint.data(), peer_endpoint.size(), ec); + return ec; + } + + template + class connect_handler + { + public: + connect_handler(socket_type socket, + boost::shared_ptr completed, + asio::io_service& io_service, + reactor_type& reactor, Handler handler) + : socket_(socket), + completed_(completed), + io_service_(io_service), + reactor_(reactor), + work_(io_service), + handler_(handler) + { + } + + bool operator()(const asio::error_code& result) + { + // Check whether a handler has already been called for the connection. + // If it has, then we don't want to do anything in this handler. + if (*completed_) + return true; + + // Cancel the other reactor operation for the connection. + *completed_ = true; + reactor_.enqueue_cancel_ops_unlocked(socket_); + + // Check whether the operation was successful. + if (result) + { + io_service_.post(bind_handler(handler_, result)); + return true; + } + + // Get the error code from the connect operation. + int connect_error = 0; + size_t connect_error_len = sizeof(connect_error); + asio::error_code ec; + if (socket_ops::getsockopt(socket_, SOL_SOCKET, SO_ERROR, + &connect_error, &connect_error_len, ec) == socket_error_retval) + { + io_service_.post(bind_handler(handler_, ec)); + return true; + } + + // If connection failed then post the handler with the error code. + if (connect_error) + { + ec = asio::error_code( + connect_error, asio::native_ecat); + io_service_.post(bind_handler(handler_, ec)); + return true; + } + + // Make the socket blocking again (the default). + ioctl_arg_type non_blocking = 0; + if (socket_ops::ioctl(socket_, FIONBIO, &non_blocking, ec)) + { + io_service_.post(bind_handler(handler_, ec)); + return true; + } + + // Post the result of the successful connection operation. + ec = asio::error_code(); + io_service_.post(bind_handler(handler_, ec)); + return true; + } + + private: + socket_type socket_; + boost::shared_ptr completed_; + asio::io_service& io_service_; + reactor_type& reactor_; + asio::io_service::work work_; + Handler handler_; + }; + + // Start an asynchronous connect. + template + void async_connect(implementation_type& impl, + const endpoint_type& peer_endpoint, Handler handler) + { + if (!is_open(impl)) + { + this->io_service().post(bind_handler(handler, + asio::error::bad_descriptor)); + return; + } + + // Update the ID of the thread from which cancellation is safe. + if (impl.safe_cancellation_thread_id_ == 0) + impl.safe_cancellation_thread_id_ = ::GetCurrentThreadId(); + else if (impl.safe_cancellation_thread_id_ != ::GetCurrentThreadId()) + impl.safe_cancellation_thread_id_ = ~DWORD(0); + + // Check if the reactor was already obtained from the io_service. + reactor_type* reactor = static_cast( + interlocked_compare_exchange_pointer( + reinterpret_cast(&reactor_), 0, 0)); + if (!reactor) + { + reactor = &(asio::use_service(this->io_service())); + interlocked_exchange_pointer( + reinterpret_cast(&reactor_), reactor); + } + + // Mark the socket as non-blocking so that the connection will take place + // asynchronously. + ioctl_arg_type non_blocking = 1; + asio::error_code ec; + if (socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ec)) + { + this->io_service().post(bind_handler(handler, ec)); + return; + } + + // Start the connect operation. + if (socket_ops::connect(impl.socket_, peer_endpoint.data(), + peer_endpoint.size(), ec) == 0) + { + // The connect operation has finished successfully so we need to post the + // handler immediately. + this->io_service().post(bind_handler(handler, ec)); + } + else if (ec == asio::error::in_progress + || ec == asio::error::would_block) + { + // The connection is happening in the background, and we need to wait + // until the socket becomes writeable. + boost::shared_ptr completed(new bool(false)); + reactor->start_write_and_except_ops(impl.socket_, + connect_handler( + impl.socket_, completed, this->io_service(), *reactor, handler)); + } + else + { + // The connect operation has failed, so post the handler immediately. + this->io_service().post(bind_handler(handler, ec)); + } + } + +private: + // Helper function to provide InterlockedCompareExchangePointer functionality + // on very old Platform SDKs. + void* interlocked_compare_exchange_pointer(void** dest, void* exch, void* cmp) + { +#if defined(_WIN32_WINNT) && (_WIN32_WINNT <= 0x400) && (_M_IX86) + return reinterpret_cast(InterlockedCompareExchange( + reinterpret_cast(dest), reinterpret_cast(exch), + reinterpret_cast(cmp))); +#else + return InterlockedCompareExchangePointer(dest, exch, cmp); +#endif + } + + // Helper function to provide InterlockedExchangePointer functionality on very + // old Platform SDKs. + void* interlocked_exchange_pointer(void** dest, void* val) + { +#if defined(_WIN32_WINNT) && (_WIN32_WINNT <= 0x400) && (_M_IX86) + return reinterpret_cast(InterlockedExchange( + reinterpret_cast(dest), reinterpret_cast(val))); +#else + return InterlockedExchangePointer(dest, val); +#endif + } + + // The IOCP service used for running asynchronous operations and dispatching + // handlers. + win_iocp_io_service& iocp_service_; + + // The reactor used for performing connect operations. This object is created + // only if needed. + reactor_type* reactor_; + + // Mutex to protect access to the linked list of implementations. + asio::detail::mutex mutex_; + + // The head of a linked list of all implementations. + implementation_type* impl_list_; +}; + +} // namespace detail +} // namespace asio + +#endif // defined(ASIO_HAS_IOCP) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_WIN_IOCP_SOCKET_SERVICE_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/detail/win_local_free_on_block_exit.hpp b/encryption/libtorrent/include/libtorrent/asio/detail/win_local_free_on_block_exit.hpp new file mode 100644 index 000000000..c909e1af3 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/detail/win_local_free_on_block_exit.hpp @@ -0,0 +1,59 @@ +// +// win_local_free_on_block_exit.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WIN_LOCAL_FREE_ON_BLOCK_EXIT_HPP +#define ASIO_DETAIL_WIN_LOCAL_FREE_ON_BLOCK_EXIT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include "asio/detail/pop_options.hpp" + +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/socket_types.hpp" + +namespace asio { +namespace detail { + +class win_local_free_on_block_exit + : private noncopyable +{ +public: + // Constructor blocks all signals for the calling thread. + explicit win_local_free_on_block_exit(void* p) + : p_(p) + { + } + + // Destructor restores the previous signal mask. + ~win_local_free_on_block_exit() + { + ::LocalFree(p_); + } + +private: + void* p_; +}; + +} // namespace detail +} // namespace asio + +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_WIN_LOCAL_FREE_ON_BLOCK_EXIT_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/detail/win_mutex.hpp b/encryption/libtorrent/include/libtorrent/asio/detail/win_mutex.hpp new file mode 100644 index 000000000..82659831f --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/detail/win_mutex.hpp @@ -0,0 +1,146 @@ +// +// win_mutex.hpp +// ~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WIN_MUTEX_HPP +#define ASIO_DETAIL_WIN_MUTEX_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include "asio/detail/pop_options.hpp" + +#if defined(BOOST_WINDOWS) + +#include "asio/system_error.hpp" +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/socket_types.hpp" +#include "asio/detail/scoped_lock.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include "asio/detail/pop_options.hpp" + +namespace asio { +namespace detail { + +class win_mutex + : private noncopyable +{ +public: + typedef asio::detail::scoped_lock scoped_lock; + + // Constructor. + win_mutex() + { + int error = do_init(); + if (error != 0) + { + asio::system_error e( + asio::error_code(error, asio::native_ecat), + "mutex"); + boost::throw_exception(e); + } + } + + // Destructor. + ~win_mutex() + { + ::DeleteCriticalSection(&crit_section_); + } + + // Lock the mutex. + void lock() + { + int error = do_lock(); + if (error != 0) + { + asio::system_error e( + asio::error_code(error, asio::native_ecat), + "mutex"); + boost::throw_exception(e); + } + } + + // Unlock the mutex. + void unlock() + { + ::LeaveCriticalSection(&crit_section_); + } + +private: + // Initialisation must be performed in a separate function to the constructor + // since the compiler does not support the use of structured exceptions and + // C++ exceptions in the same function. + int do_init() + { +#if defined(__MINGW32__) + // Not sure if MinGW supports structured exception handling, so for now + // we'll just call the Windows API and hope. + ::InitializeCriticalSection(&crit_section_); + return 0; +#else + __try + { + ::InitializeCriticalSection(&crit_section_); + } + __except(GetExceptionCode() == STATUS_NO_MEMORY + ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) + { + return ERROR_OUTOFMEMORY; + } + + return 0; +#endif + } + + // Locking must be performed in a separate function to lock() since the + // compiler does not support the use of structured exceptions and C++ + // exceptions in the same function. + int do_lock() + { +#if defined(__MINGW32__) + // Not sure if MinGW supports structured exception handling, so for now + // we'll just call the Windows API and hope. + ::EnterCriticalSection(&crit_section_); + return 0; +#else + __try + { + ::EnterCriticalSection(&crit_section_); + } + __except(GetExceptionCode() == STATUS_INVALID_HANDLE + || GetExceptionCode() == STATUS_NO_MEMORY + ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) + { + if (GetExceptionCode() == STATUS_NO_MEMORY) + return ERROR_OUTOFMEMORY; + return ERROR_INVALID_HANDLE; + } + + return 0; +#endif + } + + ::CRITICAL_SECTION crit_section_; +}; + +} // namespace detail +} // namespace asio + +#endif // defined(BOOST_WINDOWS) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_WIN_MUTEX_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/detail/win_signal_blocker.hpp b/encryption/libtorrent/include/libtorrent/asio/detail/win_signal_blocker.hpp new file mode 100644 index 000000000..892c40f89 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/detail/win_signal_blocker.hpp @@ -0,0 +1,67 @@ +// +// win_signal_blocker.hpp +// ~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WIN_SIGNAL_BLOCKER_HPP +#define ASIO_DETAIL_WIN_SIGNAL_BLOCKER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include "asio/detail/pop_options.hpp" + +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + +#include "asio/detail/noncopyable.hpp" + +namespace asio { +namespace detail { + +class win_signal_blocker + : private noncopyable +{ +public: + // Constructor blocks all signals for the calling thread. + win_signal_blocker() + { + // No-op. + } + + // Destructor restores the previous signal mask. + ~win_signal_blocker() + { + // No-op. + } + + // Block all signals for the calling thread. + void block() + { + // No-op. + } + + // Restore the previous signal mask. + void unblock() + { + // No-op. + } +}; + +} // namespace detail +} // namespace asio + +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_WIN_SIGNAL_BLOCKER_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/detail/win_thread.hpp b/encryption/libtorrent/include/libtorrent/asio/detail/win_thread.hpp new file mode 100644 index 000000000..a6c9b15d2 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/detail/win_thread.hpp @@ -0,0 +1,123 @@ +// +// win_thread.hpp +// ~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WIN_THREAD_HPP +#define ASIO_DETAIL_WIN_THREAD_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include "asio/detail/pop_options.hpp" + +#if defined(BOOST_WINDOWS) + +#include "asio/system_error.hpp" +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/socket_types.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include +#include "asio/detail/pop_options.hpp" + +namespace asio { +namespace detail { + +unsigned int __stdcall win_thread_function(void* arg); + +class win_thread + : private noncopyable +{ +public: + // Constructor. + template + win_thread(Function f) + { + std::auto_ptr arg(new func(f)); + unsigned int thread_id = 0; + thread_ = reinterpret_cast(::_beginthreadex(0, 0, + win_thread_function, arg.get(), 0, &thread_id)); + if (!thread_) + { + DWORD last_error = ::GetLastError(); + asio::system_error e( + asio::error_code(last_error, asio::native_ecat), + "thread"); + boost::throw_exception(e); + } + arg.release(); + } + + // Destructor. + ~win_thread() + { + ::CloseHandle(thread_); + } + + // Wait for the thread to exit. + void join() + { + ::WaitForSingleObject(thread_, INFINITE); + } + +private: + friend unsigned int __stdcall win_thread_function(void* arg); + + class func_base + { + public: + virtual ~func_base() {} + virtual void run() = 0; + }; + + template + class func + : public func_base + { + public: + func(Function f) + : f_(f) + { + } + + virtual void run() + { + f_(); + } + + private: + Function f_; + }; + + ::HANDLE thread_; +}; + +inline unsigned int __stdcall win_thread_function(void* arg) +{ + std::auto_ptr func( + static_cast(arg)); + func->run(); + return 0; +} + +} // namespace detail +} // namespace asio + +#endif // defined(BOOST_WINDOWS) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_WIN_THREAD_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/detail/win_tss_ptr.hpp b/encryption/libtorrent/include/libtorrent/asio/detail/win_tss_ptr.hpp new file mode 100644 index 000000000..d3e2f8161 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/detail/win_tss_ptr.hpp @@ -0,0 +1,87 @@ +// +// win_tss_ptr.hpp +// ~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WIN_TSS_PTR_HPP +#define ASIO_DETAIL_WIN_TSS_PTR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include "asio/detail/pop_options.hpp" + +#if defined(BOOST_WINDOWS) + +#include "asio/system_error.hpp" +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/socket_types.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include "asio/detail/pop_options.hpp" + +namespace asio { +namespace detail { + +template +class win_tss_ptr + : private noncopyable +{ +public: + // Constructor. + win_tss_ptr() + { + tss_key_ = ::TlsAlloc(); + if (tss_key_ == TLS_OUT_OF_INDEXES) + { + DWORD last_error = ::GetLastError(); + asio::system_error e( + asio::error_code(last_error, asio::native_ecat), + "tss"); + boost::throw_exception(e); + } + } + + // Destructor. + ~win_tss_ptr() + { + ::TlsFree(tss_key_); + } + + // Get the value. + operator T*() const + { + return static_cast(::TlsGetValue(tss_key_)); + } + + // Set the value. + void operator=(T* value) + { + ::TlsSetValue(tss_key_, value); + } + +private: + // Thread-specific storage to allow unlocked access to determine whether a + // thread is a member of the pool. + DWORD tss_key_; +}; + +} // namespace detail +} // namespace asio + +#endif // defined(BOOST_WINDOWS) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_WIN_TSS_PTR_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/detail/winsock_init.hpp b/encryption/libtorrent/include/libtorrent/asio/detail/winsock_init.hpp new file mode 100644 index 000000000..67c69e8ce --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/detail/winsock_init.hpp @@ -0,0 +1,118 @@ +// +// winsock_init.hpp +// ~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WINSOCK_INIT_HPP +#define ASIO_DETAIL_WINSOCK_INIT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include "asio/detail/pop_options.hpp" + +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + +#include "asio/detail/push_options.hpp" +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/system_error.hpp" +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/socket_types.hpp" + +namespace asio { +namespace detail { + +template +class winsock_init + : private noncopyable +{ +private: + // Structure to perform the actual initialisation. + struct do_init + { + do_init() + { + WSADATA wsa_data; + result_ = ::WSAStartup(MAKEWORD(Major, Minor), &wsa_data); + } + + ~do_init() + { + ::WSACleanup(); + } + + int result() const + { + return result_; + } + + // Helper function to manage a do_init singleton. The static instance of the + // winsock_init object ensures that this function is always called before + // main, and therefore before any other threads can get started. The do_init + // instance must be static in this function to ensure that it gets + // initialised before any other global objects try to use it. + static boost::shared_ptr instance() + { + static boost::shared_ptr init(new do_init); + return init; + } + + private: + int result_; + }; + +public: + // Constructor. + winsock_init() + : ref_(do_init::instance()) + { + // Check whether winsock was successfully initialised. This check is not + // performed for the global instance since there will be nobody around to + // catch the exception. + if (this != &instance_ && ref_->result() != 0) + { + asio::system_error e( + asio::error_code(ref_->result(), asio::native_ecat), + "winsock"); + boost::throw_exception(e); + } + } + + // Destructor. + ~winsock_init() + { + } + +private: + // Instance to force initialisation of winsock at global scope. + static winsock_init instance_; + + // Reference to singleton do_init object to ensure that winsock does not get + // cleaned up until the last user has finished with it. + boost::shared_ptr ref_; +}; + +template +winsock_init winsock_init::instance_; + +} // namespace detail +} // namespace asio + +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_WINSOCK_INIT_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/detail/wrapped_handler.hpp b/encryption/libtorrent/include/libtorrent/asio/detail/wrapped_handler.hpp new file mode 100644 index 000000000..f757fd3dc --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/detail/wrapped_handler.hpp @@ -0,0 +1,187 @@ +// +// wrapped_handler.hpp +// ~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WRAPPED_HANDLER_HPP +#define ASIO_DETAIL_WRAPPED_HANDLER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" + +namespace asio { +namespace detail { + +template +class wrapped_handler +{ +public: + typedef void result_type; + + wrapped_handler(Dispatcher& dispatcher, Handler handler) + : dispatcher_(dispatcher), + handler_(handler) + { + } + + void operator()() + { + dispatcher_.dispatch(handler_); + } + + void operator()() const + { + dispatcher_.dispatch(handler_); + } + + template + void operator()(const Arg1& arg1) + { + dispatcher_.dispatch(detail::bind_handler(handler_, arg1)); + } + + template + void operator()(const Arg1& arg1) const + { + dispatcher_.dispatch(detail::bind_handler(handler_, arg1)); + } + + template + void operator()(const Arg1& arg1, const Arg2& arg2) + { + dispatcher_.dispatch(detail::bind_handler(handler_, arg1, arg2)); + } + + template + void operator()(const Arg1& arg1, const Arg2& arg2) const + { + dispatcher_.dispatch(detail::bind_handler(handler_, arg1, arg2)); + } + + template + void operator()(const Arg1& arg1, const Arg2& arg2, const Arg3& arg3) + { + dispatcher_.dispatch(detail::bind_handler(handler_, arg1, arg2, arg3)); + } + + template + void operator()(const Arg1& arg1, const Arg2& arg2, const Arg3& arg3) const + { + dispatcher_.dispatch(detail::bind_handler(handler_, arg1, arg2, arg3)); + } + + template + void operator()(const Arg1& arg1, const Arg2& arg2, const Arg3& arg3, + const Arg4& arg4) + { + dispatcher_.dispatch( + detail::bind_handler(handler_, arg1, arg2, arg3, arg4)); + } + + template + void operator()(const Arg1& arg1, const Arg2& arg2, const Arg3& arg3, + const Arg4& arg4) const + { + dispatcher_.dispatch( + detail::bind_handler(handler_, arg1, arg2, arg3, arg4)); + } + + template + void operator()(const Arg1& arg1, const Arg2& arg2, const Arg3& arg3, + const Arg4& arg4, const Arg5& arg5) + { + dispatcher_.dispatch( + detail::bind_handler(handler_, arg1, arg2, arg3, arg4, arg5)); + } + + template + void operator()(const Arg1& arg1, const Arg2& arg2, const Arg3& arg3, + const Arg4& arg4, const Arg5& arg5) const + { + dispatcher_.dispatch( + detail::bind_handler(handler_, arg1, arg2, arg3, arg4, arg5)); + } + +//private: + Dispatcher& dispatcher_; + Handler handler_; +}; + +template +inline void* asio_handler_allocate(std::size_t size, + wrapped_handler* this_handler) +{ + return asio_handler_alloc_helpers::allocate( + size, &this_handler->handler_); +} + +template +inline void asio_handler_deallocate(void* pointer, std::size_t size, + wrapped_handler* this_handler) +{ + asio_handler_alloc_helpers::deallocate( + pointer, size, &this_handler->handler_); +} + +template +class rewrapped_handler +{ +public: + explicit rewrapped_handler(const Handler& handler, const Context& context) + : handler_(handler), + context_(context) + { + } + + void operator()() + { + handler_(); + } + + void operator()() const + { + handler_(); + } + +//private: + Handler handler_; + Context context_; +}; + +template +inline void asio_handler_invoke(const Function& function, + wrapped_handler* this_handler) +{ + this_handler->dispatcher_.dispatch( + rewrapped_handler( + function, this_handler->handler_)); +} + +template +inline void asio_handler_invoke(const Function& function, + rewrapped_handler* this_handler) +{ + asio_handler_invoke_helpers::invoke( + function, &this_handler->context_); +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_WRAPPED_HANDLER_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/error.hpp b/encryption/libtorrent/include/libtorrent/asio/error.hpp new file mode 100644 index 000000000..935cc6796 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/error.hpp @@ -0,0 +1,367 @@ +// +// error.hpp +// ~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_ERROR_HPP +#define ASIO_ERROR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/error_code.hpp" +#include "asio/detail/socket_types.hpp" + +#if defined(GENERATING_DOCUMENTATION) +/// INTERNAL ONLY. +# define ASIO_NATIVE_ERROR(e) implementation_defined +/// INTERNAL ONLY. +# define ASIO_SOCKET_ERROR(e) implementation_defined +/// INTERNAL ONLY. +# define ASIO_NETDB_ERROR(e) implementation_defined +/// INTERNAL ONLY. +# define ASIO_GETADDRINFO_ERROR(e) implementation_defined +/// INTERNAL ONLY. +# define ASIO_WIN_OR_POSIX(e_win, e_posix) implementation_defined +#elif defined(BOOST_WINDOWS) || defined(__CYGWIN__) +# define ASIO_NATIVE_ERROR(e) \ + asio::error_code(e, \ + asio::native_ecat) +# define ASIO_SOCKET_ERROR(e) \ + asio::error_code(WSA ## e, \ + asio::native_ecat) +# define ASIO_NETDB_ERROR(e) \ + asio::error_code(WSA ## e, \ + asio::native_ecat) +# define ASIO_GETADDRINFO_ERROR(e) \ + asio::error_code(WSA ## e, \ + asio::native_ecat) +# define ASIO_MISC_ERROR(e) \ + asio::error_code(e, \ + asio::misc_ecat) +# define ASIO_WIN_OR_POSIX(e_win, e_posix) e_win +#else +# define ASIO_NATIVE_ERROR(e) \ + asio::error_code(e, \ + asio::native_ecat) +# define ASIO_SOCKET_ERROR(e) \ + asio::error_code(e, \ + asio::native_ecat) +# define ASIO_NETDB_ERROR(e) \ + asio::error_code(e, \ + asio::netdb_ecat) +# define ASIO_GETADDRINFO_ERROR(e) \ + asio::error_code(e, \ + asio::addrinfo_ecat) +# define ASIO_MISC_ERROR(e) \ + asio::error_code(e, \ + asio::misc_ecat) +# define ASIO_WIN_OR_POSIX(e_win, e_posix) e_posix +#endif + +namespace asio { + +namespace detail { + +/// Hack to keep asio library header-file-only. +template +class error_base +{ +public: + // boostify: error category declarations go here. + + /// Permission denied. + static const asio::error_code access_denied; + + /// Address family not supported by protocol. + static const asio::error_code address_family_not_supported; + + /// Address already in use. + static const asio::error_code address_in_use; + + /// Transport endpoint is already connected. + static const asio::error_code already_connected; + + /// Already open. + static const asio::error_code already_open; + + /// Operation already in progress. + static const asio::error_code already_started; + + /// A connection has been aborted. + static const asio::error_code connection_aborted; + + /// Connection refused. + static const asio::error_code connection_refused; + + /// Connection reset by peer. + static const asio::error_code connection_reset; + + /// Bad file descriptor. + static const asio::error_code bad_descriptor; + + /// End of file or stream. + static const asio::error_code eof; + + /// Bad address. + static const asio::error_code fault; + + /// Host not found (authoritative). + static const asio::error_code host_not_found; + + /// Host not found (non-authoritative). + static const asio::error_code host_not_found_try_again; + + /// No route to host. + static const asio::error_code host_unreachable; + + /// Operation now in progress. + static const asio::error_code in_progress; + + /// Interrupted system call. + static const asio::error_code interrupted; + + /// Invalid argument. + static const asio::error_code invalid_argument; + + /// Message too long. + static const asio::error_code message_size; + + /// Network is down. + static const asio::error_code network_down; + + /// Network dropped connection on reset. + static const asio::error_code network_reset; + + /// Network is unreachable. + static const asio::error_code network_unreachable; + + /// Too many open files. + static const asio::error_code no_descriptors; + + /// No buffer space available. + static const asio::error_code no_buffer_space; + + /// The query is valid but does not have associated address data. + static const asio::error_code no_data; + + /// Cannot allocate memory. + static const asio::error_code no_memory; + + /// Operation not permitted. + static const asio::error_code no_permission; + + /// Protocol not available. + static const asio::error_code no_protocol_option; + + /// A non-recoverable error occurred. + static const asio::error_code no_recovery; + + /// Transport endpoint is not connected. + static const asio::error_code not_connected; + + /// Element not found. + static const asio::error_code not_found; + + /// Socket operation on non-socket. + static const asio::error_code not_socket; + + /// Operation cancelled. + static const asio::error_code operation_aborted; + + /// Operation not supported. + static const asio::error_code operation_not_supported; + + /// The service is not supported for the given socket type. + static const asio::error_code service_not_found; + + /// The socket type is not supported. + static const asio::error_code socket_type_not_supported; + + /// Cannot send after transport endpoint shutdown. + static const asio::error_code shut_down; + + /// Connection timed out. + static const asio::error_code timed_out; + + /// Resource temporarily unavailable. + static const asio::error_code try_again; + + /// The socket is marked non-blocking and the requested operation would block. + static const asio::error_code would_block; + +private: + error_base(); +}; + +// boostify: error category definitions go here. + +template const asio::error_code +error_base::access_denied = ASIO_SOCKET_ERROR(EACCES); + +template const asio::error_code +error_base::address_family_not_supported = ASIO_SOCKET_ERROR( + EAFNOSUPPORT); + +template const asio::error_code +error_base::address_in_use = ASIO_SOCKET_ERROR(EADDRINUSE); + +template const asio::error_code +error_base::already_connected = ASIO_SOCKET_ERROR(EISCONN); + +template const asio::error_code +error_base::already_open = ASIO_MISC_ERROR(1); + +template const asio::error_code +error_base::already_started = ASIO_SOCKET_ERROR(EALREADY); + +template const asio::error_code +error_base::connection_aborted = ASIO_SOCKET_ERROR(ECONNABORTED); + +template const asio::error_code +error_base::connection_refused = ASIO_SOCKET_ERROR(ECONNREFUSED); + +template const asio::error_code +error_base::connection_reset = ASIO_SOCKET_ERROR(ECONNRESET); + +template const asio::error_code +error_base::bad_descriptor = ASIO_SOCKET_ERROR(EBADF); + +template const asio::error_code +error_base::eof = ASIO_MISC_ERROR(2); + +template const asio::error_code +error_base::fault = ASIO_SOCKET_ERROR(EFAULT); + +template const asio::error_code +error_base::host_not_found = ASIO_NETDB_ERROR(HOST_NOT_FOUND); + +template const asio::error_code +error_base::host_not_found_try_again = ASIO_NETDB_ERROR(TRY_AGAIN); + +template const asio::error_code +error_base::host_unreachable = ASIO_SOCKET_ERROR(EHOSTUNREACH); + +template const asio::error_code +error_base::in_progress = ASIO_SOCKET_ERROR(EINPROGRESS); + +template const asio::error_code +error_base::interrupted = ASIO_SOCKET_ERROR(EINTR); + +template const asio::error_code +error_base::invalid_argument = ASIO_SOCKET_ERROR(EINVAL); + +template const asio::error_code +error_base::message_size = ASIO_SOCKET_ERROR(EMSGSIZE); + +template const asio::error_code +error_base::network_down = ASIO_SOCKET_ERROR(ENETDOWN); + +template const asio::error_code +error_base::network_reset = ASIO_SOCKET_ERROR(ENETRESET); + +template const asio::error_code +error_base::network_unreachable = ASIO_SOCKET_ERROR(ENETUNREACH); + +template const asio::error_code +error_base::no_descriptors = ASIO_SOCKET_ERROR(EMFILE); + +template const asio::error_code +error_base::no_buffer_space = ASIO_SOCKET_ERROR(ENOBUFS); + +template const asio::error_code +error_base::no_data = ASIO_NETDB_ERROR(NO_DATA); + +template const asio::error_code +error_base::no_memory = ASIO_WIN_OR_POSIX( + ASIO_NATIVE_ERROR(ERROR_OUTOFMEMORY), + ASIO_NATIVE_ERROR(ENOMEM)); + +template const asio::error_code +error_base::no_permission = ASIO_WIN_OR_POSIX( + ASIO_NATIVE_ERROR(ERROR_ACCESS_DENIED), + ASIO_NATIVE_ERROR(EPERM)); + +template const asio::error_code +error_base::no_protocol_option = ASIO_SOCKET_ERROR(ENOPROTOOPT); + +template const asio::error_code +error_base::no_recovery = ASIO_NETDB_ERROR(NO_RECOVERY); + +template const asio::error_code +error_base::not_connected = ASIO_SOCKET_ERROR(ENOTCONN); + +template const asio::error_code +error_base::not_found = ASIO_MISC_ERROR(3); + +template const asio::error_code +error_base::not_socket = ASIO_SOCKET_ERROR(ENOTSOCK); + +template const asio::error_code +error_base::operation_aborted = ASIO_WIN_OR_POSIX( + ASIO_NATIVE_ERROR(ERROR_OPERATION_ABORTED), + ASIO_NATIVE_ERROR(ECANCELED)); + +template const asio::error_code +error_base::operation_not_supported = ASIO_SOCKET_ERROR(EOPNOTSUPP); + +template const asio::error_code +error_base::service_not_found = ASIO_WIN_OR_POSIX( + ASIO_NATIVE_ERROR(WSATYPE_NOT_FOUND), + ASIO_GETADDRINFO_ERROR(EAI_SERVICE)); + +template const asio::error_code +error_base::socket_type_not_supported = ASIO_WIN_OR_POSIX( + ASIO_NATIVE_ERROR(WSAESOCKTNOSUPPORT), + ASIO_GETADDRINFO_ERROR(EAI_SOCKTYPE)); + +template const asio::error_code +error_base::shut_down = ASIO_SOCKET_ERROR(ESHUTDOWN); + +template const asio::error_code +error_base::timed_out = ASIO_SOCKET_ERROR(ETIMEDOUT); + +template const asio::error_code +error_base::try_again = ASIO_WIN_OR_POSIX( + ASIO_NATIVE_ERROR(ERROR_RETRY), + ASIO_NATIVE_ERROR(EAGAIN)); + +template const asio::error_code +error_base::would_block = ASIO_SOCKET_ERROR(EWOULDBLOCK); + +} // namespace detail + +/// Contains error constants. +class error : public asio::detail::error_base +{ +private: + error(); +}; + +} // namespace asio + +#undef ASIO_NATIVE_ERROR +#undef ASIO_SOCKET_ERROR +#undef ASIO_NETDB_ERROR +#undef ASIO_GETADDRINFO_ERROR +#undef ASIO_MISC_ERROR +#undef ASIO_WIN_OR_POSIX + +#include "asio/impl/error_code.ipp" + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_ERROR_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/error_code.hpp b/encryption/libtorrent/include/libtorrent/asio/error_code.hpp new file mode 100644 index 000000000..0614490e2 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/error_code.hpp @@ -0,0 +1,139 @@ +// +// error_code.hpp +// ~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_ERROR_CODE_HPP +#define ASIO_ERROR_CODE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include "asio/detail/pop_options.hpp" + +#if defined(GENERATING_DOCUMENTATION) +# define ASIO_WIN_OR_POSIX(e_win, e_posix) implementation_defined +#elif defined(BOOST_WINDOWS) || defined(__CYGWIN__) +# define ASIO_WIN_OR_POSIX(e_win, e_posix) e_win +#else +# define ASIO_WIN_OR_POSIX(e_win, e_posix) e_posix +#endif + +namespace asio { + +/// Available error code categories. +enum error_category +{ + /// Native error codes. + native_ecat = ASIO_WIN_OR_POSIX(0, 0), + + /// Error codes from NetDB functions. + netdb_ecat = ASIO_WIN_OR_POSIX(native_ecat, 1), + + /// Error codes from getaddrinfo. + addrinfo_ecat = ASIO_WIN_OR_POSIX(native_ecat, 2), + + /// Miscellaneous error codes. + misc_ecat = ASIO_WIN_OR_POSIX(3, 3), + + /// SSL error codes. + ssl_ecat = ASIO_WIN_OR_POSIX(4, 4) +}; + +/// Class to represent an error code value. +class error_code +{ +public: + /// The underlying representation of an error code. + typedef int value_type; + + /// Default constructor. + error_code() + : value_(0), + category_(native_ecat) + { + } + + /// Construct with specific error code and category. + error_code(value_type v, error_category c) + : value_(v), + category_(c) + { + } + + /// Get the error value. + value_type value() const + { + return value_; + } + + /// Get the error category. + error_category category() const + { + return category_; + } + + /// Get the message associated with the error. + std::string message() const; + + struct unspecified_bool_type_t + { + }; + + typedef unspecified_bool_type_t* unspecified_bool_type; + + /// Operator returns non-null if there is a non-success error code. + operator unspecified_bool_type() const + { + if (value_ == 0) + return 0; + else + return reinterpret_cast(1); + } + + /// Operator to test if the error represents success. + bool operator!() const + { + return value_ == 0; + } + + /// Equality operator to compare two error objects. + friend bool operator==(const error_code& e1, const error_code& e2) + { + return e1.value_ == e2.value_ && e1.category_ == e2.category_; + } + + /// Inequality operator to compare two error objects. + friend bool operator!=(const error_code& e1, const error_code& e2) + { + return e1.value_ != e2.value_ || e1.category_ != e2.category_; + } + +private: + // The value associated with the error code. + value_type value_; + + // The category associated with the error code. + error_category category_; +}; + +} // namespace asio + +#undef ASIO_WIN_OR_POSIX + +#include "asio/error.hpp" + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_ERROR_CODE_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/error_handler.hpp b/encryption/libtorrent/include/libtorrent/asio/error_handler.hpp new file mode 100644 index 000000000..c315c8d5e --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/error_handler.hpp @@ -0,0 +1,120 @@ +// +// error_handler.hpp +// ~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_ERROR_HANDLER_HPP +#define ASIO_ERROR_HANDLER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include "asio/detail/pop_options.hpp" + +namespace asio { + +namespace detail { + +class ignore_error_t +{ +public: + typedef void result_type; + + template + void operator()(const Error&) const + { + } +}; + +class throw_error_t +{ +public: + typedef void result_type; + + template + void operator()(const Error& err) const + { + if (err) + boost::throw_exception(err); + } +}; + +template +class assign_error_t +{ +public: + typedef void result_type; + + assign_error_t(Target& target) + : target_(&target) + { + } + + template + void operator()(const Error& err) const + { + *target_ = err; + } + +private: + Target* target_; +}; + +} // namespace detail + +/** + * @defgroup error_handler Error Handler Function Objects + * + * Function objects for custom error handling. + */ +/*@{*/ + +/// Return a function object that always ignores the error. +#if defined(GENERATING_DOCUMENTATION) +unspecified ignore_error(); +#else +inline detail::ignore_error_t ignore_error() +{ + return detail::ignore_error_t(); +} +#endif + +/// Return a function object that always throws the error. +#if defined(GENERATING_DOCUMENTATION) +unspecified throw_error(); +#else +inline detail::throw_error_t throw_error() +{ + return detail::throw_error_t(); +} +#endif + +/// Return a function object that assigns the error to a variable. +#if defined(GENERATING_DOCUMENTATION) +template +unspecified assign_error(Target& target); +#else +template +inline detail::assign_error_t assign_error(Target& target) +{ + return detail::assign_error_t(target); +} +#endif + +/*@}*/ + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_ERROR_HANDLER_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/handler_alloc_hook.hpp b/encryption/libtorrent/include/libtorrent/asio/handler_alloc_hook.hpp new file mode 100644 index 000000000..042b1fecd --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/handler_alloc_hook.hpp @@ -0,0 +1,88 @@ +// +// handler_alloc_hook.hpp +// ~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_HANDLER_ALLOC_HOOK_HPP +#define ASIO_HANDLER_ALLOC_HOOK_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include "asio/detail/pop_options.hpp" + +namespace asio { + +/// Default allocation function for handlers. +/** + * Asynchronous operations may need to allocate temporary objects. Since + * asynchronous operations have a handler function object, these temporary + * objects can be said to be associated with the handler. + * + * Implement asio_handler_allocate and asio_handler_deallocate for your own + * handlers to provide custom allocation for these temporary objects. + * + * This default implementation is simply: + * @code + * return ::operator new(bytes); + * @endcode + * + * @note All temporary objects associated with a handler will be deallocated + * before the upcall to the handler is performed. This allows the same memory to + * be reused for a subsequent asynchronous operation initiated by the handler. + * + * @par Example + * @code + * class my_handler; + * + * void* asio_handler_allocate(std::size_t size, my_handler* context) + * { + * return ::operator new(size); + * } + * + * void asio_handler_deallocate(void* pointer, std::size_t size, + * my_handler* context) + * { + * ::operator delete(pointer); + * } + * @endcode + */ +inline void* asio_handler_allocate(std::size_t size, ...) +{ + return ::operator new(size); +} + +/// Default deallocation function for handlers. +/** + * Implement asio_handler_allocate and asio_handler_deallocate for your own + * handlers to provide custom allocation for the associated temporary objects. + * + * This default implementation is simply: + * @code + * ::operator delete(pointer); + * @endcode + * + * @sa asio_handler_allocate. + */ +inline void asio_handler_deallocate(void* pointer, std::size_t size, ...) +{ + (void)(size); + ::operator delete(pointer); +} + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_HANDLER_ALLOC_HOOK_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/handler_invoke_hook.hpp b/encryption/libtorrent/include/libtorrent/asio/handler_invoke_hook.hpp new file mode 100644 index 000000000..4ba5db329 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/handler_invoke_hook.hpp @@ -0,0 +1,69 @@ +// +// handler_invoke_hook.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_HANDLER_INVOKE_HOOK_HPP +#define ASIO_HANDLER_INVOKE_HOOK_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +namespace asio { + +/// Default invoke function for handlers. +/** + * Completion handlers for asynchronous operations are invoked by the + * io_service associated with the corresponding object (e.g. a socket or + * deadline_timer). Certain guarantees are made on when the handler may be + * invoked, in particular that a handler can only be invoked from a thread that + * is currently calling asio::io_service::run() on the corresponding + * io_service object. Handlers may subsequently be invoked through other + * objects (such as asio::strand objects) that provide additional + * guarantees. + * + * When asynchronous operations are composed from other asynchronous + * operations, all intermediate handlers should be invoked using the same + * method as the final handler. This is required to ensure that user-defined + * objects are not accessed in a way that may violate the guarantees. This + * hooking function ensures that the invoked method used for the final handler + * is accessible at each intermediate step. + * + * Implement asio_handler_invoke for your own handlers to specify a custom + * invocation strategy. + * + * This default implementation is simply: + * @code + * function(); + * @endcode + * + * @par Example + * @code + * class my_handler; + * + * template + * void asio_handler_invoke(Function function, my_handler* context) + * { + * context->strand_.dispatch(function); + * } + * @endcode + */ +template +inline void asio_handler_invoke(Function function, ...) +{ + function(); +} + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_HANDLER_INVOKE_HOOK_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/impl/error_code.ipp b/encryption/libtorrent/include/libtorrent/asio/impl/error_code.ipp new file mode 100644 index 000000000..da2f98833 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/impl/error_code.ipp @@ -0,0 +1,98 @@ +// +// error_code.ipp +// ~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_ERROR_CODE_IPP +#define ASIO_ERROR_CODE_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/error.hpp" +#include "asio/detail/local_free_on_block_exit.hpp" +#include "asio/detail/socket_types.hpp" + +namespace asio { + +inline std::string error_code::message() const +{ + if (*this == error::already_open) + return "Already open."; + if (*this == error::not_found) + return "Not found."; + if (category_ == ssl_ecat) + return "SSL error."; +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + value_type value = value_; + if (*this == error::eof) + value = ERROR_HANDLE_EOF; + char* msg = 0; + DWORD length = ::FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER + | FORMAT_MESSAGE_FROM_SYSTEM + | FORMAT_MESSAGE_IGNORE_INSERTS, 0, value, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (char*)&msg, 0, 0); + detail::local_free_on_block_exit local_free_obj(msg); + if (length && msg[length - 1] == '\n') + msg[--length] = '\0'; + if (length && msg[length - 1] == '\r') + msg[--length] = '\0'; + if (length) + return msg; + else + return "asio error"; +#else // defined(BOOST_WINDOWS) + if (*this == error::eof) + return "End of file."; + if (*this == error::host_not_found) + return "Host not found (authoritative)."; + if (*this == error::host_not_found_try_again) + return "Host not found (non-authoritative), try again later."; + if (*this == error::no_recovery) + return "A non-recoverable error occurred during database lookup."; + if (*this == error::no_data) + return "The query is valid, but it does not have associated data."; + if (*this == error::not_found) + return "Element not found."; +#if !defined(__sun) + if (*this == error::operation_aborted) + return "Operation aborted."; +#endif // !defined(__sun) + if (*this == error::service_not_found) + return "Service not found."; + if (*this == error::socket_type_not_supported) + return "Socket type not supported."; +#if defined(__sun) || defined(__QNX__) + return strerror(value_); +#elif defined(__MACH__) && defined(__APPLE__) \ +|| defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__) \ +|| defined(_AIX) + char buf[256] = ""; + strerror_r(value_, buf, sizeof(buf)); + return buf; +#else + char buf[256] = ""; + return strerror_r(value_, buf, sizeof(buf)); +#endif +#endif // defined(BOOST_WINDOWS) +} + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_ERROR_CODE_IPP diff --git a/encryption/libtorrent/include/libtorrent/asio/impl/io_service.ipp b/encryption/libtorrent/include/libtorrent/asio/impl/io_service.ipp new file mode 100644 index 000000000..e973619d1 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/impl/io_service.ipp @@ -0,0 +1,213 @@ +// +// io_service.ipp +// ~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IO_SERVICE_IPP +#define ASIO_IO_SERVICE_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/detail/epoll_reactor.hpp" +#include "asio/detail/kqueue_reactor.hpp" +#include "asio/detail/select_reactor.hpp" +#include "asio/detail/service_registry.hpp" +#include "asio/detail/task_io_service.hpp" +#include "asio/detail/throw_error.hpp" +#include "asio/detail/win_iocp_io_service.hpp" + +namespace asio { + +inline io_service::io_service() + : service_registry_(new asio::detail::service_registry(*this)), + impl_(service_registry_->use_service()) +{ + impl_.init((std::numeric_limits::max)()); +} + +inline io_service::io_service(std::size_t concurrency_hint) + : service_registry_(new asio::detail::service_registry(*this)), + impl_(service_registry_->use_service()) +{ + impl_.init(concurrency_hint); +} + +inline io_service::~io_service() +{ + delete service_registry_; +} + +inline std::size_t io_service::run() +{ + asio::error_code ec; + std::size_t s = impl_.run(ec); + asio::detail::throw_error(ec); + return s; +} + +inline std::size_t io_service::run(asio::error_code& ec) +{ + return impl_.run(ec); +} + +inline std::size_t io_service::run_one() +{ + asio::error_code ec; + std::size_t s = impl_.run_one(ec); + asio::detail::throw_error(ec); + return s; +} + +inline std::size_t io_service::run_one(asio::error_code& ec) +{ + return impl_.run_one(ec); +} + +inline std::size_t io_service::poll() +{ + asio::error_code ec; + std::size_t s = impl_.poll(ec); + asio::detail::throw_error(ec); + return s; +} + +inline std::size_t io_service::poll(asio::error_code& ec) +{ + return impl_.poll(ec); +} + +inline std::size_t io_service::poll_one() +{ + asio::error_code ec; + std::size_t s = impl_.poll_one(ec); + asio::detail::throw_error(ec); + return s; +} + +inline std::size_t io_service::poll_one(asio::error_code& ec) +{ + return impl_.poll_one(ec); +} + +inline void io_service::stop() +{ + impl_.stop(); +} + +inline void io_service::reset() +{ + impl_.reset(); +} + +template +inline void io_service::dispatch(Handler handler) +{ + impl_.dispatch(handler); +} + +template +inline void io_service::post(Handler handler) +{ + impl_.post(handler); +} + +template +#if defined(GENERATING_DOCUMENTATION) +unspecified +#else +inline detail::wrapped_handler +#endif +io_service::wrap(Handler handler) +{ + return detail::wrapped_handler(*this, handler); +} + +inline io_service::work::work(asio::io_service& io_service) + : io_service_(io_service) +{ + io_service_.impl_.work_started(); +} + +inline io_service::work::work(const work& other) + : io_service_(other.io_service_) +{ + io_service_.impl_.work_started(); +} + +inline io_service::work::~work() +{ + io_service_.impl_.work_finished(); +} + +inline asio::io_service& io_service::work::io_service() +{ + return io_service_; +} + +inline io_service::service::service(asio::io_service& owner) + : owner_(owner), + type_info_(0), + next_(0) +{ +} + +inline io_service::service::~service() +{ +} + +inline asio::io_service& io_service::service::io_service() +{ + return owner_; +} + +template +inline Service& use_service(io_service& ios) +{ + // Check that Service meets the necessary type requirements. + (void)static_cast(static_cast(0)); + (void)static_cast(&Service::id); + + return ios.service_registry_->template use_service(); +} + +template +void add_service(io_service& ios, Service* svc) +{ + // Check that Service meets the necessary type requirements. + (void)static_cast(static_cast(0)); + (void)static_cast(&Service::id); + + if (&ios != &svc->io_service()) + boost::throw_exception(invalid_service_owner()); + if (!ios.service_registry_->template add_service(svc)) + boost::throw_exception(service_already_exists()); +} + +template +bool has_service(io_service& ios) +{ + // Check that Service meets the necessary type requirements. + (void)static_cast(static_cast(0)); + (void)static_cast(&Service::id); + + return ios.service_registry_->template has_service(); +} + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IO_SERVICE_IPP diff --git a/encryption/libtorrent/include/libtorrent/asio/impl/read.ipp b/encryption/libtorrent/include/libtorrent/asio/impl/read.ipp new file mode 100644 index 000000000..f30f50fb0 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/impl/read.ipp @@ -0,0 +1,314 @@ +// +// read.ipp +// ~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_READ_IPP +#define ASIO_READ_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/buffer.hpp" +#include "asio/completion_condition.hpp" +#include "asio/error.hpp" +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/consuming_buffers.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/throw_error.hpp" + +namespace asio { + +template +std::size_t read(SyncReadStream& s, const MutableBufferSequence& buffers, + CompletionCondition completion_condition, asio::error_code& ec) +{ + asio::detail::consuming_buffers< + mutable_buffer, MutableBufferSequence> tmp(buffers); + std::size_t total_transferred = 0; + while (tmp.begin() != tmp.end()) + { + std::size_t bytes_transferred = s.read_some(tmp, ec); + tmp.consume(bytes_transferred); + total_transferred += bytes_transferred; + if (completion_condition(ec, total_transferred)) + return total_transferred; + } + ec = asio::error_code(); + return total_transferred; +} + +template +inline std::size_t read(SyncReadStream& s, const MutableBufferSequence& buffers) +{ + asio::error_code ec; + std::size_t bytes_transferred = read(s, buffers, transfer_all(), ec); + asio::detail::throw_error(ec); + return bytes_transferred; +} + +template +inline std::size_t read(SyncReadStream& s, const MutableBufferSequence& buffers, + CompletionCondition completion_condition) +{ + asio::error_code ec; + std::size_t bytes_transferred = read(s, buffers, completion_condition, ec); + asio::detail::throw_error(ec); + return bytes_transferred; +} + +template +std::size_t read(SyncReadStream& s, + asio::basic_streambuf& b, + CompletionCondition completion_condition, asio::error_code& ec) +{ + std::size_t total_transferred = 0; + for (;;) + { + std::size_t bytes_available = + std::min(512, b.max_size() - b.size()); + std::size_t bytes_transferred = s.read_some(b.prepare(bytes_available), ec); + b.commit(bytes_transferred); + total_transferred += bytes_transferred; + if (b.size() == b.max_size() + || completion_condition(ec, total_transferred)) + return total_transferred; + } +} + +template +inline std::size_t read(SyncReadStream& s, + asio::basic_streambuf& b) +{ + asio::error_code ec; + std::size_t bytes_transferred = read(s, b, transfer_all(), ec); + asio::detail::throw_error(ec); + return bytes_transferred; +} + +template +inline std::size_t read(SyncReadStream& s, + asio::basic_streambuf& b, + CompletionCondition completion_condition) +{ + asio::error_code ec; + std::size_t bytes_transferred = read(s, b, completion_condition, ec); + asio::detail::throw_error(ec); + return bytes_transferred; +} + +namespace detail +{ + template + class read_handler + { + public: + typedef asio::detail::consuming_buffers< + mutable_buffer, MutableBufferSequence> buffers_type; + + read_handler(AsyncReadStream& stream, const buffers_type& buffers, + CompletionCondition completion_condition, ReadHandler handler) + : stream_(stream), + buffers_(buffers), + total_transferred_(0), + completion_condition_(completion_condition), + handler_(handler) + { + } + + void operator()(const asio::error_code& ec, + std::size_t bytes_transferred) + { + total_transferred_ += bytes_transferred; + buffers_.consume(bytes_transferred); + if (completion_condition_(ec, total_transferred_) + || buffers_.begin() == buffers_.end()) + { + handler_(ec, total_transferred_); + } + else + { + stream_.async_read_some(buffers_, *this); + } + } + + //private: + AsyncReadStream& stream_; + buffers_type buffers_; + std::size_t total_transferred_; + CompletionCondition completion_condition_; + ReadHandler handler_; + }; + + template + inline void* asio_handler_allocate(std::size_t size, + read_handler* this_handler) + { + return asio_handler_alloc_helpers::allocate( + size, &this_handler->handler_); + } + + template + inline void asio_handler_deallocate(void* pointer, std::size_t size, + read_handler* this_handler) + { + asio_handler_alloc_helpers::deallocate( + pointer, size, &this_handler->handler_); + } + + template + inline void asio_handler_invoke(const Function& function, + read_handler* this_handler) + { + asio_handler_invoke_helpers::invoke( + function, &this_handler->handler_); + } +} // namespace detail + +template +inline void async_read(AsyncReadStream& s, const MutableBufferSequence& buffers, + CompletionCondition completion_condition, ReadHandler handler) +{ + asio::detail::consuming_buffers< + mutable_buffer, MutableBufferSequence> tmp(buffers); + s.async_read_some(tmp, + detail::read_handler( + s, tmp, completion_condition, handler)); +} + +template +inline void async_read(AsyncReadStream& s, const MutableBufferSequence& buffers, + ReadHandler handler) +{ + async_read(s, buffers, transfer_all(), handler); +} + +namespace detail +{ + template + class read_streambuf_handler + { + public: + read_streambuf_handler(AsyncReadStream& stream, + basic_streambuf& streambuf, + CompletionCondition completion_condition, ReadHandler handler) + : stream_(stream), + streambuf_(streambuf), + total_transferred_(0), + completion_condition_(completion_condition), + handler_(handler) + { + } + + void operator()(const asio::error_code& ec, + std::size_t bytes_transferred) + { + total_transferred_ += bytes_transferred; + streambuf_.commit(bytes_transferred); + if (streambuf_.size() == streambuf_.max_size() + || completion_condition_(ec, total_transferred_)) + { + handler_(ec, total_transferred_); + } + else + { + std::size_t bytes_available = + std::min(512, streambuf_.max_size() - streambuf_.size()); + stream_.async_read_some(streambuf_.prepare(bytes_available), *this); + } + } + + //private: + AsyncReadStream& stream_; + asio::basic_streambuf& streambuf_; + std::size_t total_transferred_; + CompletionCondition completion_condition_; + ReadHandler handler_; + }; + + template + inline void* asio_handler_allocate(std::size_t size, + read_streambuf_handler* this_handler) + { + return asio_handler_alloc_helpers::allocate( + size, &this_handler->handler_); + } + + template + inline void asio_handler_deallocate(void* pointer, std::size_t size, + read_streambuf_handler* this_handler) + { + asio_handler_alloc_helpers::deallocate( + pointer, size, &this_handler->handler_); + } + + template + inline void asio_handler_invoke(const Function& function, + read_streambuf_handler* this_handler) + { + asio_handler_invoke_helpers::invoke( + function, &this_handler->handler_); + } +} // namespace detail + +template +inline void async_read(AsyncReadStream& s, + asio::basic_streambuf& b, + CompletionCondition completion_condition, ReadHandler handler) +{ + std::size_t bytes_available = + std::min(512, b.max_size() - b.size()); + s.async_read_some(b.prepare(bytes_available), + detail::read_streambuf_handler( + s, b, completion_condition, handler)); +} + +template +inline void async_read(AsyncReadStream& s, + asio::basic_streambuf& b, ReadHandler handler) +{ + async_read(s, b, transfer_all(), handler); +} + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_READ_IPP diff --git a/encryption/libtorrent/include/libtorrent/asio/impl/read_until.ipp b/encryption/libtorrent/include/libtorrent/asio/impl/read_until.ipp new file mode 100644 index 000000000..64c15ec7d --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/impl/read_until.ipp @@ -0,0 +1,750 @@ +// +// read_until.ipp +// ~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_READ_UNTIL_IPP +#define ASIO_READ_UNTIL_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/buffer.hpp" +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/const_buffers_iterator.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/throw_error.hpp" + +namespace asio { + +template +inline std::size_t read_until(SyncReadStream& s, + asio::basic_streambuf& b, char delim) +{ + asio::error_code ec; + std::size_t bytes_transferred = read_until(s, b, delim, ec); + asio::detail::throw_error(ec); + return bytes_transferred; +} + +template +std::size_t read_until(SyncReadStream& s, + asio::basic_streambuf& b, char delim, + asio::error_code& ec) +{ + std::size_t next_search_start = 0; + for (;;) + { + // Determine the range of the data to be searched. + typedef typename asio::basic_streambuf< + Allocator>::const_buffers_type const_buffers_type; + typedef asio::detail::const_buffers_iterator< + const_buffers_type> iterator; + const_buffers_type buffers = b.data(); + iterator begin(buffers, next_search_start); + iterator end(buffers, (std::numeric_limits::max)()); + + // Look for a match. + iterator iter = std::find(begin, end, delim); + if (iter != end) + { + // Found a match. We're done. + ec = asio::error_code(); + return iter.position() + 1; + } + else + { + // No match. Next search can start with the new data. + next_search_start = end.position(); + } + + // Check if buffer is full. + if (b.size() == b.max_size()) + { + ec = error::not_found; + return 0; + } + + // Need more data. + std::size_t bytes_available = + std::min(512, b.max_size() - b.size()); + b.commit(s.read_some(b.prepare(bytes_available), ec)); + if (ec) + return 0; + } +} + +template +inline std::size_t read_until(SyncReadStream& s, + asio::basic_streambuf& b, const std::string& delim) +{ + asio::error_code ec; + std::size_t bytes_transferred = read_until(s, b, delim, ec); + asio::detail::throw_error(ec); + return bytes_transferred; +} + +namespace detail +{ + // Algorithm that finds a subsequence of equal values in a sequence. Returns + // (iterator,true) if a full match was found, in which case the iterator + // points to the beginning of the match. Returns (iterator,false) if a + // partial match was found at the end of the first sequence, in which case + // the iterator points to the beginning of the partial match. Returns + // (last1,false) if no full or partial match was found. + template + std::pair partial_search( + Iterator1 first1, Iterator1 last1, Iterator2 first2, Iterator2 last2) + { + for (Iterator1 iter1 = first1; iter1 != last1; ++iter1) + { + Iterator1 test_iter1 = iter1; + Iterator2 test_iter2 = first2; + for (;; ++test_iter1, ++test_iter2) + { + if (test_iter2 == last2) + return std::make_pair(iter1, true); + if (test_iter1 == last1) + { + if (test_iter2 != first2) + return std::make_pair(iter1, false); + else + break; + } + if (*test_iter1 != *test_iter2) + break; + } + } + return std::make_pair(last1, false); + } +} // namespace detail + +template +std::size_t read_until(SyncReadStream& s, + asio::basic_streambuf& b, const std::string& delim, + asio::error_code& ec) +{ + std::size_t next_search_start = 0; + for (;;) + { + // Determine the range of the data to be searched. + typedef typename asio::basic_streambuf< + Allocator>::const_buffers_type const_buffers_type; + typedef asio::detail::const_buffers_iterator< + const_buffers_type> iterator; + const_buffers_type buffers = b.data(); + iterator begin(buffers, next_search_start); + iterator end(buffers, (std::numeric_limits::max)()); + + // Look for a match. + std::pair result = asio::detail::partial_search( + begin, end, delim.begin(), delim.end()); + if (result.first != end) + { + if (result.second) + { + // Full match. We're done. + ec = asio::error_code(); + return result.first.position() + delim.length(); + } + else + { + // Partial match. Next search needs to start from beginning of match. + next_search_start = result.first.position(); + } + } + else + { + // No match. Next search can start with the new data. + next_search_start = end.position(); + } + + // Check if buffer is full. + if (b.size() == b.max_size()) + { + ec = error::not_found; + return 0; + } + + // Need more data. + std::size_t bytes_available = + std::min(512, b.max_size() - b.size()); + b.commit(s.read_some(b.prepare(bytes_available), ec)); + if (ec) + return 0; + } +} + +template +inline std::size_t read_until(SyncReadStream& s, + asio::basic_streambuf& b, const boost::regex& expr) +{ + asio::error_code ec; + std::size_t bytes_transferred = read_until(s, b, expr, ec); + asio::detail::throw_error(ec); + return bytes_transferred; +} + +template +std::size_t read_until(SyncReadStream& s, + asio::basic_streambuf& b, const boost::regex& expr, + asio::error_code& ec) +{ + std::size_t next_search_start = 0; + for (;;) + { + // Determine the range of the data to be searched. + typedef typename asio::basic_streambuf< + Allocator>::const_buffers_type const_buffers_type; + typedef asio::detail::const_buffers_iterator< + const_buffers_type> iterator; + const_buffers_type buffers = b.data(); + iterator begin(buffers, next_search_start); + iterator end(buffers, (std::numeric_limits::max)()); + + // Look for a match. + boost::match_results match_results; + if (boost::regex_search(begin, end, match_results, expr, + boost::match_default | boost::match_partial)) + { + if (match_results[0].matched) + { + // Full match. We're done. + ec = asio::error_code(); + return match_results[0].second.position(); + } + else + { + // Partial match. Next search needs to start from beginning of match. + next_search_start = match_results[0].first.position(); + } + } + else + { + // No match. Next search can start with the new data. + next_search_start = end.position(); + } + + // Check if buffer is full. + if (b.size() == b.max_size()) + { + ec = error::not_found; + return 0; + } + + // Need more data. + std::size_t bytes_available = + std::min(512, b.max_size() - b.size()); + b.commit(s.read_some(b.prepare(bytes_available), ec)); + if (ec) + return 0; + } +} + +namespace detail +{ + template + class read_until_delim_handler + { + public: + read_until_delim_handler(AsyncReadStream& stream, + asio::basic_streambuf& streambuf, char delim, + std::size_t next_search_start, ReadHandler handler) + : stream_(stream), + streambuf_(streambuf), + delim_(delim), + next_search_start_(next_search_start), + handler_(handler) + { + } + + void operator()(const asio::error_code& ec, + std::size_t bytes_transferred) + { + // Check for errors. + if (ec) + { + std::size_t bytes = 0; + handler_(ec, bytes); + return; + } + + // Commit received data to streambuf's get area. + streambuf_.commit(bytes_transferred); + + // Determine the range of the data to be searched. + typedef typename asio::basic_streambuf< + Allocator>::const_buffers_type const_buffers_type; + typedef asio::detail::const_buffers_iterator< + const_buffers_type> iterator; + const_buffers_type buffers = streambuf_.data(); + iterator begin(buffers, next_search_start_); + iterator end(buffers, (std::numeric_limits::max)()); + + // Look for a match. + iterator iter = std::find(begin, end, delim_); + if (iter != end) + { + // Found a match. We're done. + std::size_t bytes = iter.position() + 1; + handler_(ec, bytes); + return; + } + + // No match. Check if buffer is full. + if (streambuf_.size() == streambuf_.max_size()) + { + std::size_t bytes = 0; + handler_(error::not_found, bytes); + return; + } + + // Next search can start with the new data. + next_search_start_ = end.position(); + + // Start a new asynchronous read operation to obtain more data. + std::size_t bytes_available = + std::min(512, streambuf_.max_size() - streambuf_.size()); + stream_.async_read_some(streambuf_.prepare(bytes_available), *this); + } + + //private: + AsyncReadStream& stream_; + asio::basic_streambuf& streambuf_; + char delim_; + std::size_t next_search_start_; + ReadHandler handler_; + }; + + template + inline void* asio_handler_allocate(std::size_t size, + read_until_delim_handler* this_handler) + { + return asio_handler_alloc_helpers::allocate( + size, &this_handler->handler_); + } + + template + inline void asio_handler_deallocate(void* pointer, std::size_t size, + read_until_delim_handler* this_handler) + { + asio_handler_alloc_helpers::deallocate( + pointer, size, &this_handler->handler_); + } + + template + inline void asio_handler_invoke(const Function& function, + read_until_delim_handler* this_handler) + { + asio_handler_invoke_helpers::invoke( + function, &this_handler->handler_); + } +} // namespace detail + +template +void async_read_until(AsyncReadStream& s, + asio::basic_streambuf& b, char delim, ReadHandler handler) +{ + // Determine the range of the data to be searched. + typedef typename asio::basic_streambuf< + Allocator>::const_buffers_type const_buffers_type; + typedef asio::detail::const_buffers_iterator< + const_buffers_type> iterator; + const_buffers_type buffers = b.data(); + iterator begin(buffers, 0); + iterator end(buffers, (std::numeric_limits::max)()); + + // Look for a match. + iterator iter = std::find(begin, end, delim); + if (iter != end) + { + // Found a match. We're done. + asio::error_code ec; + std::size_t bytes = iter.position() + 1; + s.io_service().post(detail::bind_handler(handler, ec, bytes)); + return; + } + + // No match. Check if buffer is full. + if (b.size() == b.max_size()) + { + s.io_service().post(detail::bind_handler(handler, error::not_found, 0)); + return; + } + + // Start a new asynchronous read operation to obtain more data. + std::size_t bytes_available = + std::min(512, b.max_size() - b.size()); + s.async_read_some(b.prepare(bytes_available), + detail::read_until_delim_handler( + s, b, delim, end.position(), handler)); +} + +namespace detail +{ + template + class read_until_delim_string_handler + { + public: + read_until_delim_string_handler(AsyncReadStream& stream, + asio::basic_streambuf& streambuf, + const std::string& delim, std::size_t next_search_start, + ReadHandler handler) + : stream_(stream), + streambuf_(streambuf), + delim_(delim), + next_search_start_(next_search_start), + handler_(handler) + { + } + + void operator()(const asio::error_code& ec, + std::size_t bytes_transferred) + { + // Check for errors. + if (ec) + { + std::size_t bytes = 0; + handler_(ec, bytes); + return; + } + + // Commit received data to streambuf's get area. + streambuf_.commit(bytes_transferred); + + // Determine the range of the data to be searched. + typedef typename asio::basic_streambuf< + Allocator>::const_buffers_type const_buffers_type; + typedef asio::detail::const_buffers_iterator< + const_buffers_type> iterator; + const_buffers_type buffers = streambuf_.data(); + iterator begin(buffers, next_search_start_); + iterator end(buffers, (std::numeric_limits::max)()); + + // Look for a match. + std::pair result = asio::detail::partial_search( + begin, end, delim_.begin(), delim_.end()); + if (result.first != end) + { + if (result.second) + { + // Full match. We're done. + std::size_t bytes = result.first.position() + delim_.length(); + handler_(ec, bytes); + return; + } + else + { + // Partial match. Next search needs to start from beginning of match. + next_search_start_ = result.first.position(); + } + } + else + { + // No match. Next search can start with the new data. + next_search_start_ = end.position(); + } + + // Check if buffer is full. + if (streambuf_.size() == streambuf_.max_size()) + { + std::size_t bytes = 0; + handler_(error::not_found, bytes); + return; + } + + // Start a new asynchronous read operation to obtain more data. + std::size_t bytes_available = + std::min(512, streambuf_.max_size() - streambuf_.size()); + stream_.async_read_some(streambuf_.prepare(bytes_available), *this); + } + + //private: + AsyncReadStream& stream_; + asio::basic_streambuf& streambuf_; + std::string delim_; + std::size_t next_search_start_; + ReadHandler handler_; + }; + + template + inline void* asio_handler_allocate(std::size_t size, + read_until_delim_string_handler* this_handler) + { + return asio_handler_alloc_helpers::allocate( + size, &this_handler->handler_); + } + + template + inline void asio_handler_deallocate(void* pointer, std::size_t size, + read_until_delim_string_handler* this_handler) + { + asio_handler_alloc_helpers::deallocate( + pointer, size, &this_handler->handler_); + } + + template + inline void asio_handler_invoke(const Function& function, + read_until_delim_string_handler* this_handler) + { + asio_handler_invoke_helpers::invoke( + function, &this_handler->handler_); + } +} // namespace detail + +template +void async_read_until(AsyncReadStream& s, + asio::basic_streambuf& b, const std::string& delim, + ReadHandler handler) +{ + // Determine the range of the data to be searched. + typedef typename asio::basic_streambuf< + Allocator>::const_buffers_type const_buffers_type; + typedef asio::detail::const_buffers_iterator< + const_buffers_type> iterator; + const_buffers_type buffers = b.data(); + iterator begin(buffers, 0); + iterator end(buffers, (std::numeric_limits::max)()); + + // Look for a match. + std::size_t next_search_start; + std::pair result = asio::detail::partial_search( + begin, end, delim.begin(), delim.end()); + if (result.first != end) + { + if (result.second) + { + // Full match. We're done. + asio::error_code ec; + std::size_t bytes = result.first.position() + delim.length(); + s.io_service().post(detail::bind_handler(handler, ec, bytes)); + return; + } + else + { + // Partial match. Next search needs to start from beginning of match. + next_search_start = result.first.position(); + } + } + else + { + // No match. Next search can start with the new data. + next_search_start = end.position(); + } + + // Check if buffer is full. + if (b.size() == b.max_size()) + { + s.io_service().post(detail::bind_handler(handler, error::not_found, 0)); + return; + } + + // Start a new asynchronous read operation to obtain more data. + std::size_t bytes_available = + std::min(512, b.max_size() - b.size()); + s.async_read_some(b.prepare(bytes_available), + detail::read_until_delim_string_handler< + AsyncReadStream, Allocator, ReadHandler>( + s, b, delim, next_search_start, handler)); +} + +namespace detail +{ + template + class read_until_expr_handler + { + public: + read_until_expr_handler(AsyncReadStream& stream, + asio::basic_streambuf& streambuf, + const boost::regex& expr, std::size_t next_search_start, + ReadHandler handler) + : stream_(stream), + streambuf_(streambuf), + expr_(expr), + next_search_start_(next_search_start), + handler_(handler) + { + } + + void operator()(const asio::error_code& ec, + std::size_t bytes_transferred) + { + // Check for errors. + if (ec) + { + std::size_t bytes = 0; + handler_(ec, bytes); + return; + } + + // Commit received data to streambuf's get area. + streambuf_.commit(bytes_transferred); + + // Determine the range of the data to be searched. + typedef typename asio::basic_streambuf< + Allocator>::const_buffers_type const_buffers_type; + typedef asio::detail::const_buffers_iterator< + const_buffers_type> iterator; + const_buffers_type buffers = streambuf_.data(); + iterator begin(buffers, next_search_start_); + iterator end(buffers, (std::numeric_limits::max)()); + + // Look for a match. + boost::match_results match_results; + if (boost::regex_search(begin, end, match_results, expr_, + boost::match_default | boost::match_partial)) + { + if (match_results[0].matched) + { + // Full match. We're done. + std::size_t bytes = match_results[0].second.position(); + handler_(ec, bytes); + return; + } + else + { + // Partial match. Next search needs to start from beginning of match. + next_search_start_ = match_results[0].first.position(); + } + } + else + { + // No match. Next search can start with the new data. + next_search_start_ = end.position(); + } + + // Check if buffer is full. + if (streambuf_.size() == streambuf_.max_size()) + { + std::size_t bytes = 0; + handler_(error::not_found, bytes); + return; + } + + // Start a new asynchronous read operation to obtain more data. + std::size_t bytes_available = + std::min(512, streambuf_.max_size() - streambuf_.size()); + stream_.async_read_some(streambuf_.prepare(bytes_available), *this); + } + + //private: + AsyncReadStream& stream_; + asio::basic_streambuf& streambuf_; + boost::regex expr_; + std::size_t next_search_start_; + ReadHandler handler_; + }; + + template + inline void* asio_handler_allocate(std::size_t size, + read_until_expr_handler* this_handler) + { + return asio_handler_alloc_helpers::allocate( + size, &this_handler->handler_); + } + + template + inline void asio_handler_deallocate(void* pointer, std::size_t size, + read_until_expr_handler* this_handler) + { + asio_handler_alloc_helpers::deallocate( + pointer, size, &this_handler->handler_); + } + + template + inline void asio_handler_invoke(const Function& function, + read_until_expr_handler* this_handler) + { + asio_handler_invoke_helpers::invoke( + function, &this_handler->handler_); + } +} // namespace detail + +template +void async_read_until(AsyncReadStream& s, + asio::basic_streambuf& b, const boost::regex& expr, + ReadHandler handler) +{ + // Determine the range of the data to be searched. + typedef typename asio::basic_streambuf< + Allocator>::const_buffers_type const_buffers_type; + typedef asio::detail::const_buffers_iterator< + const_buffers_type> iterator; + const_buffers_type buffers = b.data(); + iterator begin(buffers, 0); + iterator end(buffers, (std::numeric_limits::max)()); + + // Look for a match. + std::size_t next_search_start; + boost::match_results match_results; + if (boost::regex_search(begin, end, match_results, expr, + boost::match_default | boost::match_partial)) + { + if (match_results[0].matched) + { + // Full match. We're done. + asio::error_code ec; + std::size_t bytes = match_results[0].second.position(); + s.io_service().post(detail::bind_handler(handler, ec, bytes)); + return; + } + else + { + // Partial match. Next search needs to start from beginning of match. + next_search_start = match_results[0].first.position(); + } + } + else + { + // No match. Next search can start with the new data. + next_search_start = end.position(); + } + + // Check if buffer is full. + if (b.size() == b.max_size()) + { + s.io_service().post(detail::bind_handler(handler, error::not_found, 0)); + return; + } + + // Start a new asynchronous read operation to obtain more data. + std::size_t bytes_available = + std::min(512, b.max_size() - b.size()); + s.async_read_some(b.prepare(bytes_available), + detail::read_until_expr_handler( + s, b, expr, next_search_start, handler)); +} + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_READ_UNTIL_IPP diff --git a/encryption/libtorrent/include/libtorrent/asio/impl/write.ipp b/encryption/libtorrent/include/libtorrent/asio/impl/write.ipp new file mode 100644 index 000000000..8c2d1d33f --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/impl/write.ipp @@ -0,0 +1,279 @@ +// +// write.ipp +// ~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_WRITE_IPP +#define ASIO_WRITE_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/buffer.hpp" +#include "asio/completion_condition.hpp" +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/consuming_buffers.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/throw_error.hpp" + +namespace asio { + +template +std::size_t write(SyncWriteStream& s, const ConstBufferSequence& buffers, + CompletionCondition completion_condition, asio::error_code& ec) +{ + asio::detail::consuming_buffers< + const_buffer, ConstBufferSequence> tmp(buffers); + std::size_t total_transferred = 0; + while (tmp.begin() != tmp.end()) + { + std::size_t bytes_transferred = s.write_some(tmp, ec); + tmp.consume(bytes_transferred); + total_transferred += bytes_transferred; + if (completion_condition(ec, total_transferred)) + return total_transferred; + } + ec = asio::error_code(); + return total_transferred; +} + +template +inline std::size_t write(SyncWriteStream& s, const ConstBufferSequence& buffers) +{ + asio::error_code ec; + std::size_t bytes_transferred = write(s, buffers, transfer_all(), ec); + asio::detail::throw_error(ec); + return bytes_transferred; +} + +template +inline std::size_t write(SyncWriteStream& s, const ConstBufferSequence& buffers, + CompletionCondition completion_condition) +{ + asio::error_code ec; + std::size_t bytes_transferred = write(s, buffers, completion_condition, ec); + asio::detail::throw_error(ec); + return bytes_transferred; +} + +template +std::size_t write(SyncWriteStream& s, + asio::basic_streambuf& b, + CompletionCondition completion_condition, asio::error_code& ec) +{ + std::size_t bytes_transferred = write(s, b.data(), completion_condition, ec); + b.consume(bytes_transferred); + return bytes_transferred; +} + +template +inline std::size_t write(SyncWriteStream& s, + asio::basic_streambuf& b) +{ + asio::error_code ec; + std::size_t bytes_transferred = write(s, b, transfer_all(), ec); + asio::detail::throw_error(ec); + return bytes_transferred; +} + +template +inline std::size_t write(SyncWriteStream& s, + asio::basic_streambuf& b, + CompletionCondition completion_condition) +{ + asio::error_code ec; + std::size_t bytes_transferred = write(s, b, completion_condition, ec); + asio::detail::throw_error(ec); + return bytes_transferred; +} + +namespace detail +{ + template + class write_handler + { + public: + typedef asio::detail::consuming_buffers< + const_buffer, ConstBufferSequence> buffers_type; + + write_handler(AsyncWriteStream& stream, const buffers_type& buffers, + CompletionCondition completion_condition, WriteHandler handler) + : stream_(stream), + buffers_(buffers), + total_transferred_(0), + completion_condition_(completion_condition), + handler_(handler) + { + } + + void operator()(const asio::error_code& ec, + std::size_t bytes_transferred) + { + total_transferred_ += bytes_transferred; + buffers_.consume(bytes_transferred); + if (completion_condition_(ec, total_transferred_) + || buffers_.begin() == buffers_.end()) + { + handler_(ec, total_transferred_); + } + else + { + stream_.async_write_some(buffers_, *this); + } + } + + //private: + AsyncWriteStream& stream_; + buffers_type buffers_; + std::size_t total_transferred_; + CompletionCondition completion_condition_; + WriteHandler handler_; + }; + + template + inline void* asio_handler_allocate(std::size_t size, + write_handler* this_handler) + { + return asio_handler_alloc_helpers::allocate( + size, &this_handler->handler_); + } + + template + inline void asio_handler_deallocate(void* pointer, std::size_t size, + write_handler* this_handler) + { + asio_handler_alloc_helpers::deallocate( + pointer, size, &this_handler->handler_); + } + + template + inline void asio_handler_invoke(const Function& function, + write_handler* this_handler) + { + asio_handler_invoke_helpers::invoke( + function, &this_handler->handler_); + } +} // namespace detail + +template +inline void async_write(AsyncWriteStream& s, const ConstBufferSequence& buffers, + CompletionCondition completion_condition, WriteHandler handler) +{ + asio::detail::consuming_buffers< + const_buffer, ConstBufferSequence> tmp(buffers); + s.async_write_some(tmp, + detail::write_handler( + s, tmp, completion_condition, handler)); +} + +template +inline void async_write(AsyncWriteStream& s, const ConstBufferSequence& buffers, + WriteHandler handler) +{ + async_write(s, buffers, transfer_all(), handler); +} + +namespace detail +{ + template + class write_streambuf_handler + { + public: + write_streambuf_handler(asio::basic_streambuf& streambuf, + WriteHandler handler) + : streambuf_(streambuf), + handler_(handler) + { + } + + void operator()(const asio::error_code& ec, + std::size_t bytes_transferred) + { + streambuf_.consume(bytes_transferred); + handler_(ec, bytes_transferred); + } + + //private: + asio::basic_streambuf& streambuf_; + WriteHandler handler_; + }; + + template + inline void* asio_handler_allocate(std::size_t size, + write_streambuf_handler* this_handler) + { + return asio_handler_alloc_helpers::allocate( + size, &this_handler->handler_); + } + + template + inline void asio_handler_deallocate(void* pointer, std::size_t size, + write_streambuf_handler* this_handler) + { + asio_handler_alloc_helpers::deallocate( + pointer, size, &this_handler->handler_); + } + + template + inline void asio_handler_invoke(const Function& function, + write_streambuf_handler* this_handler) + { + asio_handler_invoke_helpers::invoke( + function, &this_handler->handler_); + } +} // namespace detail + +template +inline void async_write(AsyncWriteStream& s, + asio::basic_streambuf& b, + CompletionCondition completion_condition, WriteHandler handler) +{ + async_write(s, b.data(), completion_condition, + detail::write_streambuf_handler< + AsyncWriteStream, Allocator, WriteHandler>(b, handler)); +} + +template +inline void async_write(AsyncWriteStream& s, + asio::basic_streambuf& b, WriteHandler handler) +{ + async_write(s, b, transfer_all(), handler); +} + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_WRITE_IPP diff --git a/encryption/libtorrent/include/libtorrent/asio/io_service.hpp b/encryption/libtorrent/include/libtorrent/asio/io_service.hpp new file mode 100644 index 000000000..731a7ac9a --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/io_service.hpp @@ -0,0 +1,501 @@ +// +// io_service.hpp +// ~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IO_SERVICE_HPP +#define ASIO_IO_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/error_code.hpp" +#include "asio/detail/epoll_reactor_fwd.hpp" +#include "asio/detail/kqueue_reactor_fwd.hpp" +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/select_reactor_fwd.hpp" +#include "asio/detail/service_registry_fwd.hpp" +#include "asio/detail/signal_init.hpp" +#include "asio/detail/task_io_service_fwd.hpp" +#include "asio/detail/win_iocp_io_service_fwd.hpp" +#include "asio/detail/winsock_init.hpp" +#include "asio/detail/wrapped_handler.hpp" + +namespace asio { + +/// Provides core I/O functionality. +/** + * The io_service class provides the core I/O functionality for users of the + * asynchronous I/O objects, including: + * + * @li asio::ip::tcp::socket + * @li asio::ip::tcp::acceptor + * @li asio::ip::udp::socket + * @li asio::deadline_timer. + * + * The io_service class also includes facilities intended for developers of + * custom asynchronous services. + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Safe, with the exception that calling reset() + * while there are unfinished run() calls results in undefined behaviour. + * + * @par Concepts: + * Dispatcher. + * + * @sa @ref io_service_handler_exception + */ +class io_service + : private noncopyable +{ +private: + // The type of the platform-specific implementation. +#if defined(ASIO_HAS_IOCP) + typedef detail::win_iocp_io_service impl_type; +#elif defined(ASIO_HAS_EPOLL) + typedef detail::task_io_service > impl_type; +#elif defined(ASIO_HAS_KQUEUE) + typedef detail::task_io_service > impl_type; +#else + typedef detail::task_io_service > impl_type; +#endif + +public: + class work; + friend class work; + + class id; + + class service; + + class strand; + + /// Constructor. + io_service(); + + /// Constructor. + /** + * Construct with a hint about the required level of concurrency. + * + * @param concurrency_hint A suggestion to the implementation on how many + * threads it should allow to run simultaneously. + */ + explicit io_service(std::size_t concurrency_hint); + + /// Destructor. + ~io_service(); + + /// Run the io_service's event processing loop. + /** + * The run() function blocks until all work has finished and there are no + * more handlers to be dispatched, or until the io_service has been stopped. + * + * Multiple threads may call the run() function to set up a pool of threads + * from which the io_service may execute handlers. + * + * The run() function may be safely called again once it has completed only + * after a call to reset(). + * + * @return The number of handlers that were executed. + * + * @throws asio::system_error Thrown on failure. + */ + std::size_t run(); + + /// Run the io_service's event processing loop. + /** + * The run() function blocks until all work has finished and there are no + * more handlers to be dispatched, or until the io_service has been stopped. + * + * Multiple threads may call the run() function to set up a pool of threads + * from which the io_service may execute handlers. + * + * The run() function may be safely called again once it has completed only + * after a call to reset(). + * + * @param ec Set to indicate what error occurred, if any. + * + * @return The number of handlers that were executed. + */ + std::size_t run(asio::error_code& ec); + + /// Run the io_service's event processing loop to execute at most one handler. + /** + * The run_one() function blocks until one handler has been dispatched, or + * until the io_service has been stopped. + * + * @return The number of handlers that were executed. + * + * @throws asio::system_error Thrown on failure. + */ + std::size_t run_one(); + + /// Run the io_service's event processing loop to execute at most one handler. + /** + * The run_one() function blocks until one handler has been dispatched, or + * until the io_service has been stopped. + * + * @param ec Set to indicate what error occurred, if any. + * + * @return The number of handlers that were executed. + */ + std::size_t run_one(asio::error_code& ec); + + /// Run the io_service's event processing loop to execute ready handlers. + /** + * The poll() function runs handlers that are ready to run, without blocking, + * until the io_service has been stopped or there are no more ready handlers. + * + * @return The number of handlers that were executed. + * + * @throws asio::system_error Thrown on failure. + */ + std::size_t poll(); + + /// Run the io_service's event processing loop to execute ready handlers. + /** + * The poll() function runs handlers that are ready to run, without blocking, + * until the io_service has been stopped or there are no more ready handlers. + * + * @param ec Set to indicate what error occurred, if any. + * + * @return The number of handlers that were executed. + */ + std::size_t poll(asio::error_code& ec); + + /// Run the io_service's event processing loop to execute one ready handler. + /** + * The poll_one() function runs at most one handler that is ready to run, + * without blocking. + * + * @return The number of handlers that were executed. + * + * @throws asio::system_error Thrown on failure. + */ + std::size_t poll_one(); + + /// Run the io_service's event processing loop to execute one ready handler. + /** + * The poll_one() function runs at most one handler that is ready to run, + * without blocking. + * + * @param ec Set to indicate what error occurred, if any. + * + * @return The number of handlers that were executed. + */ + std::size_t poll_one(asio::error_code& ec); + + /// Stop the io_service's event processing loop. + /** + * This function does not block, but instead simply signals the io_service to + * stop. All invocations of its run() or run_one() member functions should + * return as soon as possible. Subsequent calls to run(), run_one(), poll() + * or poll_one() will return immediately until reset() is called. + */ + void stop(); + + /// Reset the io_service in preparation for a subsequent run() invocation. + /** + * This function must be called prior to any second or later set of + * invocations of the run(), run_one(), poll() or poll_one() functions when a + * previous invocation of these functions returned due to the io_service + * being stopped or running out of work. This function allows the io_service + * to reset any internal state, such as a "stopped" flag. + * + * This function must not be called while there are any unfinished calls to + * the run(), run_one(), poll() or poll_one() functions. + */ + void reset(); + + /// Request the io_service to invoke the given handler. + /** + * This function is used to ask the io_service to execute the given handler. + * + * The io_service guarantees that the handler will only be called in a thread + * in which the run(), run_one(), poll() or poll_one() member functions is + * currently being invoked. The handler may be executed inside this function + * if the guarantee can be met. + * + * @param handler The handler to be called. The io_service will make + * a copy of the handler object as required. The function signature of the + * handler must be: @code void handler(); @endcode + */ + template + void dispatch(CompletionHandler handler); + + /// Request the io_service to invoke the given handler and return immediately. + /** + * This function is used to ask the io_service to execute the given handler, + * but without allowing the io_service to call the handler from inside this + * function. + * + * The io_service guarantees that the handler will only be called in a thread + * in which the run(), run_one(), poll() or poll_one() member functions is + * currently being invoked. + * + * @param handler The handler to be called. The io_service will make + * a copy of the handler object as required. The function signature of the + * handler must be: @code void handler(); @endcode + */ + template + void post(CompletionHandler handler); + + /// Create a new handler that automatically dispatches the wrapped handler + /// on the io_service. + /** + * This function is used to create a new handler function object that, when + * invoked, will automatically pass the wrapped handler to the io_service's + * dispatch function. + * + * @param handler The handler to be wrapped. The io_service will make a copy + * of the handler object as required. The function signature of the handler + * must be: @code void handler(A1 a1, ... An an); @endcode + * + * @return A function object that, when invoked, passes the wrapped handler to + * the io_service's dispatch function. Given a function object with the + * signature: + * @code R f(A1 a1, ... An an); @endcode + * If this function object is passed to the wrap function like so: + * @code io_service.wrap(f); @endcode + * then the return value is a function object with the signature + * @code void g(A1 a1, ... An an); @endcode + * that, when invoked, executes code equivalent to: + * @code io_service.dispatch(boost::bind(f, a1, ... an)); @endcode + */ + template +#if defined(GENERATING_DOCUMENTATION) + unspecified +#else + detail::wrapped_handler +#endif + wrap(Handler handler); + + /// Obtain the service object corresponding to the given type. + /** + * This function is used to locate a service object that corresponds to + * the given service type. If there is no existing implementation of the + * service, then the io_service will create a new instance of the service. + * + * @param ios The io_service object that owns the service. + * + * @return The service interface implementing the specified service type. + * Ownership of the service interface is not transferred to the caller. + */ + template + friend Service& use_service(io_service& ios); + + /// Add a service object to the io_service. + /** + * This function is used to add a service to the io_service. + * + * @param ios The io_service object that owns the service. + * + * @param svc The service object. On success, ownership of the service object + * is transferred to the io_service. When the io_service object is destroyed, + * it will destroy the service object by performing: + * @code delete static_cast(svc) @endcode + * + * @throws asio::service_already_exists Thrown if a service of the + * given type is already present in the io_service. + * + * @throws asio::invalid_service_owner Thrown if the service's owning + * io_service is not the io_service object specified by the ios parameter. + */ + template + friend void add_service(io_service& ios, Service* svc); + + /// Determine if an io_service contains a specified service type. + /** + * This function is used to determine whether the io_service contains a + * service object corresponding to the given service type. + * + * @param ios The io_service object that owns the service. + * + * @return A boolean indicating whether the io_service contains the service. + */ + template + friend bool has_service(io_service& ios); + +private: +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + detail::winsock_init<> init_; +#elif defined(__sun) || defined(__QNX__) + detail::signal_init<> init_; +#endif + + // The service registry. + asio::detail::service_registry* service_registry_; + + // The implementation. + impl_type& impl_; +}; + +/// Class to inform the io_service when it has work to do. +/** + * The work class is used to inform the io_service when work starts and + * finishes. This ensures that the io_service's run() function will not exit + * while work is underway, and that it does exit when there is no unfinished + * work remaining. + * + * The work class is copy-constructible so that it may be used as a data member + * in a handler class. It is not assignable. + */ +class io_service::work +{ +public: + /// Constructor notifies the io_service that work is starting. + /** + * The constructor is used to inform the io_service that some work has begun. + * This ensures that the io_service's run() function will not exit while the + * work is underway. + */ + explicit work(asio::io_service& io_service); + + /// Copy constructor notifies the io_service that work is starting. + /** + * The constructor is used to inform the io_service that some work has begun. + * This ensures that the io_service's run() function will not exit while the + * work is underway. + */ + work(const work& other); + + /// Destructor notifies the io_service that the work is complete. + /** + * The destructor is used to inform the io_service that some work has + * finished. Once the count of unfinished work reaches zero, the io_service's + * run() function is permitted to exit. + */ + ~work(); + + /// Get the io_service associated with the work. + asio::io_service& io_service(); + +private: + // Prevent assignment. + void operator=(const work& other); + + // The io_service. + asio::io_service& io_service_; +}; + +/// Class used to uniquely identify a service. +class io_service::id + : private noncopyable +{ +public: + /// Constructor. + id() {} +}; + +/// Base class for all io_service services. +class io_service::service + : private noncopyable +{ +public: + /// Get the io_service object that owns the service. + asio::io_service& io_service(); + +protected: + /// Constructor. + /** + * @param owner The io_service object that owns the service. + */ + service(asio::io_service& owner); + + /// Destructor. + virtual ~service(); + +private: + /// Destroy all user-defined handler objects owned by the service. + virtual void shutdown_service() = 0; + + friend class asio::detail::service_registry; + asio::io_service& owner_; + const std::type_info* type_info_; + const asio::io_service::id* id_; + service* next_; +}; + +/// Exception thrown when trying to add a duplicate service to an io_service. +class service_already_exists + : public std::logic_error +{ +public: + service_already_exists() + : std::logic_error("Service already exists.") + { + } +}; + +/// Exception thrown when trying to add a service object to an io_service where +/// the service has a different owner. +class invalid_service_owner + : public std::logic_error +{ +public: + invalid_service_owner() + : std::logic_error("Invalid service owner.") + { + } +}; + +/** + * @page io_service_handler_exception Effect of exceptions thrown from handlers + * + * If an exception is thrown from a handler, the exception is allowed to + * propagate through the throwing thread's invocation of + * asio::io_service::run(), asio::io_service::run_one(), + * asio::io_service::poll() or asio::io_service::poll_one(). + * No other threads that are calling any of these functions are affected. It is + * then the responsibility of the application to catch the exception. + * + * After the exception has been caught, the + * asio::io_service::run(), asio::io_service::run_one(), + * asio::io_service::poll() or asio::io_service::poll_one() + * call may be restarted @em without the need for an intervening call to + * asio::io_service::reset(). This allows the thread to rejoin the + * io_service's thread pool without impacting any other threads in the pool. + * + * @par Example + * @code + * asio::io_service io_service; + * ... + * for (;;) + * { + * try + * { + * io_service.run(); + * break; // run() exited normally + * } + * catch (my_exception& e) + * { + * // Deal with exception as appropriate. + * } + * } + * @endcode + */ + +} // namespace asio + +#include "asio/impl/io_service.ipp" + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IO_SERVICE_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/ip/address.hpp b/encryption/libtorrent/include/libtorrent/asio/ip/address.hpp new file mode 100644 index 000000000..1b2c548bf --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/ip/address.hpp @@ -0,0 +1,277 @@ +// +// address.hpp +// ~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IP_ADDRESS_HPP +#define ASIO_IP_ADDRESS_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/error.hpp" +#include "asio/ip/address_v4.hpp" +#include "asio/ip/address_v6.hpp" +#include "asio/detail/throw_error.hpp" + +namespace asio { +namespace ip { + +/// Implements version-independent IP addresses. +/** + * The asio::ip::address class provides the ability to use either IP + * version 4 or version 6 addresses. + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + */ +class address +{ +public: + /// Default constructor. + address() + : type_(ipv4), + ipv4_address_(), + ipv6_address_() + { + } + + /// Construct an address from an IPv4 address. + address(const asio::ip::address_v4& ipv4_address) + : type_(ipv4), + ipv4_address_(ipv4_address), + ipv6_address_() + { + } + + /// Construct an address from an IPv6 address. + address(const asio::ip::address_v6& ipv6_address) + : type_(ipv6), + ipv4_address_(), + ipv6_address_(ipv6_address) + { + } + + /// Copy constructor. + address(const address& other) + : type_(other.type_), + ipv4_address_(other.ipv4_address_), + ipv6_address_(other.ipv6_address_) + { + } + + /// Assign from another address. + address& operator=(const address& other) + { + type_ = other.type_; + ipv4_address_ = other.ipv4_address_; + ipv6_address_ = other.ipv6_address_; + return *this; + } + + /// Assign from an IPv4 address. + address& operator=(const asio::ip::address_v4& ipv4_address) + { + type_ = ipv4; + ipv4_address_ = ipv4_address; + ipv6_address_ = asio::ip::address_v6(); + return *this; + } + + /// Assign from an IPv6 address. + address& operator=(const asio::ip::address_v6& ipv6_address) + { + type_ = ipv6; + ipv4_address_ = asio::ip::address_v4(); + ipv6_address_ = ipv6_address; + return *this; + } + + /// Get whether the address is an IP version 4 address. + bool is_v4() const + { + return type_ == ipv4; + } + + /// Get whether the address is an IP version 6 address. + bool is_v6() const + { + return type_ == ipv6; + } + + /// Get the address as an IP version 4 address. + asio::ip::address_v4 to_v4() const + { + if (type_ != ipv4) + { + asio::system_error e( + asio::error::address_family_not_supported); + boost::throw_exception(e); + } + return ipv4_address_; + } + + /// Get the address as an IP version 6 address. + asio::ip::address_v6 to_v6() const + { + if (type_ != ipv6) + { + asio::system_error e( + asio::error::address_family_not_supported); + boost::throw_exception(e); + } + return ipv6_address_; + } + + /// Get the address as a string in dotted decimal format. + std::string to_string() const + { + if (type_ == ipv6) + return ipv6_address_.to_string(); + return ipv4_address_.to_string(); + } + + /// Get the address as a string in dotted decimal format. + std::string to_string(asio::error_code& ec) const + { + if (type_ == ipv6) + return ipv6_address_.to_string(ec); + return ipv4_address_.to_string(ec); + } + + /// Create an address from an IPv4 address string in dotted decimal form, + /// or from an IPv6 address in hexadecimal notation. + static address from_string(const char* str) + { + asio::error_code ec; + address addr = from_string(str, ec); + asio::detail::throw_error(ec); + return addr; + } + + /// Create an address from an IPv4 address string in dotted decimal form, + /// or from an IPv6 address in hexadecimal notation. + static address from_string(const char* str, asio::error_code& ec) + { + asio::ip::address_v6 ipv6_address = + asio::ip::address_v6::from_string(str, ec); + if (!ec) + { + address tmp; + tmp.type_ = ipv6; + tmp.ipv6_address_ = ipv6_address; + return tmp; + } + + asio::ip::address_v4 ipv4_address = + asio::ip::address_v4::from_string(str, ec); + if (!ec) + { + address tmp; + tmp.type_ = ipv4; + tmp.ipv4_address_ = ipv4_address; + return tmp; + } + + return address(); + } + + /// Create an address from an IPv4 address string in dotted decimal form, + /// or from an IPv6 address in hexadecimal notation. + static address from_string(const std::string& str) + { + return from_string(str.c_str()); + } + + /// Create an address from an IPv4 address string in dotted decimal form, + /// or from an IPv6 address in hexadecimal notation. + static address from_string(const std::string& str, + asio::error_code& ec) + { + return from_string(str.c_str(), ec); + } + + /// Compare two addresses for equality. + friend bool operator==(const address& a1, const address& a2) + { + if (a1.type_ != a2.type_) + return false; + if (a1.type_ == ipv6) + return a1.ipv6_address_ == a2.ipv6_address_; + return a1.ipv4_address_ == a2.ipv4_address_; + } + + /// Compare two addresses for inequality. + friend bool operator!=(const address& a1, const address& a2) + { + if (a1.type_ != a2.type_) + return true; + if (a1.type_ == ipv6) + return a1.ipv6_address_ != a2.ipv6_address_; + return a1.ipv4_address_ != a2.ipv4_address_; + } + + /// Compare addresses for ordering. + friend bool operator<(const address& a1, const address& a2) + { + if (a1.type_ < a2.type_) + return true; + if (a1.type_ > a2.type_) + return false; + if (a1.type_ == ipv6) + return a1.ipv6_address_ < a2.ipv6_address_; + return a1.ipv4_address_ < a2.ipv4_address_; + } + +private: + // The type of the address. + enum { ipv4, ipv6 } type_; + + // The underlying IPv4 address. + asio::ip::address_v4 ipv4_address_; + + // The underlying IPv6 address. + asio::ip::address_v6 ipv6_address_; +}; + +/// Output an address as a string. +/** + * Used to output a human-readable string for a specified address. + * + * @param os The output stream to which the string will be written. + * + * @param addr The address to be written. + * + * @return The output stream. + * + * @relates asio::ip::address + */ +template +std::basic_ostream& operator<<( + std::basic_ostream& os, const address& addr) +{ + os << addr.to_string(); + return os; +} + +} // namespace ip +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IP_ADDRESS_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/ip/address_v4.hpp b/encryption/libtorrent/include/libtorrent/asio/ip/address_v4.hpp new file mode 100644 index 000000000..ae3891c95 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/ip/address_v4.hpp @@ -0,0 +1,283 @@ +// +// address_v4.hpp +// ~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IP_ADDRESS_V4_HPP +#define ASIO_IP_ADDRESS_V4_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/error.hpp" +#include "asio/detail/socket_ops.hpp" +#include "asio/detail/socket_types.hpp" +#include "asio/detail/throw_error.hpp" + +namespace asio { +namespace ip { + +/// Implements IP version 4 style addresses. +/** + * The asio::ip::address_v4 class provides the ability to use and + * manipulate IP version 4 addresses. + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + */ +class address_v4 +{ +public: + /// The type used to represent an address as an array of bytes. + typedef boost::array bytes_type; + + /// Default constructor. + address_v4() + { + addr_.s_addr = 0; + } + + /// Construct an address from raw bytes. + explicit address_v4(const bytes_type& bytes) + { + using namespace std; // For memcpy. + memcpy(&addr_.s_addr, bytes.elems, 4); + } + + /// Construct an address from a unsigned long in host byte order. + explicit address_v4(unsigned long addr) + { + addr_.s_addr = asio::detail::socket_ops::host_to_network_long(addr); + } + + /// Copy constructor. + address_v4(const address_v4& other) + : addr_(other.addr_) + { + } + + /// Assign from another address. + address_v4& operator=(const address_v4& other) + { + addr_ = other.addr_; + return *this; + } + + /// Get the address in bytes. + bytes_type to_bytes() const + { + using namespace std; // For memcpy. + bytes_type bytes; + memcpy(bytes.elems, &addr_.s_addr, 4); + return bytes; + } + + /// Get the address as an unsigned long in host byte order + unsigned long to_ulong() const + { + return asio::detail::socket_ops::network_to_host_long(addr_.s_addr); + } + + /// Get the address as a string in dotted decimal format. + std::string to_string() const + { + asio::error_code ec; + std::string addr = to_string(ec); + asio::detail::throw_error(ec); + return addr; + } + + /// Get the address as a string in dotted decimal format. + std::string to_string(asio::error_code& ec) const + { + char addr_str[asio::detail::max_addr_v4_str_len]; + const char* addr = + asio::detail::socket_ops::inet_ntop(AF_INET, &addr_, addr_str, + asio::detail::max_addr_v4_str_len, 0, ec); + if (addr == 0) + return std::string(); + return addr; + } + + /// Create an address from an IP address string in dotted decimal form. + static address_v4 from_string(const char* str) + { + asio::error_code ec; + address_v4 addr = from_string(str, ec); + asio::detail::throw_error(ec); + return addr; + } + + /// Create an address from an IP address string in dotted decimal form. + static address_v4 from_string(const char* str, asio::error_code& ec) + { + address_v4 tmp; + if (asio::detail::socket_ops::inet_pton( + AF_INET, str, &tmp.addr_, 0, ec) <= 0) + return address_v4(); + return tmp; + } + + /// Create an address from an IP address string in dotted decimal form. + static address_v4 from_string(const std::string& str) + { + return from_string(str.c_str()); + } + + /// Create an address from an IP address string in dotted decimal form. + static address_v4 from_string(const std::string& str, + asio::error_code& ec) + { + return from_string(str.c_str(), ec); + } + + /// Determine whether the address is a class A address. + bool is_class_a() const + { + return IN_CLASSA(to_ulong()); + } + + /// Determine whether the address is a class B address. + bool is_class_b() const + { + return IN_CLASSB(to_ulong()); + } + + /// Determine whether the address is a class C address. + bool is_class_c() const + { + return IN_CLASSC(to_ulong()); + } + + /// Determine whether the address is a multicast address. + bool is_multicast() const + { + return IN_MULTICAST(to_ulong()); + } + + /// Compare two addresses for equality. + friend bool operator==(const address_v4& a1, const address_v4& a2) + { + return a1.addr_.s_addr == a2.addr_.s_addr; + } + + /// Compare two addresses for inequality. + friend bool operator!=(const address_v4& a1, const address_v4& a2) + { + return a1.addr_.s_addr != a2.addr_.s_addr; + } + + /// Compare addresses for ordering. + friend bool operator<(const address_v4& a1, const address_v4& a2) + { + return a1.to_ulong() < a2.to_ulong(); + } + + /// Compare addresses for ordering. + friend bool operator>(const address_v4& a1, const address_v4& a2) + { + return a1.to_ulong() > a2.to_ulong(); + } + + /// Compare addresses for ordering. + friend bool operator<=(const address_v4& a1, const address_v4& a2) + { + return a1.to_ulong() <= a2.to_ulong(); + } + + /// Compare addresses for ordering. + friend bool operator>=(const address_v4& a1, const address_v4& a2) + { + return a1.to_ulong() >= a2.to_ulong(); + } + + /// Obtain an address object that represents any address. + static address_v4 any() + { + return address_v4(static_cast(INADDR_ANY)); + } + + /// Obtain an address object that represents the loopback address. + static address_v4 loopback() + { + return address_v4(static_cast(INADDR_LOOPBACK)); + } + + /// Obtain an address object that represents the broadcast address. + static address_v4 broadcast() + { + return address_v4(static_cast(INADDR_BROADCAST)); + } + + /// Obtain an address object that represents the broadcast address that + /// corresponds to the specified address and netmask. + static address_v4 broadcast(const address_v4& addr, const address_v4& mask) + { + return address_v4(addr.to_ulong() | ~mask.to_ulong()); + } + + /// Obtain the netmask that corresponds to the address, based on its address + /// class. + static address_v4 netmask(const address_v4& addr) + { + if (addr.is_class_a()) + return address_v4(0xFF000000); + if (addr.is_class_b()) + return address_v4(0xFFFF0000); + if (addr.is_class_c()) + return address_v4(0xFFFFFF00); + return address_v4(0xFFFFFFFF); + } + +private: + // The underlying IPv4 address. + asio::detail::in4_addr_type addr_; +}; + +/// Output an address as a string. +/** + * Used to output a human-readable string for a specified address. + * + * @param os The output stream to which the string will be written. + * + * @param addr The address to be written. + * + * @return The output stream. + * + * @relates asio::ip::address_v4 + */ +template +std::basic_ostream& operator<<( + std::basic_ostream& os, const address_v4& addr) +{ + asio::error_code ec; + std::string s = addr.to_string(ec); + if (ec) + os.setstate(std::ios_base::failbit); + else + for (std::string::iterator i = s.begin(); i != s.end(); ++i) + os << os.widen(*i); + return os; +} + +} // namespace ip +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IP_ADDRESS_V4_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/ip/address_v6.hpp b/encryption/libtorrent/include/libtorrent/asio/ip/address_v6.hpp new file mode 100644 index 000000000..f732955fa --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/ip/address_v6.hpp @@ -0,0 +1,401 @@ +// +// address_v6.hpp +// ~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IP_ADDRESS_V6_HPP +#define ASIO_IP_ADDRESS_V6_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include +#include +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/error.hpp" +#include "asio/detail/socket_ops.hpp" +#include "asio/detail/socket_types.hpp" +#include "asio/detail/throw_error.hpp" +#include "asio/ip/address_v4.hpp" + +namespace asio { +namespace ip { + +/// Implements IP version 6 style addresses. +/** + * The asio::ip::address_v6 class provides the ability to use and + * manipulate IP version 6 addresses. + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + */ +class address_v6 +{ +public: + /// The type used to represent an address as an array of bytes. + typedef boost::array bytes_type; + + /// Default constructor. + address_v6() + : scope_id_(0) + { + asio::detail::in6_addr_type tmp_addr = IN6ADDR_ANY_INIT; + addr_ = tmp_addr; + } + + /// Construct an address from raw bytes and scope ID. + explicit address_v6(const bytes_type& bytes, unsigned long scope_id = 0) + : scope_id_(scope_id) + { + using namespace std; // For memcpy. + memcpy(addr_.s6_addr, bytes.elems, 16); + } + + /// Copy constructor. + address_v6(const address_v6& other) + : addr_(other.addr_), + scope_id_(other.scope_id_) + { + } + + /// Assign from another address. + address_v6& operator=(const address_v6& other) + { + addr_ = other.addr_; + scope_id_ = other.scope_id_; + return *this; + } + + /// The scope ID of the address. + /** + * Returns the scope ID associated with the IPv6 address. + */ + unsigned long scope_id() const + { + return scope_id_; + } + + /// The scope ID of the address. + /** + * Modifies the scope ID associated with the IPv6 address. + */ + void scope_id(unsigned long id) + { + scope_id_ = id; + } + + /// Get the address in bytes. + bytes_type to_bytes() const + { + using namespace std; // For memcpy. + bytes_type bytes; + memcpy(bytes.elems, addr_.s6_addr, 16); + return bytes; + } + + /// Get the address as a string. + std::string to_string() const + { + asio::error_code ec; + std::string addr = to_string(ec); + asio::detail::throw_error(ec); + return addr; + } + + /// Get the address as a string. + std::string to_string(asio::error_code& ec) const + { + char addr_str[asio::detail::max_addr_v6_str_len]; + const char* addr = + asio::detail::socket_ops::inet_ntop(AF_INET6, &addr_, addr_str, + asio::detail::max_addr_v6_str_len, scope_id_, ec); + if (addr == 0) + return std::string(); + return addr; + } + + /// Create an address from an IP address string. + static address_v6 from_string(const char* str) + { + asio::error_code ec; + address_v6 addr = from_string(str, ec); + asio::detail::throw_error(ec); + return addr; + } + + /// Create an address from an IP address string. + static address_v6 from_string(const char* str, asio::error_code& ec) + { + address_v6 tmp; + if (asio::detail::socket_ops::inet_pton( + AF_INET6, str, &tmp.addr_, &tmp.scope_id_, ec) <= 0) + return address_v6(); + return tmp; + } + + /// Create an address from an IP address string. + static address_v6 from_string(const std::string& str) + { + return from_string(str.c_str()); + } + + /// Create an address from an IP address string. + static address_v6 from_string(const std::string& str, + asio::error_code& ec) + { + return from_string(str.c_str(), ec); + } + + /// Converts an IPv4-mapped or IPv4-compatible address to an IPv4 address. + address_v4 to_v4() const + { + if (!is_v4_mapped() && !is_v4_compatible()) + throw std::bad_cast(); + address_v4::bytes_type v4_bytes = { { addr_.s6_addr[12], + addr_.s6_addr[13], addr_.s6_addr[14], addr_.s6_addr[15] } }; + return address_v4(v4_bytes); + } + + /// Determine whether the address is a loopback address. + bool is_loopback() const + { +#if defined(__BORLANDC__) + return ((addr_.s6_addr[0] == 0) && (addr_.s6_addr[1] == 0) + && (addr_.s6_addr[2] == 0) && (addr_.s6_addr[3] == 0) + && (addr_.s6_addr[4] == 0) && (addr_.s6_addr[5] == 0) + && (addr_.s6_addr[6] == 0) && (addr_.s6_addr[7] == 0) + && (addr_.s6_addr[8] == 0) && (addr_.s6_addr[9] == 0) + && (addr_.s6_addr[10] == 0) && (addr_.s6_addr[11] == 0) + && (addr_.s6_addr[12] == 0) && (addr_.s6_addr[13] == 0) + && (addr_.s6_addr[14] == 0) && (addr_.s6_addr[15] == 1)); +#else + using namespace asio::detail; + return IN6_IS_ADDR_LOOPBACK(&addr_) != 0; +#endif + } + + /// Determine whether the address is unspecified. + bool is_unspecified() const + { +#if defined(__BORLANDC__) + return ((addr_.s6_addr[0] == 0) && (addr_.s6_addr[1] == 0) + && (addr_.s6_addr[2] == 0) && (addr_.s6_addr[3] == 0) + && (addr_.s6_addr[4] == 0) && (addr_.s6_addr[5] == 0) + && (addr_.s6_addr[6] == 0) && (addr_.s6_addr[7] == 0) + && (addr_.s6_addr[8] == 0) && (addr_.s6_addr[9] == 0) + && (addr_.s6_addr[10] == 0) && (addr_.s6_addr[11] == 0) + && (addr_.s6_addr[12] == 0) && (addr_.s6_addr[13] == 0) + && (addr_.s6_addr[14] == 0) && (addr_.s6_addr[15] == 0)); +#else + using namespace asio::detail; + return IN6_IS_ADDR_UNSPECIFIED(&addr_) != 0; +#endif + } + + /// Determine whether the address is link local. + bool is_link_local() const + { + using namespace asio::detail; + return IN6_IS_ADDR_LINKLOCAL(&addr_) != 0; + } + + /// Determine whether the address is site local. + bool is_site_local() const + { + using namespace asio::detail; + return IN6_IS_ADDR_SITELOCAL(&addr_) != 0; + } + + /// Determine whether the address is a mapped IPv4 address. + bool is_v4_mapped() const + { + using namespace asio::detail; + return IN6_IS_ADDR_V4MAPPED(&addr_) != 0; + } + + /// Determine whether the address is an IPv4-compatible address. + bool is_v4_compatible() const + { + using namespace asio::detail; + return IN6_IS_ADDR_V4COMPAT(&addr_) != 0; + } + + /// Determine whether the address is a multicast address. + bool is_multicast() const + { + using namespace asio::detail; + return IN6_IS_ADDR_MULTICAST(&addr_) != 0; + } + + /// Determine whether the address is a global multicast address. + bool is_multicast_global() const + { + using namespace asio::detail; + return IN6_IS_ADDR_MC_GLOBAL(&addr_) != 0; + } + + /// Determine whether the address is a link-local multicast address. + bool is_multicast_link_local() const + { + using namespace asio::detail; + return IN6_IS_ADDR_MC_LINKLOCAL(&addr_) != 0; + } + + /// Determine whether the address is a node-local multicast address. + bool is_multicast_node_local() const + { + using namespace asio::detail; + return IN6_IS_ADDR_MC_NODELOCAL(&addr_) != 0; + } + + /// Determine whether the address is a org-local multicast address. + bool is_multicast_org_local() const + { + using namespace asio::detail; + return IN6_IS_ADDR_MC_ORGLOCAL(&addr_) != 0; + } + + /// Determine whether the address is a site-local multicast address. + bool is_multicast_site_local() const + { + using namespace asio::detail; + return IN6_IS_ADDR_MC_SITELOCAL(&addr_) != 0; + } + + /// Compare two addresses for equality. + friend bool operator==(const address_v6& a1, const address_v6& a2) + { + using namespace std; // For memcmp. + return memcmp(&a1.addr_, &a2.addr_, + sizeof(asio::detail::in6_addr_type)) == 0 + && a1.scope_id_ == a2.scope_id_; + } + + /// Compare two addresses for inequality. + friend bool operator!=(const address_v6& a1, const address_v6& a2) + { + using namespace std; // For memcmp. + return memcmp(&a1.addr_, &a2.addr_, + sizeof(asio::detail::in6_addr_type)) != 0 + || a1.scope_id_ != a2.scope_id_; + } + + /// Compare addresses for ordering. + friend bool operator<(const address_v6& a1, const address_v6& a2) + { + using namespace std; // For memcmp. + int memcmp_result = memcmp(&a1.addr_, &a2.addr_, + sizeof(asio::detail::in6_addr_type)) < 0; + if (memcmp_result < 0) + return true; + if (memcmp_result > 0) + return false; + return a1.scope_id_ < a2.scope_id_; + } + + /// Compare addresses for ordering. + friend bool operator>(const address_v6& a1, const address_v6& a2) + { + return a2 < a1; + } + + /// Compare addresses for ordering. + friend bool operator<=(const address_v6& a1, const address_v6& a2) + { + return !(a2 < a1); + } + + /// Compare addresses for ordering. + friend bool operator>=(const address_v6& a1, const address_v6& a2) + { + return !(a1 < a2); + } + + /// Obtain an address object that represents any address. + static address_v6 any() + { + return address_v6(); + } + + /// Obtain an address object that represents the loopback address. + static address_v6 loopback() + { + address_v6 tmp; + asio::detail::in6_addr_type tmp_addr = IN6ADDR_LOOPBACK_INIT; + tmp.addr_ = tmp_addr; + return tmp; + } + + /// Create an IPv4-mapped IPv6 address. + static address_v6 v4_mapped(const address_v4& addr) + { + address_v4::bytes_type v4_bytes = addr.to_bytes(); + bytes_type v6_bytes = { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, + v4_bytes[0], v4_bytes[1], v4_bytes[2], v4_bytes[3] } }; + return address_v6(v6_bytes); + } + + /// Create an IPv4-compatible IPv6 address. + static address_v6 v4_compatible(const address_v4& addr) + { + address_v4::bytes_type v4_bytes = addr.to_bytes(); + bytes_type v6_bytes = { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + v4_bytes[0], v4_bytes[1], v4_bytes[2], v4_bytes[3] } }; + return address_v6(v6_bytes); + } + +private: + // The underlying IPv6 address. + asio::detail::in6_addr_type addr_; + + // The scope ID associated with the address. + unsigned long scope_id_; +}; + +/// Output an address as a string. +/** + * Used to output a human-readable string for a specified address. + * + * @param os The output stream to which the string will be written. + * + * @param addr The address to be written. + * + * @return The output stream. + * + * @relates asio::ip::address_v6 + */ +template +std::basic_ostream& operator<<( + std::basic_ostream& os, const address_v6& addr) +{ + asio::error_code ec; + std::string s = addr.to_string(ec); + if (ec) + os.setstate(std::ios_base::failbit); + else + for (std::string::iterator i = s.begin(); i != s.end(); ++i) + os << os.widen(*i); + return os; +} + +} // namespace ip +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IP_ADDRESS_V6_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/ip/basic_endpoint.hpp b/encryption/libtorrent/include/libtorrent/asio/ip/basic_endpoint.hpp new file mode 100644 index 000000000..3ca91dc03 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/ip/basic_endpoint.hpp @@ -0,0 +1,368 @@ +// +// basic_endpoint.hpp +// ~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IP_BASIC_ENDPOINT_HPP +#define ASIO_IP_BASIC_ENDPOINT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include +#if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564)) +# include +#endif // BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564)) +#include "asio/detail/pop_options.hpp" + +#include "asio/error.hpp" +#include "asio/ip/address.hpp" +#include "asio/detail/socket_ops.hpp" +#include "asio/detail/socket_types.hpp" + +namespace asio { +namespace ip { + +/// Describes an endpoint for a version-independent IP socket. +/** + * The asio::ip::basic_endpoint class template describes an endpoint that + * may be associated with a particular socket. + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + * + * @par Concepts: + * Endpoint. + */ +template +class basic_endpoint +{ +public: + /// The protocol type associated with the endpoint. + typedef InternetProtocol protocol_type; + + /// The type of the endpoint structure. This type is dependent on the + /// underlying implementation of the socket layer. +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined data_type; +#else + typedef asio::detail::socket_addr_type data_type; +#endif + + /// The type for the size of the endpoint structure. This type is dependent on + /// the underlying implementation of the socket layer. +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined size_type; +#else + typedef asio::detail::socket_addr_len_type size_type; +#endif + + /// Default constructor. + basic_endpoint() + : data_() + { + asio::detail::sockaddr_in4_type& data + = reinterpret_cast(data_); + data.sin_family = AF_INET; + data.sin_port = 0; + data.sin_addr.s_addr = INADDR_ANY; + } + + /// Construct an endpoint using a port number, specified in the host's byte + /// order. The IP address will be the any address (i.e. INADDR_ANY or + /// in6addr_any). This constructor would typically be used for accepting new + /// connections. + /** + * @par Examples + * To initialise an IPv4 TCP endpoint for port 1234, use: + * @code + * asio::ip::tcp::endpoint ep(asio::ip::tcp::v4(), 1234); + * @endcode + * + * To specify an IPv6 UDP endpoint for port 9876, use: + * @code + * asio::ip::udp::endpoint ep(asio::ip::udp::v6(), 9876); + * @endcode + */ + basic_endpoint(const InternetProtocol& protocol, unsigned short port_num) + : data_() + { + using namespace std; // For memcpy. + if (protocol.family() == PF_INET) + { + asio::detail::sockaddr_in4_type& data + = reinterpret_cast(data_); + data.sin_family = AF_INET; + data.sin_port = + asio::detail::socket_ops::host_to_network_short(port_num); + data.sin_addr.s_addr = INADDR_ANY; + } + else + { + asio::detail::sockaddr_in6_type& data + = reinterpret_cast(data_); + data.sin6_family = AF_INET6; + data.sin6_port = + asio::detail::socket_ops::host_to_network_short(port_num); + data.sin6_flowinfo = 0; + asio::detail::in6_addr_type tmp_addr = IN6ADDR_ANY_INIT; + data.sin6_addr = tmp_addr; + data.sin6_scope_id = 0; + } + } + + /// Construct an endpoint using a port number and an IP address. This + /// constructor may be used for accepting connections on a specific interface + /// or for making a connection to a remote endpoint. + basic_endpoint(const asio::ip::address& addr, unsigned short port_num) + : data_() + { + using namespace std; // For memcpy. + if (addr.is_v4()) + { + asio::detail::sockaddr_in4_type& data + = reinterpret_cast(data_); + data.sin_family = AF_INET; + data.sin_port = + asio::detail::socket_ops::host_to_network_short(port_num); + data.sin_addr.s_addr = + asio::detail::socket_ops::host_to_network_long( + addr.to_v4().to_ulong()); + } + else + { + asio::detail::sockaddr_in6_type& data + = reinterpret_cast(data_); + data.sin6_family = AF_INET6; + data.sin6_port = + asio::detail::socket_ops::host_to_network_short(port_num); + data.sin6_flowinfo = 0; + asio::ip::address_v6 v6_addr = addr.to_v6(); + asio::ip::address_v6::bytes_type bytes = v6_addr.to_bytes(); + memcpy(data.sin6_addr.s6_addr, bytes.elems, 16); + data.sin6_scope_id = v6_addr.scope_id(); + } + } + + /// Copy constructor. + basic_endpoint(const basic_endpoint& other) + : data_(other.data_) + { + } + + /// Assign from another endpoint. + basic_endpoint& operator=(const basic_endpoint& other) + { + data_ = other.data_; + return *this; + } + + /// The protocol associated with the endpoint. + protocol_type protocol() const + { + if (is_v4()) + return InternetProtocol::v4(); + return InternetProtocol::v6(); + } + + /// Get the underlying endpoint in the native type. + data_type* data() + { + return reinterpret_cast(&data_); + } + + /// Get the underlying endpoint in the native type. + const data_type* data() const + { + return reinterpret_cast(&data_); + } + + /// Get the underlying size of the endpoint in the native type. + size_type size() const + { + if (is_v4()) + return sizeof(asio::detail::sockaddr_in4_type); + else + return sizeof(asio::detail::sockaddr_in6_type); + } + + /// Set the underlying size of the endpoint in the native type. + void resize(size_type size) + { + if (size > size_type(sizeof(data_))) + { + asio::system_error e(asio::error::invalid_argument); + boost::throw_exception(e); + } + } + + /// Get the capacity of the endpoint in the native type. + size_type capacity() const + { + return sizeof(data_); + } + + /// Get the port associated with the endpoint. The port number is always in + /// the host's byte order. + unsigned short port() const + { + if (is_v4()) + { + return asio::detail::socket_ops::network_to_host_short( + reinterpret_cast( + data_).sin_port); + } + else + { + return asio::detail::socket_ops::network_to_host_short( + reinterpret_cast( + data_).sin6_port); + } + } + + /// Set the port associated with the endpoint. The port number is always in + /// the host's byte order. + void port(unsigned short port_num) + { + if (is_v4()) + { + reinterpret_cast(data_).sin_port + = asio::detail::socket_ops::host_to_network_short(port_num); + } + else + { + reinterpret_cast(data_).sin6_port + = asio::detail::socket_ops::host_to_network_short(port_num); + } + } + + /// Get the IP address associated with the endpoint. + asio::ip::address address() const + { + using namespace std; // For memcpy. + if (is_v4()) + { + const asio::detail::sockaddr_in4_type& data + = reinterpret_cast( + data_); + return asio::ip::address_v4( + asio::detail::socket_ops::network_to_host_long( + data.sin_addr.s_addr)); + } + else + { + const asio::detail::sockaddr_in6_type& data + = reinterpret_cast( + data_); + asio::ip::address_v6::bytes_type bytes; + memcpy(bytes.elems, data.sin6_addr.s6_addr, 16); + return asio::ip::address_v6(bytes, data.sin6_scope_id); + } + } + + /// Set the IP address associated with the endpoint. + void address(const asio::ip::address& addr) + { + basic_endpoint tmp_endpoint(addr, port()); + data_ = tmp_endpoint.data_; + } + + /// Compare two endpoints for equality. + friend bool operator==(const basic_endpoint& e1, + const basic_endpoint& e2) + { + return e1.address() == e2.address() && e1.port() == e2.port(); + } + + /// Compare two endpoints for inequality. + friend bool operator!=(const basic_endpoint& e1, + const basic_endpoint& e2) + { + return e1.address() != e2.address() || e1.port() != e2.port(); + } + + /// Compare endpoints for ordering. + friend bool operator<(const basic_endpoint& e1, + const basic_endpoint& e2) + { + if (e1.address() < e2.address()) + return true; + if (e1.address() != e2.address()) + return false; + return e1.port() < e2.port(); + } + +private: + // Helper function to determine whether the endpoint is IPv4. + bool is_v4() const + { +#if defined(_AIX) + return data_.__ss_family == AF_INET; +#else + return data_.ss_family == AF_INET; +#endif + } + + // The underlying IP socket address. + asio::detail::sockaddr_storage_type data_; +}; + +/// Output an endpoint as a string. +/** + * Used to output a human-readable string for a specified endpoint. + * + * @param os The output stream to which the string will be written. + * + * @param endpoint The endpoint to be written. + * + * @return The output stream. + * + * @relates asio::ip::basic_endpoint + */ +#if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564)) +template +std::ostream& operator<<(std::ostream& os, + const basic_endpoint& endpoint) +{ + const address& addr = endpoint.address(); + if (addr.is_v4()) + os << addr.to_string(); + else + os << '[' << addr.to_string() << ']'; + os << ':' << endpoint.port(); + return os; +} +#else // BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564)) +template +std::basic_ostream& operator<<( + std::basic_ostream& os, + const basic_endpoint& endpoint) +{ + const address& addr = endpoint.address(); + if (addr.is_v4()) + os << addr.to_string(); + else + os << '[' << addr.to_string() << ']'; + os << ':' << endpoint.port(); + return os; +} +#endif // BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564)) + +} // namespace ip +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IP_BASIC_ENDPOINT_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/ip/basic_resolver.hpp b/encryption/libtorrent/include/libtorrent/asio/ip/basic_resolver.hpp new file mode 100644 index 000000000..8c9d25486 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/ip/basic_resolver.hpp @@ -0,0 +1,245 @@ +// +// basic_resolver.hpp +// ~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IP_BASIC_RESOLVER_HPP +#define ASIO_IP_BASIC_RESOLVER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/basic_io_object.hpp" +#include "asio/error.hpp" +#include "asio/ip/resolver_service.hpp" +#include "asio/detail/throw_error.hpp" + +namespace asio { +namespace ip { + +/// Provides endpoint resolution functionality. +/** + * The basic_resolver class template provides the ability to resolve a query + * to a list of endpoints. + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + */ +template > +class basic_resolver + : public basic_io_object +{ +public: + /// The protocol type. + typedef InternetProtocol protocol_type; + + /// The endpoint type. + typedef typename InternetProtocol::endpoint endpoint_type; + + /// The query type. + typedef typename InternetProtocol::resolver_query query; + + /// The iterator type. + typedef typename InternetProtocol::resolver_iterator iterator; + + /// Constructor. + /** + * This constructor creates a basic_resolver. + * + * @param io_service The io_service object that the resolver will use to + * dispatch handlers for any asynchronous operations performed on the timer. + */ + explicit basic_resolver(asio::io_service& io_service) + : basic_io_object(io_service) + { + } + + /// Cancel any asynchronous operations that are waiting on the resolver. + /** + * This function forces the completion of any pending asynchronous + * operations on the host resolver. The handler for each cancelled operation + * will be invoked with the asio::error::operation_aborted error code. + */ + void cancel() + { + return this->service.cancel(this->implementation); + } + + /// Resolve a query to a list of entries. + /** + * This function is used to resolve a query into a list of endpoint entries. + * + * @param q A query object that determines what endpoints will be returned. + * + * @returns A forward-only iterator that can be used to traverse the list + * of endpoint entries. + * + * @throws asio::system_error Thrown on failure. + * + * @note A default constructed iterator represents the end of the list. + * + * A successful call to this function is guaranteed to return at least one + * entry. + */ + iterator resolve(const query& q) + { + asio::error_code ec; + iterator i = this->service.resolve(this->implementation, q, ec); + asio::detail::throw_error(ec); + return i; + } + + /// Resolve a query to a list of entries. + /** + * This function is used to resolve a query into a list of endpoint entries. + * + * @param q A query object that determines what endpoints will be returned. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns A forward-only iterator that can be used to traverse the list + * of endpoint entries. Returns a default constructed iterator if an error + * occurs. + * + * @note A default constructed iterator represents the end of the list. + * + * A successful call to this function is guaranteed to return at least one + * entry. + */ + iterator resolve(const query& q, asio::error_code& ec) + { + return this->service.resolve(this->implementation, q, ec); + } + + /// Asynchronously resolve a query to a list of entries. + /** + * This function is used to asynchronously resolve a query into a list of + * endpoint entries. + * + * @param q A query object that determines what endpoints will be returned. + * + * @param handler The handler to be called when the resolve operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * resolver::iterator iterator // Forward-only iterator that can + * // be used to traverse the list + * // of endpoint entries. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_service::post(). + * + * @note A default constructed iterator represents the end of the list. + * + * A successful resolve operation is guaranteed to pass at least one entry to + * the handler. + */ + template + void async_resolve(const query& q, ResolveHandler handler) + { + return this->service.async_resolve(this->implementation, q, handler); + } + + /// Resolve an endpoint to a list of entries. + /** + * This function is used to resolve an endpoint into a list of endpoint + * entries. + * + * @param e An endpoint object that determines what endpoints will be + * returned. + * + * @returns A forward-only iterator that can be used to traverse the list + * of endpoint entries. + * + * @throws asio::system_error Thrown on failure. + * + * @note A default constructed iterator represents the end of the list. + * + * A successful call to this function is guaranteed to return at least one + * entry. + */ + iterator resolve(const endpoint_type& e) + { + asio::error_code ec; + iterator i = this->service.resolve(this->implementation, e, ec); + asio::detail::throw_error(ec); + return i; + } + + /// Resolve an endpoint to a list of entries. + /** + * This function is used to resolve an endpoint into a list of endpoint + * entries. + * + * @param e An endpoint object that determines what endpoints will be + * returned. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns A forward-only iterator that can be used to traverse the list + * of endpoint entries. Returns a default constructed iterator if an error + * occurs. + * + * @note A default constructed iterator represents the end of the list. + * + * A successful call to this function is guaranteed to return at least one + * entry. + */ + iterator resolve(const endpoint_type& e, asio::error_code& ec) + { + return this->service.resolve(this->implementation, e, ec); + } + + /// Asynchronously resolve an endpoint to a list of entries. + /** + * This function is used to asynchronously resolve an endpoint into a list of + * endpoint entries. + * + * @param e An endpoint object that determines what endpoints will be + * returned. + * + * @param handler The handler to be called when the resolve operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * resolver::iterator iterator // Forward-only iterator that can + * // be used to traverse the list + * // of endpoint entries. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * asio::io_service::post(). + * + * @note A default constructed iterator represents the end of the list. + * + * A successful resolve operation is guaranteed to pass at least one entry to + * the handler. + */ + template + void async_resolve(const endpoint_type& e, ResolveHandler handler) + { + return this->service.async_resolve(this->implementation, e, handler); + } +}; + +} // namespace ip +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IP_BASIC_RESOLVER_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/ip/basic_resolver_entry.hpp b/encryption/libtorrent/include/libtorrent/asio/ip/basic_resolver_entry.hpp new file mode 100644 index 000000000..03184d8f5 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/ip/basic_resolver_entry.hpp @@ -0,0 +1,95 @@ +// +// basic_resolver_entry.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IP_BASIC_RESOLVER_ENTRY_HPP +#define ASIO_IP_BASIC_RESOLVER_ENTRY_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include "asio/detail/pop_options.hpp" + +namespace asio { +namespace ip { + +/// An entry produced by a resolver. +/** + * The asio::ip::basic_resolver_entry class template describes an entry + * as returned by a resolver. + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + */ +template +class basic_resolver_entry +{ +public: + /// The protocol type associated with the endpoint entry. + typedef InternetProtocol protocol_type; + + /// The endpoint type associated with the endpoint entry. + typedef typename InternetProtocol::endpoint endpoint_type; + + /// Default constructor. + basic_resolver_entry() + { + } + + /// Construct with specified endpoint, host name and service name. + basic_resolver_entry(const endpoint_type& endpoint, + const std::string& host_name, const std::string& service_name) + : endpoint_(endpoint), + host_name_(host_name), + service_name_(service_name) + { + } + + /// Get the endpoint associated with the entry. + endpoint_type endpoint() const + { + return endpoint_; + } + + /// Convert to the endpoint associated with the entry. + operator endpoint_type() const + { + return endpoint_; + } + + /// Get the host name associated with the entry. + std::string host_name() const + { + return host_name_; + } + + /// Get the service name associated with the entry. + std::string service_name() const + { + return service_name_; + } + +private: + endpoint_type endpoint_; + std::string host_name_; + std::string service_name_; +}; + +} // namespace ip +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IP_BASIC_RESOLVER_ENTRY_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/ip/basic_resolver_iterator.hpp b/encryption/libtorrent/include/libtorrent/asio/ip/basic_resolver_iterator.hpp new file mode 100644 index 000000000..9edec2b0a --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/ip/basic_resolver_iterator.hpp @@ -0,0 +1,154 @@ +// +// basic_resolver_iterator.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IP_BASIC_RESOLVER_ITERATOR_HPP +#define ASIO_IP_BASIC_RESOLVER_ITERATOR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/detail/socket_ops.hpp" +#include "asio/detail/socket_types.hpp" +#include "asio/ip/basic_resolver_entry.hpp" + +namespace asio { +namespace ip { + +/// An iterator over the entries produced by a resolver. +/** + * The asio::ip::basic_resolver_iterator class template is used to define + * iterators over the results returned by a resolver. + * + * The iterator's value_type, obtained when the iterator is dereferenced, is: + * @code const basic_resolver_entry @endcode + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + */ +template +class basic_resolver_iterator + : public boost::iterator_facade< + basic_resolver_iterator, + const basic_resolver_entry, + boost::forward_traversal_tag> +{ +public: + /// Default constructor creates an end iterator. + basic_resolver_iterator() + { + } + + /// Create an iterator from an addrinfo list returned by getaddrinfo. + static basic_resolver_iterator create( + asio::detail::addrinfo_type* address_info, + const std::string& host_name, const std::string& service_name) + { + basic_resolver_iterator iter; + if (!address_info) + return iter; + + std::string actual_host_name = host_name; + if (address_info->ai_canonname) + actual_host_name = address_info->ai_canonname; + + iter.values_.reset(new values_type); + + while (address_info) + { + if (address_info->ai_family == PF_INET + || address_info->ai_family == PF_INET6) + { + using namespace std; // For memcpy. + typename InternetProtocol::endpoint endpoint; + endpoint.resize( + static_cast( + address_info->ai_addrlen)); + memcpy(endpoint.data(), address_info->ai_addr, + address_info->ai_addrlen); + iter.values_->push_back( + basic_resolver_entry(endpoint, + actual_host_name, service_name)); + } + address_info = address_info->ai_next; + } + + if (iter.values_->size()) + iter.iter_ = iter.values_->begin(); + else + iter.values_.reset(); + + return iter; + } + + /// Create an iterator from an endpoint, host name and service name. + static basic_resolver_iterator create( + const typename InternetProtocol::endpoint& endpoint, + const std::string& host_name, const std::string& service_name) + { + basic_resolver_iterator iter; + iter.values_.reset(new values_type); + iter.values_->push_back( + basic_resolver_entry( + endpoint, host_name, service_name)); + iter.iter_ = iter.values_->begin(); + return iter; + } + +private: + friend class boost::iterator_core_access; + + void increment() + { + if (++iter_ == values_->end()) + { + // Reset state to match a default constructed end iterator. + values_.reset(); + typedef typename values_type::const_iterator values_iterator_type; + iter_ = values_iterator_type(); + } + } + + bool equal(const basic_resolver_iterator& other) const + { + if (!values_ && !other.values_) + return true; + if (values_ != other.values_) + return false; + return iter_ == other.iter_; + } + + const basic_resolver_entry& dereference() const + { + return *iter_; + } + + typedef std::vector > values_type; + boost::shared_ptr values_; + typename values_type::const_iterator iter_; +}; + +} // namespace ip +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IP_BASIC_RESOLVER_ITERATOR_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/ip/basic_resolver_query.hpp b/encryption/libtorrent/include/libtorrent/asio/ip/basic_resolver_query.hpp new file mode 100644 index 000000000..0fdd003fe --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/ip/basic_resolver_query.hpp @@ -0,0 +1,149 @@ +// +// basic_resolver_query.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IP_BASIC_RESOLVER_QUERY_HPP +#define ASIO_IP_BASIC_RESOLVER_QUERY_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/detail/socket_ops.hpp" +#include "asio/ip/resolver_query_base.hpp" + +namespace asio { +namespace ip { + +/// An query to be passed to a resolver. +/** + * The asio::ip::basic_resolver_query class template describes a query + * that can be passed to a resolver. + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + */ +template +class basic_resolver_query + : public resolver_query_base +{ +public: + /// The protocol type associated with the endpoint query. + typedef InternetProtocol protocol_type; + + /// Construct with specified service name for any protocol. + basic_resolver_query(const std::string& service_name, + int flags = passive | address_configured) + : hints_(), + host_name_(), + service_name_(service_name) + { + typename InternetProtocol::endpoint endpoint; + hints_.ai_flags = flags; + hints_.ai_family = PF_UNSPEC; + hints_.ai_socktype = endpoint.protocol().type(); + hints_.ai_protocol = endpoint.protocol().protocol(); + hints_.ai_addrlen = 0; + hints_.ai_canonname = 0; + hints_.ai_addr = 0; + hints_.ai_next = 0; + } + + /// Construct with specified service name for a given protocol. + basic_resolver_query(const protocol_type& protocol, + const std::string& service_name, + int flags = passive | address_configured) + : hints_(), + host_name_(), + service_name_(service_name) + { + hints_.ai_flags = flags; + hints_.ai_family = protocol.family(); + hints_.ai_socktype = protocol.type(); + hints_.ai_protocol = protocol.protocol(); + hints_.ai_addrlen = 0; + hints_.ai_canonname = 0; + hints_.ai_addr = 0; + hints_.ai_next = 0; + } + + /// Construct with specified host name and service name for any protocol. + basic_resolver_query(const std::string& host_name, + const std::string& service_name, int flags = address_configured) + : hints_(), + host_name_(host_name), + service_name_(service_name) + { + typename InternetProtocol::endpoint endpoint; + hints_.ai_flags = flags; + hints_.ai_family = PF_UNSPEC; + hints_.ai_socktype = endpoint.protocol().type(); + hints_.ai_protocol = endpoint.protocol().protocol(); + hints_.ai_addrlen = 0; + hints_.ai_canonname = 0; + hints_.ai_addr = 0; + hints_.ai_next = 0; + } + + /// Construct with specified host name and service name for a given protocol. + basic_resolver_query(const protocol_type& protocol, + const std::string& host_name, const std::string& service_name, + int flags = address_configured) + : hints_(), + host_name_(host_name), + service_name_(service_name) + { + hints_.ai_flags = flags; + hints_.ai_family = protocol.family(); + hints_.ai_socktype = protocol.type(); + hints_.ai_protocol = protocol.protocol(); + hints_.ai_addrlen = 0; + hints_.ai_canonname = 0; + hints_.ai_addr = 0; + hints_.ai_next = 0; + } + + /// Get the hints associated with the query. + const asio::detail::addrinfo_type& hints() const + { + return hints_; + } + + /// Get the host name associated with the query. + std::string host_name() const + { + return host_name_; + } + + /// Get the service name associated with the query. + std::string service_name() const + { + return service_name_; + } + +private: + asio::detail::addrinfo_type hints_; + std::string host_name_; + std::string service_name_; +}; + +} // namespace ip +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IP_BASIC_RESOLVER_QUERY_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/ip/detail/socket_option.hpp b/encryption/libtorrent/include/libtorrent/asio/ip/detail/socket_option.hpp new file mode 100644 index 000000000..db7f8edc4 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/ip/detail/socket_option.hpp @@ -0,0 +1,530 @@ +// +// socket_option.hpp +// ~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IP_DETAIL_SOCKET_OPTION_HPP +#define ASIO_IP_DETAIL_SOCKET_OPTION_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/ip/address.hpp" +#include "asio/detail/socket_ops.hpp" +#include "asio/detail/socket_types.hpp" + +namespace asio { +namespace ip { +namespace detail { +namespace socket_option { + +// Helper template for implementing boolean-based options. +template +class boolean +{ +public: + // Default constructor. + boolean() + : value_(0) + { + } + + // Construct with a specific option value. + explicit boolean(bool v) + : value_(v ? 1 : 0) + { + } + + // Set the value of the boolean. + boolean& operator=(bool v) + { + value_ = v ? 1 : 0; + return *this; + } + + // Get the current value of the boolean. + bool value() const + { + return !!value_; + } + + // Convert to bool. + operator bool() const + { + return !!value_; + } + + // Test for false. + bool operator!() const + { + return !value_; + } + + // Get the level of the socket option. + template + int level(const Protocol& protocol) const + { + if (protocol.family() == PF_INET6) + return IPv6_Level; + return IPv4_Level; + } + + // Get the name of the socket option. + template + int name(const Protocol& protocol) const + { + if (protocol.family() == PF_INET6) + return IPv6_Name; + return IPv4_Name; + } + + // Get the address of the boolean data. + template + int* data(const Protocol&) + { + return &value_; + } + + // Get the address of the boolean data. + template + const int* data(const Protocol&) const + { + return &value_; + } + + // Get the size of the boolean data. + template + std::size_t size(const Protocol&) const + { + return sizeof(value_); + } + + // Set the size of the boolean data. + template + void resize(const Protocol&, std::size_t s) + { + if (s != sizeof(value_)) + throw std::length_error("boolean socket option resize"); + } + +private: + int value_; +}; + +// Helper template for implementing unicast hops options. +template +class unicast_hops +{ +public: + // Default constructor. + unicast_hops() + : value_(0) + { + } + + // Construct with a specific option value. + explicit unicast_hops(int v) + : value_(v) + { + } + + // Set the value of the option. + unicast_hops& operator=(int v) + { + value_ = v; + return *this; + } + + // Get the current value of the option. + int value() const + { + return value_; + } + + // Get the level of the socket option. + template + int level(const Protocol& protocol) const + { + if (protocol.family() == PF_INET6) + return IPv6_Level; + return IPv4_Level; + } + + // Get the name of the socket option. + template + int name(const Protocol& protocol) const + { + if (protocol.family() == PF_INET6) + return IPv6_Name; + return IPv4_Name; + } + + // Get the address of the data. + template + int* data(const Protocol&) + { + return &value_; + } + + // Get the address of the data. + template + const int* data(const Protocol&) const + { + return &value_; + } + + // Get the size of the data. + template + std::size_t size(const Protocol&) const + { + return sizeof(value_); + } + + // Set the size of the data. + template + void resize(const Protocol&, std::size_t s) + { + if (s != sizeof(value_)) + throw std::length_error("unicast hops socket option resize"); + } + +private: + int value_; +}; + +// Helper template for implementing multicast hops options. +template +class multicast_hops +{ +public: + // Default constructor. + multicast_hops() + : ipv4_value_(0), + ipv6_value_(0) + { + } + + // Construct with a specific option value. + explicit multicast_hops(int v) + { + if (v < 0 || v > 255) + throw std::out_of_range("multicast hops value out of range"); + ipv4_value_ = static_cast(v); + ipv6_value_ = v; + } + + // Set the value of the option. + multicast_hops& operator=(int v) + { + if (v < 0 || v > 255) + throw std::out_of_range("multicast hops value out of range"); + ipv4_value_ = static_cast(v); + ipv6_value_ = v; + return *this; + } + + // Get the current value of the option. + int value() const + { + return ipv6_value_; + } + + // Get the level of the socket option. + template + int level(const Protocol& protocol) const + { + if (protocol.family() == PF_INET6) + return IPv6_Level; + return IPv4_Level; + } + + // Get the name of the socket option. + template + int name(const Protocol& protocol) const + { + if (protocol.family() == PF_INET6) + return IPv6_Name; + return IPv4_Name; + } + + // Get the address of the data. + template + void* data(const Protocol& protocol) + { + if (protocol.family() == PF_INET6) + return &ipv6_value_; + return &ipv4_value_; + } + + // Get the address of the data. + template + const void* data(const Protocol& protocol) const + { + if (protocol.family() == PF_INET6) + return &ipv6_value_; + return &ipv4_value_; + } + + // Get the size of the data. + template + std::size_t size(const Protocol& protocol) const + { + if (protocol.family() == PF_INET6) + return sizeof(ipv6_value_); + return sizeof(ipv4_value_); + } + + // Set the size of the data. + template + void resize(const Protocol& protocol, std::size_t s) + { + if (protocol.family() == PF_INET6) + { + if (s != sizeof(ipv6_value_)) + throw std::length_error("multicast hops socket option resize"); + if (ipv6_value_ < 0) + ipv4_value_ = 0; + else if (ipv6_value_ > 255) + ipv4_value_ = 255; + else + ipv4_value_ = static_cast(ipv6_value_); + } + else + { + if (s != sizeof(ipv4_value_)) + throw std::length_error("multicast hops socket option resize"); + ipv6_value_ = ipv4_value_; + } + } + +private: + unsigned char ipv4_value_; + int ipv6_value_; +}; + +// Helper template for implementing ip_mreq-based options. +template +class multicast_request +{ +public: + // Default constructor. + multicast_request() + { + ipv4_value_.imr_multiaddr.s_addr = + asio::detail::socket_ops::host_to_network_long( + asio::ip::address_v4::any().to_ulong()); + ipv4_value_.imr_interface.s_addr = + asio::detail::socket_ops::host_to_network_long( + asio::ip::address_v4::any().to_ulong()); + + asio::detail::in6_addr_type tmp_addr = IN6ADDR_ANY_INIT; + ipv6_value_.ipv6mr_multiaddr = tmp_addr; + ipv6_value_.ipv6mr_interface = 0; + } + + // Construct with multicast address only. + explicit multicast_request(const asio::ip::address& multicast_address) + { + if (multicast_address.is_v6()) + { + ipv4_value_.imr_multiaddr.s_addr = + asio::detail::socket_ops::host_to_network_long( + asio::ip::address_v4::any().to_ulong()); + ipv4_value_.imr_interface.s_addr = + asio::detail::socket_ops::host_to_network_long( + asio::ip::address_v4::any().to_ulong()); + + using namespace std; // For memcpy. + asio::ip::address_v6 ipv6_address = multicast_address.to_v6(); + asio::ip::address_v6::bytes_type bytes = ipv6_address.to_bytes(); + memcpy(ipv6_value_.ipv6mr_multiaddr.s6_addr, bytes.elems, 16); + ipv6_value_.ipv6mr_interface = 0; + } + else + { + ipv4_value_.imr_multiaddr.s_addr = + asio::detail::socket_ops::host_to_network_long( + multicast_address.to_v4().to_ulong()); + ipv4_value_.imr_interface.s_addr = + asio::detail::socket_ops::host_to_network_long( + asio::ip::address_v4::any().to_ulong()); + + asio::detail::in6_addr_type tmp_addr = IN6ADDR_ANY_INIT; + ipv6_value_.ipv6mr_multiaddr = tmp_addr; + ipv6_value_.ipv6mr_interface = 0; + } + } + + // Construct with multicast address and IPv4 address specifying an interface. + explicit multicast_request( + const asio::ip::address_v4& multicast_address, + const asio::ip::address_v4& network_interface + = asio::ip::address_v4::any()) + { + ipv4_value_.imr_multiaddr.s_addr = + asio::detail::socket_ops::host_to_network_long( + multicast_address.to_ulong()); + ipv4_value_.imr_interface.s_addr = + asio::detail::socket_ops::host_to_network_long( + network_interface.to_ulong()); + + asio::detail::in6_addr_type tmp_addr = IN6ADDR_ANY_INIT; + ipv6_value_.ipv6mr_multiaddr = tmp_addr; + ipv6_value_.ipv6mr_interface = 0; + } + + // Construct with multicast address and IPv6 network interface index. + explicit multicast_request( + const asio::ip::address_v6& multicast_address, + unsigned long network_interface = 0) + { + ipv4_value_.imr_multiaddr.s_addr = + asio::detail::socket_ops::host_to_network_long( + asio::ip::address_v4::any().to_ulong()); + ipv4_value_.imr_interface.s_addr = + asio::detail::socket_ops::host_to_network_long( + asio::ip::address_v4::any().to_ulong()); + + using namespace std; // For memcpy. + asio::ip::address_v6::bytes_type bytes = + multicast_address.to_bytes(); + memcpy(ipv6_value_.ipv6mr_multiaddr.s6_addr, bytes.elems, 16); + ipv6_value_.ipv6mr_interface = network_interface; + } + + // Get the level of the socket option. + template + int level(const Protocol& protocol) const + { + if (protocol.family() == PF_INET6) + return IPv6_Level; + return IPv4_Level; + } + + // Get the name of the socket option. + template + int name(const Protocol& protocol) const + { + if (protocol.family() == PF_INET6) + return IPv6_Name; + return IPv4_Name; + } + + // Get the address of the option data. + template + const void* data(const Protocol& protocol) const + { + if (protocol.family() == PF_INET6) + return &ipv6_value_; + return &ipv4_value_; + } + + // Get the size of the option data. + template + std::size_t size(const Protocol& protocol) const + { + if (protocol.family() == PF_INET6) + return sizeof(ipv6_value_); + return sizeof(ipv4_value_); + } + +private: + asio::detail::in4_mreq_type ipv4_value_; + asio::detail::in6_mreq_type ipv6_value_; +}; + +// Helper template for implementing options that specify a network interface. +template +class network_interface +{ +public: + // Default constructor. + network_interface() + { + ipv4_value_.s_addr = + asio::detail::socket_ops::host_to_network_long( + asio::ip::address_v4::any().to_ulong()); + ipv6_value_ = 0; + } + + // Construct with IPv4 interface. + explicit network_interface(const asio::ip::address_v4& ipv4_interface) + { + ipv4_value_.s_addr = + asio::detail::socket_ops::host_to_network_long( + ipv4_interface.to_ulong()); + ipv6_value_ = 0; + } + + // Construct with IPv6 interface. + explicit network_interface(unsigned long ipv6_interface) + { + ipv4_value_.s_addr = + asio::detail::socket_ops::host_to_network_long( + asio::ip::address_v4::any().to_ulong()); + ipv6_value_ = ipv6_interface; + } + + // Get the level of the socket option. + template + int level(const Protocol& protocol) const + { + if (protocol.family() == PF_INET6) + return IPv6_Level; + return IPv4_Level; + } + + // Get the name of the socket option. + template + int name(const Protocol& protocol) const + { + if (protocol.family() == PF_INET6) + return IPv6_Name; + return IPv4_Name; + } + + // Get the address of the option data. + template + const void* data(const Protocol& protocol) const + { + if (protocol.family() == PF_INET6) + return &ipv6_value_; + return &ipv4_value_; + } + + // Get the size of the option data. + template + std::size_t size(const Protocol& protocol) const + { + if (protocol.family() == PF_INET6) + return sizeof(ipv6_value_); + return sizeof(ipv4_value_); + } + +private: + asio::detail::in4_addr_type ipv4_value_; + unsigned long ipv6_value_; +}; + +} // namespace socket_option +} // namespace detail +} // namespace ip +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IP_DETAIL_SOCKET_OPTION_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/ip/host_name.hpp b/encryption/libtorrent/include/libtorrent/asio/ip/host_name.hpp new file mode 100644 index 000000000..15adc08cc --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/ip/host_name.hpp @@ -0,0 +1,62 @@ +// +// host_name.hpp +// ~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IP_HOST_NAME_HPP +#define ASIO_IP_HOST_NAME_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/error.hpp" +#include "asio/detail/socket_ops.hpp" +#include "asio/detail/throw_error.hpp" + +namespace asio { +namespace ip { + +/// Get the current host name. +std::string host_name(); + +/// Get the current host name. +std::string host_name(asio::error_code& ec); + +inline std::string host_name() +{ + char name[1024]; + asio::error_code ec; + if (asio::detail::socket_ops::gethostname(name, sizeof(name), ec) != 0) + { + asio::detail::throw_error(ec); + return std::string(); + } + return std::string(name); +} + +inline std::string host_name(asio::error_code& ec) +{ + char name[1024]; + if (asio::detail::socket_ops::gethostname(name, sizeof(name), ec) != 0) + return std::string(); + return std::string(name); +} + +} // namespace ip +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IP_HOST_NAME_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/ip/multicast.hpp b/encryption/libtorrent/include/libtorrent/asio/ip/multicast.hpp new file mode 100644 index 000000000..0d90659ca --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/ip/multicast.hpp @@ -0,0 +1,181 @@ +// +// multicast.hpp +// ~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IP_MULTICAST_HPP +#define ASIO_IP_MULTICAST_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/ip/detail/socket_option.hpp" + +namespace asio { +namespace ip { +namespace multicast { + +/// Socket option to join a multicast group on a specified interface. +/** + * Implements the IPPROTO_IP/IP_ADD_MEMBERSHIP socket option. + * + * @par Examples + * Setting the option to join a multicast group: + * @code + * asio::ip::udp::socket socket(io_service); + * ... + * asio::ip::address multicast_address = + * asio::ip::address::from_string("225.0.0.1"); + * asio::ip::multicast::join_group option(multicast_address); + * socket.set_option(option); + * @endcode + * + * @par Concepts: + * SettableSocketOption. + */ +#if defined(GENERATING_DOCUMENTATION) +typedef implementation_defined join_group; +#else +typedef asio::ip::detail::socket_option::multicast_request< + IPPROTO_IP, IP_ADD_MEMBERSHIP, IPPROTO_IPV6, IPV6_JOIN_GROUP> join_group; +#endif + +/// Socket option to leave a multicast group on a specified interface. +/** + * Implements the IPPROTO_IP/IP_DROP_MEMBERSHIP socket option. + * + * @par Examples + * Setting the option to leave a multicast group: + * @code + * asio::ip::udp::socket socket(io_service); + * ... + * asio::ip::address multicast_address = + * asio::ip::address::from_string("225.0.0.1"); + * asio::ip::multicast::leave_group option(multicast_address); + * socket.set_option(option); + * @endcode + * + * @par Concepts: + * SettableSocketOption. + */ +#if defined(GENERATING_DOCUMENTATION) +typedef implementation_defined leave_group; +#else +typedef asio::ip::detail::socket_option::multicast_request< + IPPROTO_IP, IP_DROP_MEMBERSHIP, IPPROTO_IPV6, IPV6_LEAVE_GROUP> leave_group; +#endif + +/// Socket option for local interface to use for outgoing multicast packets. +/** + * Implements the IPPROTO_IP/IP_MULTICAST_IF socket option. + * + * @par Examples + * Setting the option: + * @code + * asio::ip::udp::socket socket(io_service); + * ... + * asio::ip::address_v4 local_interface = + * asio::ip::address_v4::from_string("1.2.3.4"); + * asio::ip::multicast::outbound_interface option(local_interface); + * socket.set_option(option); + * @endcode + * + * @par Concepts: + * SettableSocketOption. + */ +#if defined(GENERATING_DOCUMENTATION) +typedef implementation_defined outbound_interface; +#else +typedef asio::ip::detail::socket_option::network_interface< + IPPROTO_IP, IP_MULTICAST_IF, IPPROTO_IPV6, IPV6_MULTICAST_IF> + outbound_interface; +#endif + +/// Socket option for time-to-live associated with outgoing multicast packets. +/** + * Implements the IPPROTO_IP/IP_MULTICAST_TTL socket option. + * + * @par Examples + * Setting the option: + * @code + * asio::ip::udp::socket socket(io_service); + * ... + * asio::ip::multicast::hops option(4); + * socket.set_option(option); + * @endcode + * + * @par + * Getting the current option value: + * @code + * asio::ip::udp::socket socket(io_service); + * ... + * asio::ip::multicast::hops option; + * socket.get_option(option); + * int ttl = option.value(); + * @endcode + * + * @par Concepts: + * GettableSocketOption, SettableSocketOption. + */ +#if defined(GENERATING_DOCUMENTATION) +typedef implementation_defined hops; +#else +typedef asio::ip::detail::socket_option::multicast_hops< + IPPROTO_IP, IP_MULTICAST_TTL, IPPROTO_IPV6, IPV6_MULTICAST_HOPS> hops; +#endif + +/// Socket option determining whether outgoing multicast packets will be +/// received on the same socket if it is a member of the multicast group. +/** + * Implements the IPPROTO_IP/IP_MULTICAST_LOOP socket option. + * + * @par Examples + * Setting the option: + * @code + * asio::ip::udp::socket socket(io_service); + * ... + * asio::ip::multicast::enable_loopback option(true); + * socket.set_option(option); + * @endcode + * + * @par + * Getting the current option value: + * @code + * asio::ip::udp::socket socket(io_service); + * ... + * asio::ip::multicast::enable_loopback option; + * socket.get_option(option); + * bool is_set = option.value(); + * @endcode + * + * @par Concepts: + * GettableSocketOption, SettableSocketOption. + */ +#if defined(GENERATING_DOCUMENTATION) +typedef implementation_defined enable_loopback; +#else +typedef asio::ip::detail::socket_option::boolean< + IPPROTO_IP, IP_MULTICAST_LOOP, IPPROTO_IPV6, IPV6_MULTICAST_LOOP> + enable_loopback; +#endif + +} // namespace multicast +} // namespace ip +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IP_MULTICAST_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/ip/resolver_query_base.hpp b/encryption/libtorrent/include/libtorrent/asio/ip/resolver_query_base.hpp new file mode 100644 index 000000000..5b76fb3c2 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/ip/resolver_query_base.hpp @@ -0,0 +1,107 @@ +// +// resolver_query_base.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IP_RESOLVER_QUERY_BASE_HPP +#define ASIO_IP_RESOLVER_QUERY_BASE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/detail/socket_types.hpp" + +namespace asio { +namespace ip { + +/// The resolver_query_base class is used as a base for the +/// basic_resolver_query class templates to provide a common place to define +/// the flag constants. +class resolver_query_base +{ +public: +#if defined(GENERATING_DOCUMENTATION) + /// Determine the canonical name of the host specified in the query. + static const int canonical_name = implementation_defined; + + /// Indicate that returned endpoint is intended for use as a locally bound + /// socket endpoint. + static const int passive = implementation_defined; + + /// Host name should be treated as a numeric string defining an IPv4 or IPv6 + /// address and no name resolution should be attempted. + static const int numeric_host = implementation_defined; + + /// Service name should be treated as a numeric string defining a port number + /// and no name resolution should be attempted. + static const int numeric_service = implementation_defined; + + /// If the query protocol family is specified as IPv6, return IPv4-mapped + /// IPv6 addresses on finding no IPv6 addresses. + static const int v4_mapped = implementation_defined; + + /// If used with v4_mapped, return all matching IPv6 and IPv4 addresses. + static const int all_matching = implementation_defined; + + /// Only return IPv4 addresses if a non-loopback IPv4 address is configured + /// for the system. Only return IPv6 addresses if a non-loopback IPv6 address + /// is configured for the system. + static const int address_configured = implementation_defined; +#else + BOOST_STATIC_CONSTANT(int, canonical_name = AI_CANONNAME); + BOOST_STATIC_CONSTANT(int, passive = AI_PASSIVE); + BOOST_STATIC_CONSTANT(int, numeric_host = AI_NUMERICHOST); +# if defined(AI_NUMERICSERV) + BOOST_STATIC_CONSTANT(int, numeric_service = AI_NUMERICSERV); +# else + BOOST_STATIC_CONSTANT(int, numeric_service = 0); +# endif +# if defined(AI_V4MAPPED) + BOOST_STATIC_CONSTANT(int, v4_mapped = AI_V4MAPPED); +# else + BOOST_STATIC_CONSTANT(int, v4_mapped = 0); +# endif +# if defined(AI_ALL) + BOOST_STATIC_CONSTANT(int, all_matching = AI_ALL); +# else + BOOST_STATIC_CONSTANT(int, all_matching = 0); +# endif +# if defined(AI_ADDRCONFIG) + BOOST_STATIC_CONSTANT(int, address_configured = AI_ADDRCONFIG); +# else + BOOST_STATIC_CONSTANT(int, address_configured = 0); +# endif +#endif + +protected: + /// Protected destructor to prevent deletion through this type. + ~resolver_query_base() + { + } + +#if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564)) +private: + // Workaround to enable the empty base optimisation with Borland C++. + char dummy_; +#endif +}; + +} // namespace ip +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IP_RESOLVER_QUERY_BASE_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/ip/resolver_service.hpp b/encryption/libtorrent/include/libtorrent/asio/ip/resolver_service.hpp new file mode 100644 index 000000000..6d5193ea8 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/ip/resolver_service.hpp @@ -0,0 +1,140 @@ +// +// resolver_service.hpp +// ~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IP_RESOLVER_SERVICE_HPP +#define ASIO_IP_RESOLVER_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/error.hpp" +#include "asio/io_service.hpp" +#include "asio/detail/resolver_service.hpp" +#include "asio/detail/service_base.hpp" + +namespace asio { +namespace ip { + +/// Default service implementation for a resolver. +template +class resolver_service +#if defined(GENERATING_DOCUMENTATION) + : public asio::io_service::service +#else + : public asio::detail::service_base< + resolver_service > +#endif +{ +public: +#if defined(GENERATING_DOCUMENTATION) + /// The unique service identifier. + static asio::io_service::id id; +#endif + + /// The protocol type. + typedef InternetProtocol protocol_type; + + /// The endpoint type. + typedef typename InternetProtocol::endpoint endpoint_type; + + /// The query type. + typedef typename InternetProtocol::resolver_query query_type; + + /// The iterator type. + typedef typename InternetProtocol::resolver_iterator iterator_type; + +private: + // The type of the platform-specific implementation. + typedef asio::detail::resolver_service + service_impl_type; + +public: + /// The type of a resolver implementation. +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined implementation_type; +#else + typedef typename service_impl_type::implementation_type implementation_type; +#endif + + /// Construct a new resolver service for the specified io_service. + explicit resolver_service(asio::io_service& io_service) + : asio::detail::service_base< + resolver_service >(io_service), + service_impl_(asio::use_service(io_service)) + { + } + + /// Destroy all user-defined handler objects owned by the service. + void shutdown_service() + { + } + + /// Construct a new resolver implementation. + void construct(implementation_type& impl) + { + service_impl_.construct(impl); + } + + /// Destroy a resolver implementation. + void destroy(implementation_type& impl) + { + service_impl_.destroy(impl); + } + + /// Cancel pending asynchronous operations. + void cancel(implementation_type& impl) + { + service_impl_.cancel(impl); + } + + /// Resolve a query to a list of entries. + iterator_type resolve(implementation_type& impl, const query_type& query, + asio::error_code& ec) + { + return service_impl_.resolve(impl, query, ec); + } + + /// Asynchronously resolve a query to a list of entries. + template + void async_resolve(implementation_type& impl, const query_type& query, + Handler handler) + { + service_impl_.async_resolve(impl, query, handler); + } + + /// Resolve an endpoint to a list of entries. + iterator_type resolve(implementation_type& impl, + const endpoint_type& endpoint, asio::error_code& ec) + { + return service_impl_.resolve(impl, endpoint, ec); + } + + /// Asynchronously resolve an endpoint to a list of entries. + template + void async_resolve(implementation_type& impl, const endpoint_type& endpoint, + ResolveHandler handler) + { + return service_impl_.async_resolve(impl, endpoint, handler); + } + +private: + // The service that provides the platform-specific implementation. + service_impl_type& service_impl_; +}; + +} // namespace ip +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IP_RESOLVER_SERVICE_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/ip/tcp.hpp b/encryption/libtorrent/include/libtorrent/asio/ip/tcp.hpp new file mode 100644 index 000000000..447210caf --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/ip/tcp.hpp @@ -0,0 +1,158 @@ +// +// tcp.hpp +// ~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IP_TCP_HPP +#define ASIO_IP_TCP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/basic_socket_acceptor.hpp" +#include "asio/basic_socket_iostream.hpp" +#include "asio/basic_stream_socket.hpp" +#include "asio/ip/basic_endpoint.hpp" +#include "asio/ip/basic_resolver.hpp" +#include "asio/ip/basic_resolver_iterator.hpp" +#include "asio/ip/basic_resolver_query.hpp" +#include "asio/detail/socket_option.hpp" +#include "asio/detail/socket_types.hpp" + +namespace asio { +namespace ip { + +/// Encapsulates the flags needed for TCP. +/** + * The asio::ip::tcp class contains flags necessary for TCP sockets. + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Safe. + * + * @par Concepts: + * Protocol, InternetProtocol. + */ +class tcp +{ +public: + /// The type of a TCP endpoint. + typedef basic_endpoint endpoint; + + /// The type of a resolver query. + typedef basic_resolver_query resolver_query; + + /// The type of a resolver iterator. + typedef basic_resolver_iterator resolver_iterator; + + /// Construct to represent the IPv4 TCP protocol. + static tcp v4() + { + return tcp(PF_INET); + } + + /// Construct to represent the IPv4 TCP protocol. + static tcp v6() + { + return tcp(PF_INET6); + } + + /// Obtain an identifier for the type of the protocol. + int type() const + { + return SOCK_STREAM; + } + + /// Obtain an identifier for the protocol. + int protocol() const + { + return IPPROTO_TCP; + } + + /// Obtain an identifier for the protocol family. + int family() const + { + return family_; + } + + /// The TCP socket type. + typedef basic_stream_socket socket; + + /// The TCP acceptor type. + typedef basic_socket_acceptor acceptor; + + /// The TCP resolver type. + typedef basic_resolver resolver; + + /// The TCP iostream type. + typedef basic_socket_iostream iostream; + + /// Socket option for disabling the Nagle algorithm. + /** + * Implements the IPPROTO_TCP/TCP_NODELAY socket option. + * + * @par Examples + * Setting the option: + * @code + * asio::ip::tcp::socket socket(io_service); + * ... + * asio::ip::tcp::no_delay option(true); + * socket.set_option(option); + * @endcode + * + * @par + * Getting the current option value: + * @code + * asio::ip::tcp::socket socket(io_service); + * ... + * asio::ip::tcp::no_delay option; + * socket.get_option(option); + * bool is_set = option.value(); + * @endcode + * + * @par Concepts: + * Socket_Option, Boolean_Socket_Option. + */ +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined no_delay; +#else + typedef asio::detail::socket_option::boolean< + IPPROTO_TCP, TCP_NODELAY> no_delay; +#endif + + /// Compare two protocols for equality. + friend bool operator==(const tcp& p1, const tcp& p2) + { + return p1.family_ == p2.family_; + } + + /// Compare two protocols for inequality. + friend bool operator!=(const tcp& p1, const tcp& p2) + { + return p1.family_ != p2.family_; + } + +private: + // Construct with a specific family. + explicit tcp(int family) + : family_(family) + { + } + + int family_; +}; + +} // namespace ip +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IP_TCP_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/ip/udp.hpp b/encryption/libtorrent/include/libtorrent/asio/ip/udp.hpp new file mode 100644 index 000000000..ed3bb8536 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/ip/udp.hpp @@ -0,0 +1,116 @@ +// +// udp.hpp +// ~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IP_UDP_HPP +#define ASIO_IP_UDP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/basic_datagram_socket.hpp" +#include "asio/ip/basic_endpoint.hpp" +#include "asio/ip/basic_resolver.hpp" +#include "asio/ip/basic_resolver_iterator.hpp" +#include "asio/ip/basic_resolver_query.hpp" +#include "asio/detail/socket_types.hpp" + +namespace asio { +namespace ip { + +/// Encapsulates the flags needed for UDP. +/** + * The asio::ip::udp class contains flags necessary for UDP sockets. + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Safe. + * + * @par Concepts: + * Protocol, InternetProtocol. + */ +class udp +{ +public: + /// The type of a UDP endpoint. + typedef basic_endpoint endpoint; + + /// The type of a resolver query. + typedef basic_resolver_query resolver_query; + + /// The type of a resolver iterator. + typedef basic_resolver_iterator resolver_iterator; + + /// Construct to represent the IPv4 UDP protocol. + static udp v4() + { + return udp(PF_INET); + } + + /// Construct to represent the IPv4 UDP protocol. + static udp v6() + { + return udp(PF_INET6); + } + + /// Obtain an identifier for the type of the protocol. + int type() const + { + return SOCK_DGRAM; + } + + /// Obtain an identifier for the protocol. + int protocol() const + { + return IPPROTO_UDP; + } + + /// Obtain an identifier for the protocol family. + int family() const + { + return family_; + } + + /// The IPv4 UDP socket type. + typedef basic_datagram_socket socket; + + /// The UDP resolver type. + typedef basic_resolver resolver; + + /// Compare two protocols for equality. + friend bool operator==(const udp& p1, const udp& p2) + { + return p1.family_ == p2.family_; + } + + /// Compare two protocols for inequality. + friend bool operator!=(const udp& p1, const udp& p2) + { + return p1.family_ != p2.family_; + } + +private: + // Construct with a specific family. + explicit udp(int family) + : family_(family) + { + } + + int family_; +}; + +} // namespace ip +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IP_UDP_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/is_read_buffered.hpp b/encryption/libtorrent/include/libtorrent/asio/is_read_buffered.hpp new file mode 100644 index 000000000..091ba8c5f --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/is_read_buffered.hpp @@ -0,0 +1,62 @@ +// +// is_read_buffered.hpp +// ~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IS_READ_BUFFERED_HPP +#define ASIO_IS_READ_BUFFERED_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/buffered_read_stream_fwd.hpp" +#include "asio/buffered_stream_fwd.hpp" + +namespace asio { + +namespace detail { + +template +char is_read_buffered_helper(buffered_stream* s); + +template +char is_read_buffered_helper(buffered_read_stream* s); + +struct is_read_buffered_big_type { char data[10]; }; +is_read_buffered_big_type is_read_buffered_helper(...); + +} // namespace detail + +/// The is_read_buffered class is a traits class that may be used to determine +/// whether a stream type supports buffering of read data. +template +class is_read_buffered +{ +public: +#if defined(GENERATING_DOCUMENTATION) + /// The value member is true only if the Stream type supports buffering of + /// read data. + static const bool value; +#else + BOOST_STATIC_CONSTANT(bool, + value = sizeof(detail::is_read_buffered_helper((Stream*)0)) == 1); +#endif +}; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IS_READ_BUFFERED_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/is_write_buffered.hpp b/encryption/libtorrent/include/libtorrent/asio/is_write_buffered.hpp new file mode 100644 index 000000000..946f7468c --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/is_write_buffered.hpp @@ -0,0 +1,62 @@ +// +// is_write_buffered.hpp +// ~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IS_WRITE_BUFFERED_HPP +#define ASIO_IS_WRITE_BUFFERED_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/buffered_stream_fwd.hpp" +#include "asio/buffered_write_stream_fwd.hpp" + +namespace asio { + +namespace detail { + +template +char is_write_buffered_helper(buffered_stream* s); + +template +char is_write_buffered_helper(buffered_write_stream* s); + +struct is_write_buffered_big_type { char data[10]; }; +is_write_buffered_big_type is_write_buffered_helper(...); + +} // namespace detail + +/// The is_write_buffered class is a traits class that may be used to determine +/// whether a stream type supports buffering of written data. +template +class is_write_buffered +{ +public: +#if defined(GENERATING_DOCUMENTATION) + /// The value member is true only if the Stream type supports buffering of + /// written data. + static const bool value; +#else + BOOST_STATIC_CONSTANT(bool, + value = sizeof(detail::is_write_buffered_helper((Stream*)0)) == 1); +#endif +}; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IS_WRITE_BUFFERED_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/placeholders.hpp b/encryption/libtorrent/include/libtorrent/asio/placeholders.hpp new file mode 100644 index 000000000..0c1b6b4a6 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/placeholders.hpp @@ -0,0 +1,107 @@ +// +// placeholders.hpp +// ~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_PLACEHOLDERS_HPP +#define ASIO_PLACEHOLDERS_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include "asio/detail/pop_options.hpp" + +namespace asio { +namespace placeholders { + +#if defined(GENERATING_DOCUMENTATION) + +/// An argument placeholder, for use with @ref boost_bind, that corresponds to +/// the error argument of a handler for any of the asynchronous functions. +unspecified error; + +/// An argument placeholder, for use with @ref boost_bind, that corresponds to +/// the bytes_transferred argument of a handler for asynchronous functions such +/// as asio::basic_stream_socket::async_write_some or +/// asio::async_write. +unspecified bytes_transferred; + +/// An argument placeholder, for use with @ref boost_bind, that corresponds to +/// the iterator argument of a handler for asynchronous functions such as +/// asio::basic_resolver::resolve. +unspecified iterator; + +#elif defined(__BORLANDC__) || defined(__GNUC__) + +inline boost::arg<1> error() +{ + return boost::arg<1>(); +} + +inline boost::arg<2> bytes_transferred() +{ + return boost::arg<2>(); +} + +inline boost::arg<2> iterator() +{ + return boost::arg<2>(); +} + +#else + +namespace detail +{ + template + struct placeholder + { + static boost::arg& get() + { + static boost::arg result; + return result; + } + }; +} + +#if BOOST_WORKAROUND(BOOST_MSVC, < 1400) + +static boost::arg<1>& error + = asio::placeholders::detail::placeholder<1>::get(); +static boost::arg<2>& bytes_transferred + = asio::placeholders::detail::placeholder<2>::get(); +static boost::arg<2>& iterator + = asio::placeholders::detail::placeholder<2>::get(); + +#else + +namespace +{ + boost::arg<1>& error + = asio::placeholders::detail::placeholder<1>::get(); + boost::arg<2>& bytes_transferred + = asio::placeholders::detail::placeholder<2>::get(); + boost::arg<2>& iterator + = asio::placeholders::detail::placeholder<2>::get(); +} // namespace + +#endif + +#endif + +} // namespace placeholders +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_PLACEHOLDERS_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/read.hpp b/encryption/libtorrent/include/libtorrent/asio/read.hpp new file mode 100644 index 000000000..6e9772dee --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/read.hpp @@ -0,0 +1,516 @@ +// +// read.hpp +// ~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_READ_HPP +#define ASIO_READ_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/basic_streambuf.hpp" +#include "asio/error.hpp" + +namespace asio { + +/** + * @defgroup read asio::read + */ +/*@{*/ + +/// Attempt to read a certain amount of data from a stream before returning. +/** + * This function is used to read a certain number of bytes of data from a + * stream. The call will block until one of the following conditions is true: + * + * @li The supplied buffers are full. That is, the bytes transferred is equal to + * the sum of the buffer sizes. + * + * @li An error occurred. + * + * This operation is implemented in terms of one or more calls to the stream's + * read_some function. + * + * @param s The stream from which the data is to be read. The type must support + * the SyncReadStream concept. + * + * @param buffers One or more buffers into which the data will be read. The sum + * of the buffer sizes indicates the maximum number of bytes to read from the + * stream. + * + * @returns The number of bytes transferred. + * + * @throws asio::system_error Thrown on failure. + * + * @par Example + * To read into a single data buffer use the @ref buffer function as follows: + * @code asio::read(s, asio::buffer(data, size)); @endcode + * See the @ref buffer documentation for information on reading into multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + * + * @note This overload is equivalent to calling: + * @code asio::read( + * s, buffers, + * asio::transfer_all()); @endcode + */ +template +std::size_t read(SyncReadStream& s, const MutableBufferSequence& buffers); + +/// Attempt to read a certain amount of data from a stream before returning. +/** + * This function is used to read a certain number of bytes of data from a + * stream. The call will block until one of the following conditions is true: + * + * @li The supplied buffers are full. That is, the bytes transferred is equal to + * the sum of the buffer sizes. + * + * @li The completion_condition function object returns true. + * + * This operation is implemented in terms of one or more calls to the stream's + * read_some function. + * + * @param s The stream from which the data is to be read. The type must support + * the SyncReadStream concept. + * + * @param buffers One or more buffers into which the data will be read. The sum + * of the buffer sizes indicates the maximum number of bytes to read from the + * stream. + * + * @param completion_condition The function object to be called to determine + * whether the read operation is complete. The signature of the function object + * must be: + * @code bool completion_condition( + * const asio::error_code& error, // Result of latest read_some + * // operation. + * + * std::size_t bytes_transferred // Number of bytes transferred + * // so far. + * ); @endcode + * A return value of true indicates that the read operation is complete. False + * indicates that further calls to the stream's read_some function are required. + * + * @returns The number of bytes transferred. + * + * @throws asio::system_error Thrown on failure. + * + * @par Example + * To read into a single data buffer use the @ref buffer function as follows: + * @code asio::read(s, asio::buffer(data, size), + * asio::transfer_at_least(32)); @endcode + * See the @ref buffer documentation for information on reading into multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ +template +std::size_t read(SyncReadStream& s, const MutableBufferSequence& buffers, + CompletionCondition completion_condition); + +/// Attempt to read a certain amount of data from a stream before returning. +/** + * This function is used to read a certain number of bytes of data from a + * stream. The call will block until one of the following conditions is true: + * + * @li The supplied buffers are full. That is, the bytes transferred is equal to + * the sum of the buffer sizes. + * + * @li The completion_condition function object returns true. + * + * This operation is implemented in terms of one or more calls to the stream's + * read_some function. + * + * @param s The stream from which the data is to be read. The type must support + * the SyncReadStream concept. + * + * @param buffers One or more buffers into which the data will be read. The sum + * of the buffer sizes indicates the maximum number of bytes to read from the + * stream. + * + * @param completion_condition The function object to be called to determine + * whether the read operation is complete. The signature of the function object + * must be: + * @code bool completion_condition( + * const asio::error_code& error, // Result of latest read_some + * // operation. + * + * std::size_t bytes_transferred // Number of bytes transferred + * // so far. + * ); @endcode + * A return value of true indicates that the read operation is complete. False + * indicates that further calls to the stream's read_some function are required. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes read. If an error occurs, returns the total + * number of bytes successfully transferred prior to the error. + */ +template +std::size_t read(SyncReadStream& s, const MutableBufferSequence& buffers, + CompletionCondition completion_condition, asio::error_code& ec); + +/// Attempt to read a certain amount of data from a stream before returning. +/** + * This function is used to read a certain number of bytes of data from a + * stream. The call will block until one of the following conditions is true: + * + * @li An error occurred. + * + * This operation is implemented in terms of one or more calls to the stream's + * read_some function. + * + * @param s The stream from which the data is to be read. The type must support + * the SyncReadStream concept. + * + * @param b The basic_streambuf object into which the data will be read. + * + * @returns The number of bytes transferred. + * + * @throws asio::system_error Thrown on failure. + * + * @note This overload is equivalent to calling: + * @code asio::read( + * s, b, + * asio::transfer_all()); @endcode + */ +template +std::size_t read(SyncReadStream& s, basic_streambuf& b); + +/// Attempt to read a certain amount of data from a stream before returning. +/** + * This function is used to read a certain number of bytes of data from a + * stream. The call will block until one of the following conditions is true: + * + * @li The completion_condition function object returns true. + * + * This operation is implemented in terms of one or more calls to the stream's + * read_some function. + * + * @param s The stream from which the data is to be read. The type must support + * the SyncReadStream concept. + * + * @param b The basic_streambuf object into which the data will be read. + * + * @param completion_condition The function object to be called to determine + * whether the read operation is complete. The signature of the function object + * must be: + * @code bool completion_condition( + * const asio::error_code& error, // Result of latest read_some + * // operation. + * + * std::size_t bytes_transferred // Number of bytes transferred + * // so far. + * ); @endcode + * A return value of true indicates that the read operation is complete. False + * indicates that further calls to the stream's read_some function are required. + * + * @returns The number of bytes transferred. + * + * @throws asio::system_error Thrown on failure. + */ +template +std::size_t read(SyncReadStream& s, basic_streambuf& b, + CompletionCondition completion_condition); + +/// Attempt to read a certain amount of data from a stream before returning. +/** + * This function is used to read a certain number of bytes of data from a + * stream. The call will block until one of the following conditions is true: + * + * @li The completion_condition function object returns true. + * + * This operation is implemented in terms of one or more calls to the stream's + * read_some function. + * + * @param s The stream from which the data is to be read. The type must support + * the SyncReadStream concept. + * + * @param b The basic_streambuf object into which the data will be read. + * + * @param completion_condition The function object to be called to determine + * whether the read operation is complete. The signature of the function object + * must be: + * @code bool completion_condition( + * const asio::error_code& error, // Result of latest read_some + * // operation. + * + * std::size_t bytes_transferred // Number of bytes transferred + * // so far. + * ); @endcode + * A return value of true indicates that the read operation is complete. False + * indicates that further calls to the stream's read_some function are required. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes read. If an error occurs, returns the total + * number of bytes successfully transferred prior to the error. + */ +template +std::size_t read(SyncReadStream& s, basic_streambuf& b, + CompletionCondition completion_condition, asio::error_code& ec); + +/*@}*/ +/** + * @defgroup async_read asio::async_read + */ +/*@{*/ + +/// Start an asynchronous operation to read a certain amount of data from a +/// stream. +/** + * This function is used to asynchronously read a certain number of bytes of + * data from a stream. The function call always returns immediately. The + * asynchronous operation will continue until one of the following conditions is + * true: + * + * @li The supplied buffers are full. That is, the bytes transferred is equal to + * the sum of the buffer sizes. + * + * @li An error occurred. + * + * This operation is implemented in terms of one or more calls to the stream's + * async_read_some function. + * + * @param s The stream from which the data is to be read. The type must support + * the AsyncReadStream concept. + * + * @param buffers One or more buffers into which the data will be read. The sum + * of the buffer sizes indicates the maximum number of bytes to read from the + * stream. Although the buffers object may be copied as necessary, ownership of + * the underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param handler The handler to be called when the read operation completes. + * Copies will be made of the handler as required. The function signature of the + * handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * + * std::size_t bytes_transferred // Number of bytes copied into the + * // buffers. If an error occurred, + * // this will be the number of + * // bytes successfully transferred + * // prior to the error. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation of + * the handler will be performed in a manner equivalent to using + * asio::io_service::post(). + * + * @par Example + * To read into a single data buffer use the @ref buffer function as follows: + * @code + * asio::async_read(s, asio::buffer(data, size), handler); + * @endcode + * See the @ref buffer documentation for information on reading into multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + * + * @note This overload is equivalent to calling: + * @code asio::async_read( + * s, buffers, + * asio::transfer_all(), + * handler); @endcode + */ +template +void async_read(AsyncReadStream& s, const MutableBufferSequence& buffers, + ReadHandler handler); + +/// Start an asynchronous operation to read a certain amount of data from a +/// stream. +/** + * This function is used to asynchronously read a certain number of bytes of + * data from a stream. The function call always returns immediately. The + * asynchronous operation will continue until one of the following conditions is + * true: + * + * @li The supplied buffers are full. That is, the bytes transferred is equal to + * the sum of the buffer sizes. + * + * @li The completion_condition function object returns true. + * + * @param s The stream from which the data is to be read. The type must support + * the AsyncReadStream concept. + * + * @param buffers One or more buffers into which the data will be read. The sum + * of the buffer sizes indicates the maximum number of bytes to read from the + * stream. Although the buffers object may be copied as necessary, ownership of + * the underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param completion_condition The function object to be called to determine + * whether the read operation is complete. The signature of the function object + * must be: + * @code bool completion_condition( + * const asio::error_code& error, // Result of latest read_some + * // operation. + * + * std::size_t bytes_transferred // Number of bytes transferred + * // so far. + * ); @endcode + * A return value of true indicates that the read operation is complete. False + * indicates that further calls to the stream's async_read_some function are + * required. + * + * @param handler The handler to be called when the read operation completes. + * Copies will be made of the handler as required. The function signature of the + * handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * + * std::size_t bytes_transferred // Number of bytes copied into the + * // buffers. If an error occurred, + * // this will be the number of + * // bytes successfully transferred + * // prior to the error. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation of + * the handler will be performed in a manner equivalent to using + * asio::io_service::post(). + * + * @par Example + * To read into a single data buffer use the @ref buffer function as follows: + * @code asio::async_read(s, + * asio::buffer(data, size), + * asio::transfer_at_least(32), + * handler); @endcode + * See the @ref buffer documentation for information on reading into multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ +template +void async_read(AsyncReadStream& s, const MutableBufferSequence& buffers, + CompletionCondition completion_condition, ReadHandler handler); + +/// Start an asynchronous operation to read a certain amount of data from a +/// stream. +/** + * This function is used to asynchronously read a certain number of bytes of + * data from a stream. The function call always returns immediately. The + * asynchronous operation will continue until one of the following conditions is + * true: + * + * @li An error occurred. + * + * This operation is implemented in terms of one or more calls to the stream's + * async_read_some function. + * + * @param s The stream from which the data is to be read. The type must support + * the AsyncReadStream concept. + * + * @param b A basic_streambuf object into which the data will be read. Ownership + * of the streambuf is retained by the caller, which must guarantee that it + * remains valid until the handler is called. + * + * @param handler The handler to be called when the read operation completes. + * Copies will be made of the handler as required. The function signature of the + * handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * + * std::size_t bytes_transferred // Number of bytes copied into the + * // buffers. If an error occurred, + * // this will be the number of + * // bytes successfully transferred + * // prior to the error. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation of + * the handler will be performed in a manner equivalent to using + * asio::io_service::post(). + * + * @note This overload is equivalent to calling: + * @code asio::async_read( + * s, b, + * asio::transfer_all(), + * handler); @endcode + */ +template +void async_read(AsyncReadStream& s, basic_streambuf& b, + ReadHandler handler); + +/// Start an asynchronous operation to read a certain amount of data from a +/// stream. +/** + * This function is used to asynchronously read a certain number of bytes of + * data from a stream. The function call always returns immediately. The + * asynchronous operation will continue until one of the following conditions is + * true: + * + * @li The completion_condition function object returns true. + * + * This operation is implemented in terms of one or more calls to the stream's + * async_read_some function. + * + * @param s The stream from which the data is to be read. The type must support + * the AsyncReadStream concept. + * + * @param b A basic_streambuf object into which the data will be read. Ownership + * of the streambuf is retained by the caller, which must guarantee that it + * remains valid until the handler is called. + * + * @param completion_condition The function object to be called to determine + * whether the read operation is complete. The signature of the function object + * must be: + * @code bool completion_condition( + * const asio::error_code& error, // Result of latest read_some + * // operation. + * + * std::size_t bytes_transferred // Number of bytes transferred + * // so far. + * ); @endcode + * A return value of true indicates that the read operation is complete. False + * indicates that further calls to the stream's async_read_some function are + * required. + * + * @param handler The handler to be called when the read operation completes. + * Copies will be made of the handler as required. The function signature of the + * handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * + * std::size_t bytes_transferred // Number of bytes copied into the + * // buffers. If an error occurred, + * // this will be the number of + * // bytes successfully transferred + * // prior to the error. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation of + * the handler will be performed in a manner equivalent to using + * asio::io_service::post(). + */ +template +void async_read(AsyncReadStream& s, basic_streambuf& b, + CompletionCondition completion_condition, ReadHandler handler); + +/*@}*/ + +} // namespace asio + +#include "asio/impl/read.ipp" + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_READ_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/read_until.hpp b/encryption/libtorrent/include/libtorrent/asio/read_until.hpp new file mode 100644 index 000000000..6917ec6b8 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/read_until.hpp @@ -0,0 +1,452 @@ +// +// read_until.hpp +// ~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_READ_UNTIL_HPP +#define ASIO_READ_UNTIL_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/basic_streambuf.hpp" +#include "asio/error.hpp" + +namespace asio { + +/** + * @defgroup read_until asio::read_until + */ +/*@{*/ + +/// Read data into a streambuf until a delimiter is encountered. +/** + * This function is used to read data into the specified streambuf until the + * streambuf's get area contains the specified delimiter. The call will block + * until one of the following conditions is true: + * + * @li The get area of the streambuf contains the specified delimiter. + * + * @li An error occurred. + * + * This operation is implemented in terms of zero or more calls to the stream's + * read_some function. If the streambuf's get area already contains the + * delimiter, the function returns immediately. + * + * @param s The stream from which the data is to be read. The type must support + * the SyncReadStream concept. + * + * @param b A streambuf object into which the data will be read. + * + * @param delim The delimiter character. + * + * @returns The number of bytes in the streambuf's get area up to and including + * the delimiter. + * + * @throws asio::system_error Thrown on failure. + * + * @par Example + * To read data into a streambuf until a newline is encountered: + * @code asio::streambuf b; + * asio::read_until(s, b, '\n'); + * std::istream is(&b); + * std::string line; + * std::getline(is, line); @endcode + */ +template +std::size_t read_until(SyncReadStream& s, + asio::basic_streambuf& b, char delim); + +/// Read data into a streambuf until a delimiter is encountered. +/** + * This function is used to read data into the specified streambuf until the + * streambuf's get area contains the specified delimiter. The call will block + * until one of the following conditions is true: + * + * @li The get area of the streambuf contains the specified delimiter. + * + * @li An error occurred. + * + * This operation is implemented in terms of zero or more calls to the stream's + * read_some function. If the streambuf's get area already contains the + * delimiter, the function returns immediately. + * + * @param s The stream from which the data is to be read. The type must support + * the SyncReadStream concept. + * + * @param b A streambuf object into which the data will be read. + * + * @param delim The delimiter character. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes in the streambuf's get area up to and including + * the delimiter. Returns 0 if an error occurred. + */ +template +std::size_t read_until(SyncReadStream& s, + asio::basic_streambuf& b, char delim, + asio::error_code& ec); + +/// Read data into a streambuf until a delimiter is encountered. +/** + * This function is used to read data into the specified streambuf until the + * streambuf's get area contains the specified delimiter. The call will block + * until one of the following conditions is true: + * + * @li The get area of the streambuf contains the specified delimiter. + * + * @li An error occurred. + * + * This operation is implemented in terms of zero or more calls to the stream's + * read_some function. If the streambuf's get area already contains the + * delimiter, the function returns immediately. + * + * @param s The stream from which the data is to be read. The type must support + * the SyncReadStream concept. + * + * @param b A streambuf object into which the data will be read. + * + * @param delim The delimiter string. + * + * @returns The number of bytes in the streambuf's get area up to and including + * the delimiter. + * + * @throws asio::system_error Thrown on failure. + * + * @par Example + * To read data into a streambuf until a newline is encountered: + * @code asio::streambuf b; + * asio::read_until(s, b, "\r\n"); + * std::istream is(&b); + * std::string line; + * std::getline(is, line); @endcode + */ +template +std::size_t read_until(SyncReadStream& s, + asio::basic_streambuf& b, const std::string& delim); + +/// Read data into a streambuf until a delimiter is encountered. +/** + * This function is used to read data into the specified streambuf until the + * streambuf's get area contains the specified delimiter. The call will block + * until one of the following conditions is true: + * + * @li The get area of the streambuf contains the specified delimiter. + * + * @li An error occurred. + * + * This operation is implemented in terms of zero or more calls to the stream's + * read_some function. If the streambuf's get area already contains the + * delimiter, the function returns immediately. + * + * @param s The stream from which the data is to be read. The type must support + * the SyncReadStream concept. + * + * @param b A streambuf object into which the data will be read. + * + * @param delim The delimiter string. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes in the streambuf's get area up to and including + * the delimiter. Returns 0 if an error occurred. + */ +template +std::size_t read_until(SyncReadStream& s, + asio::basic_streambuf& b, const std::string& delim, + asio::error_code& ec); + +/// Read data into a streambuf until a regular expression is located. +/** + * This function is used to read data into the specified streambuf until the + * streambuf's get area contains some data that matches a regular expression. + * The call will block until one of the following conditions is true: + * + * @li A substring of the streambuf's get area matches the regular expression. + * + * @li An error occurred. + * + * This operation is implemented in terms of zero or more calls to the stream's + * read_some function. If the streambuf's get area already contains data that + * matches the regular expression, the function returns immediately. + * + * @param s The stream from which the data is to be read. The type must support + * the SyncReadStream concept. + * + * @param b A streambuf object into which the data will be read. + * + * @param expr The regular expression. + * + * @returns The number of bytes in the streambuf's get area up to and including + * the substring that matches the regular expression. + * + * @throws asio::system_error Thrown on failure. + * + * @par Example + * To read data into a streambuf until a CR-LF sequence is encountered: + * @code asio::streambuf b; + * asio::read_until(s, b, boost::regex("\r\n")); + * std::istream is(&b); + * std::string line; + * std::getline(is, line); @endcode + */ +template +std::size_t read_until(SyncReadStream& s, + asio::basic_streambuf& b, const boost::regex& expr); + +/// Read data into a streambuf until a regular expression is located. +/** + * This function is used to read data into the specified streambuf until the + * streambuf's get area contains some data that matches a regular expression. + * The call will block until one of the following conditions is true: + * + * @li A substring of the streambuf's get area matches the regular expression. + * + * @li An error occurred. + * + * This operation is implemented in terms of zero or more calls to the stream's + * read_some function. If the streambuf's get area already contains data that + * matches the regular expression, the function returns immediately. + * + * @param s The stream from which the data is to be read. The type must support + * the SyncReadStream concept. + * + * @param b A streambuf object into which the data will be read. + * + * @param expr The regular expression. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes in the streambuf's get area up to and including + * the substring that matches the regular expression. Returns 0 if an error + * occurred. + */ +template +std::size_t read_until(SyncReadStream& s, + asio::basic_streambuf& b, const boost::regex& expr, + asio::error_code& ec); + +/*@}*/ +/** +* @defgroup async_read_until asio::async_read_until +*/ +/*@{*/ + +/// Start an asynchronous operation to read data into a streambuf until a +/// delimiter is encountered. +/** + * This function is used to asynchronously read data into the specified + * streambuf until the streambuf's get area contains the specified delimiter. + * The function call always returns immediately. The asynchronous operation + * will continue until one of the following conditions is true: + * + * @li The get area of the streambuf contains the specified delimiter. + * + * @li An error occurred. + * + * This operation is implemented in terms of zero or more calls to the stream's + * async_read_some function. If the streambuf's get area already contains the + * delimiter, the asynchronous operation completes immediately. + * + * @param s The stream from which the data is to be read. The type must support + * the AsyncReadStream concept. + * + * @param b A streambuf object into which the data will be read. Ownership of + * the streambuf is retained by the caller, which must guarantee that it remains + * valid until the handler is called. + * + * @param delim The delimiter character. + * + * @param handler The handler to be called when the read operation completes. + * Copies will be made of the handler as required. The function signature of the + * handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * + * std::size_t bytes_transferred // The number of bytes in the + * // streambuf's get area up to + * // and including the delimiter. + * // 0 if an error occurred. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation of + * the handler will be performed in a manner equivalent to using + * asio::io_service::post(). + * + * @par Example + * To asynchronously read data into a streambuf until a newline is encountered: + * @code asio::streambuf b; + * ... + * void handler(const asio::error_code& e, std::size_t size) + * { + * if (!e) + * { + * std::istream is(&b); + * std::string line; + * std::getline(is, line); + * ... + * } + * } + * ... + * asio::async_read_until(s, b, '\n', handler); @endcode + */ +template +void async_read_until(AsyncReadStream& s, + asio::basic_streambuf& b, + char delim, ReadHandler handler); + +/// Start an asynchronous operation to read data into a streambuf until a +/// delimiter is encountered. +/** + * This function is used to asynchronously read data into the specified + * streambuf until the streambuf's get area contains the specified delimiter. + * The function call always returns immediately. The asynchronous operation + * will continue until one of the following conditions is true: + * + * @li The get area of the streambuf contains the specified delimiter. + * + * @li An error occurred. + * + * This operation is implemented in terms of zero or more calls to the stream's + * async_read_some function. If the streambuf's get area already contains the + * delimiter, the asynchronous operation completes immediately. + * + * @param s The stream from which the data is to be read. The type must support + * the AsyncReadStream concept. + * + * @param b A streambuf object into which the data will be read. Ownership of + * the streambuf is retained by the caller, which must guarantee that it remains + * valid until the handler is called. + * + * @param delim The delimiter string. + * + * @param handler The handler to be called when the read operation completes. + * Copies will be made of the handler as required. The function signature of the + * handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * + * std::size_t bytes_transferred // The number of bytes in the + * // streambuf's get area up to + * // and including the delimiter. + * // 0 if an error occurred. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation of + * the handler will be performed in a manner equivalent to using + * asio::io_service::post(). + * + * @par Example + * To asynchronously read data into a streambuf until a newline is encountered: + * @code asio::streambuf b; + * ... + * void handler(const asio::error_code& e, std::size_t size) + * { + * if (!e) + * { + * std::istream is(&b); + * std::string line; + * std::getline(is, line); + * ... + * } + * } + * ... + * asio::async_read_until(s, b, "\r\n", handler); @endcode + */ +template +void async_read_until(AsyncReadStream& s, + asio::basic_streambuf& b, const std::string& delim, + ReadHandler handler); + +/// Start an asynchronous operation to read data into a streambuf until a +/// regular expression is located. +/** + * This function is used to asynchronously read data into the specified + * streambuf until the streambuf's get area contains some data that matches a + * regular expression. The function call always returns immediately. The + * asynchronous operation will continue until one of the following conditions + * is true: + * + * @li A substring of the streambuf's get area matches the regular expression. + * + * @li An error occurred. + * + * This operation is implemented in terms of zero or more calls to the stream's + * async_read_some function. If the streambuf's get area already contains data + * that matches the regular expression, the function returns immediately. + * + * @param s The stream from which the data is to be read. The type must support + * the AsyncReadStream concept. + * + * @param b A streambuf object into which the data will be read. Ownership of + * the streambuf is retained by the caller, which must guarantee that it remains + * valid until the handler is called. + * + * @param expr The regular expression. + * + * @param handler The handler to be called when the read operation completes. + * Copies will be made of the handler as required. The function signature of the + * handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * + * std::size_t bytes_transferred // The number of bytes in the + * // streambuf's get area up to + * // and including the substring + * // that matches the regular. + * // expression. 0 if an error + * // occurred. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation of + * the handler will be performed in a manner equivalent to using + * asio::io_service::post(). + * + * @par Example + * To asynchronously read data into a streambuf until a CR-LF sequence is + * encountered: + * @code asio::streambuf b; + * ... + * void handler(const asio::error_code& e, std::size_t size) + * { + * if (!e) + * { + * std::istream is(&b); + * std::string line; + * std::getline(is, line); + * ... + * } + * } + * ... + * asio::async_read_until(s, b, boost::regex("\r\n"), handler); @endcode + */ +template +void async_read_until(AsyncReadStream& s, + asio::basic_streambuf& b, const boost::regex& expr, + ReadHandler handler); + +/*@}*/ + +} // namespace asio + +#include "asio/impl/read_until.ipp" + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_READ_UNTIL_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/resolver_service.hpp b/encryption/libtorrent/include/libtorrent/asio/resolver_service.hpp new file mode 100644 index 000000000..bdd8dbfbd --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/resolver_service.hpp @@ -0,0 +1,126 @@ +// +// resolver_service.hpp +// ~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_RESOLVER_SERVICE_HPP +#define ASIO_RESOLVER_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/io_service.hpp" +#include "asio/detail/resolver_service.hpp" + +namespace asio { + +/// Default service implementation for a resolver. +template +class resolver_service + : public asio::io_service::service +{ +public: + /// The protocol type. + typedef Protocol protocol_type; + + /// The endpoint type. + typedef typename Protocol::endpoint endpoint_type; + + /// The query type. + typedef typename Protocol::resolver_query query_type; + + /// The iterator type. + typedef typename Protocol::resolver_iterator iterator_type; + +private: + // The type of the platform-specific implementation. + typedef detail::resolver_service service_impl_type; + +public: + /// The type of a resolver implementation. +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined implementation_type; +#else + typedef typename service_impl_type::implementation_type implementation_type; +#endif + + /// Construct a new resolver service for the specified io_service. + explicit resolver_service(asio::io_service& io_service) + : asio::io_service::service(io_service), + service_impl_(asio::use_service(io_service)) + { + } + + /// Destroy all user-defined handler objects owned by the service. + void shutdown_service() + { + } + + /// Construct a new resolver implementation. + void construct(implementation_type& impl) + { + service_impl_.construct(impl); + } + + /// Destroy a resolver implementation. + void destroy(implementation_type& impl) + { + service_impl_.destroy(impl); + } + + /// Cancel pending asynchronous operations. + void cancel(implementation_type& impl) + { + service_impl_.cancel(impl); + } + + /// Resolve a query to a list of entries. + template + iterator_type resolve(implementation_type& impl, const query_type& query, + Error_Handler error_handler) + { + return service_impl_.resolve(impl, query, error_handler); + } + + /// Asynchronously resolve a query to a list of entries. + template + void async_resolve(implementation_type& impl, const query_type& query, + Handler handler) + { + service_impl_.async_resolve(impl, query, handler); + } + + /// Resolve an endpoint to a list of entries. + template + iterator_type resolve(implementation_type& impl, + const endpoint_type& endpoint, Error_Handler error_handler) + { + return service_impl_.resolve(impl, endpoint, error_handler); + } + + /// Asynchronously resolve an endpoint to a list of entries. + template + void async_resolve(implementation_type& impl, const endpoint_type& endpoint, + Handler handler) + { + return service_impl_.async_resolve(impl, endpoint, handler); + } + +private: + // The service that provides the platform-specific implementation. + service_impl_type& service_impl_; +}; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_RESOLVER_SERVICE_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/socket_acceptor_service.hpp b/encryption/libtorrent/include/libtorrent/asio/socket_acceptor_service.hpp new file mode 100644 index 000000000..965bff39e --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/socket_acceptor_service.hpp @@ -0,0 +1,222 @@ +// +// socket_acceptor_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_SOCKET_ACCEPTOR_SERVICE_HPP +#define ASIO_SOCKET_ACCEPTOR_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/basic_socket.hpp" +#include "asio/error.hpp" +#include "asio/io_service.hpp" +#include "asio/detail/epoll_reactor.hpp" +#include "asio/detail/kqueue_reactor.hpp" +#include "asio/detail/select_reactor.hpp" +#include "asio/detail/service_base.hpp" +#include "asio/detail/reactive_socket_service.hpp" +#include "asio/detail/win_iocp_socket_service.hpp" + +namespace asio { + +/// Default service implementation for a socket acceptor. +template +class socket_acceptor_service +#if defined(GENERATING_DOCUMENTATION) + : public asio::io_service::service +#else + : public asio::detail::service_base > +#endif +{ +public: +#if defined(GENERATING_DOCUMENTATION) + /// The unique service identifier. + static asio::io_service::id id; +#endif + + /// The protocol type. + typedef Protocol protocol_type; + + /// The endpoint type. + typedef typename protocol_type::endpoint endpoint_type; + +private: + // The type of the platform-specific implementation. +#if defined(ASIO_HAS_IOCP) + typedef detail::win_iocp_socket_service service_impl_type; +#elif defined(ASIO_HAS_EPOLL) + typedef detail::reactive_socket_service< + Protocol, detail::epoll_reactor > service_impl_type; +#elif defined(ASIO_HAS_KQUEUE) + typedef detail::reactive_socket_service< + Protocol, detail::kqueue_reactor > service_impl_type; +#else + typedef detail::reactive_socket_service< + Protocol, detail::select_reactor > service_impl_type; +#endif + +public: + /// The native type of the socket acceptor. +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined implementation_type; +#else + typedef typename service_impl_type::implementation_type implementation_type; +#endif + + /// The native acceptor type. +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined native_type; +#else + typedef typename service_impl_type::native_type native_type; +#endif + + /// Construct a new socket acceptor service for the specified io_service. + explicit socket_acceptor_service(asio::io_service& io_service) + : asio::detail::service_base< + socket_acceptor_service >(io_service), + service_impl_(asio::use_service(io_service)) + { + } + + /// Destroy all user-defined handler objects owned by the service. + void shutdown_service() + { + } + + /// Construct a new socket acceptor implementation. + void construct(implementation_type& impl) + { + service_impl_.construct(impl); + } + + /// Destroy a socket acceptor implementation. + void destroy(implementation_type& impl) + { + service_impl_.destroy(impl); + } + + /// Open a new socket acceptor implementation. + asio::error_code open(implementation_type& impl, + const protocol_type& protocol, asio::error_code& ec) + { + return service_impl_.open(impl, protocol, ec); + } + + /// Assign an existing native acceptor to a socket acceptor. + asio::error_code assign(implementation_type& impl, + const protocol_type& protocol, const native_type& native_acceptor, + asio::error_code& ec) + { + return service_impl_.assign(impl, protocol, native_acceptor, ec); + } + + /// Determine whether the acceptor is open. + bool is_open(const implementation_type& impl) const + { + return service_impl_.is_open(impl); + } + + /// Cancel all asynchronous operations associated with the acceptor. + asio::error_code cancel(implementation_type& impl, + asio::error_code& ec) + { + return service_impl_.cancel(impl, ec); + } + + /// Bind the socket acceptor to the specified local endpoint. + asio::error_code bind(implementation_type& impl, + const endpoint_type& endpoint, asio::error_code& ec) + { + return service_impl_.bind(impl, endpoint, ec); + } + + /// Place the socket acceptor into the state where it will listen for new + /// connections. + asio::error_code listen(implementation_type& impl, int backlog, + asio::error_code& ec) + { + return service_impl_.listen(impl, backlog, ec); + } + + /// Close a socket acceptor implementation. + asio::error_code close(implementation_type& impl, + asio::error_code& ec) + { + return service_impl_.close(impl, ec); + } + + /// Get the native acceptor implementation. + native_type native(implementation_type& impl) + { + return service_impl_.native(impl); + } + + /// Set a socket option. + template + asio::error_code set_option(implementation_type& impl, + const SettableSocketOption& option, asio::error_code& ec) + { + return service_impl_.set_option(impl, option, ec); + } + + /// Get a socket option. + template + asio::error_code get_option(const implementation_type& impl, + GettableSocketOption& option, asio::error_code& ec) const + { + return service_impl_.get_option(impl, option, ec); + } + + /// Perform an IO control command on the socket. + template + asio::error_code io_control(implementation_type& impl, + IoControlCommand& command, asio::error_code& ec) + { + return service_impl_.io_control(impl, command, ec); + } + + /// Get the local endpoint. + endpoint_type local_endpoint(const implementation_type& impl, + asio::error_code& ec) const + { + return service_impl_.local_endpoint(impl, ec); + } + + /// Accept a new connection. + template + asio::error_code accept(implementation_type& impl, + basic_socket& peer, + endpoint_type* peer_endpoint, asio::error_code& ec) + { + return service_impl_.accept(impl, peer, peer_endpoint, ec); + } + + /// Start an asynchronous accept. + template + void async_accept(implementation_type& impl, + basic_socket& peer, + endpoint_type* peer_endpoint, AcceptHandler handler) + { + service_impl_.async_accept(impl, peer, peer_endpoint, handler); + } + +private: + // The service that provides the platform-specific implementation. + service_impl_type& service_impl_; +}; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_SOCKET_ACCEPTOR_SERVICE_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/socket_base.hpp b/encryption/libtorrent/include/libtorrent/asio/socket_base.hpp new file mode 100644 index 000000000..1239989a9 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/socket_base.hpp @@ -0,0 +1,515 @@ +// +// socket_base.hpp +// ~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_SOCKET_BASE_HPP +#define ASIO_SOCKET_BASE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/detail/io_control.hpp" +#include "asio/detail/socket_option.hpp" +#include "asio/detail/socket_types.hpp" + +namespace asio { + +/// The socket_base class is used as a base for the basic_stream_socket and +/// basic_datagram_socket class templates so that we have a common place to +/// define the shutdown_type and enum. +class socket_base +{ +public: + /// Different ways a socket may be shutdown. + enum shutdown_type + { +#if defined(GENERATING_DOCUMENTATION) + /// Shutdown the receive side of the socket. + shutdown_receive = implementation_defined, + + /// Shutdown the send side of the socket. + shutdown_send = implementation_defined, + + /// Shutdown both send and receive on the socket. + shutdown_both = implementation_defined +#else + shutdown_receive = asio::detail::shutdown_receive, + shutdown_send = asio::detail::shutdown_send, + shutdown_both = asio::detail::shutdown_both +#endif + }; + + /// Bitmask type for flags that can be passed to send and receive operations. + typedef int message_flags; + +#if defined(GENERATING_DOCUMENTATION) + /// Peek at incoming data without removing it from the input queue. + static const int message_peek = implementation_defined; + + /// Process out-of-band data. + static const int message_out_of_band = implementation_defined; + + /// Specify that the data should not be subject to routing. + static const int message_do_not_route = implementation_defined; +#else + BOOST_STATIC_CONSTANT(int, + message_peek = asio::detail::message_peek); + BOOST_STATIC_CONSTANT(int, + message_out_of_band = asio::detail::message_out_of_band); + BOOST_STATIC_CONSTANT(int, + message_do_not_route = asio::detail::message_do_not_route); +#endif + + /// Socket option to permit sending of broadcast messages. + /** + * Implements the SOL_SOCKET/SO_BROADCAST socket option. + * + * @par Examples + * Setting the option: + * @code + * asio::ip::udp::socket socket(io_service); + * ... + * asio::socket_base::broadcast option(true); + * socket.set_option(option); + * @endcode + * + * @par + * Getting the current option value: + * @code + * asio::ip::udp::socket socket(io_service); + * ... + * asio::socket_base::broadcast option; + * socket.get_option(option); + * bool is_set = option.value(); + * @endcode + * + * @par Concepts: + * Socket_Option, Boolean_Socket_Option. + */ +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined broadcast; +#else + typedef asio::detail::socket_option::boolean< + SOL_SOCKET, SO_BROADCAST> broadcast; +#endif + + /// Socket option to enable socket-level debugging. + /** + * Implements the SOL_SOCKET/SO_DEBUG socket option. + * + * @par Examples + * Setting the option: + * @code + * asio::ip::tcp::socket socket(io_service); + * ... + * asio::socket_base::debug option(true); + * socket.set_option(option); + * @endcode + * + * @par + * Getting the current option value: + * @code + * asio::ip::tcp::socket socket(io_service); + * ... + * asio::socket_base::debug option; + * socket.get_option(option); + * bool is_set = option.value(); + * @endcode + * + * @par Concepts: + * Socket_Option, Boolean_Socket_Option. + */ +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined debug; +#else + typedef asio::detail::socket_option::boolean< + SOL_SOCKET, SO_DEBUG> debug; +#endif + + /// Socket option to prevent routing, use local interfaces only. + /** + * Implements the SOL_SOCKET/SO_DONTROUTE socket option. + * + * @par Examples + * Setting the option: + * @code + * asio::ip::udp::socket socket(io_service); + * ... + * asio::socket_base::do_not_route option(true); + * socket.set_option(option); + * @endcode + * + * @par + * Getting the current option value: + * @code + * asio::ip::udp::socket socket(io_service); + * ... + * asio::socket_base::do_not_route option; + * socket.get_option(option); + * bool is_set = option.value(); + * @endcode + * + * @par Concepts: + * Socket_Option, Boolean_Socket_Option. + */ +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined do_not_route; +#else + typedef asio::detail::socket_option::boolean< + SOL_SOCKET, SO_DONTROUTE> do_not_route; +#endif + + /// Socket option to send keep-alives. + /** + * Implements the SOL_SOCKET/SO_KEEPALIVE socket option. + * + * @par Examples + * Setting the option: + * @code + * asio::ip::tcp::socket socket(io_service); + * ... + * asio::socket_base::keep_alive option(true); + * socket.set_option(option); + * @endcode + * + * @par + * Getting the current option value: + * @code + * asio::ip::tcp::socket socket(io_service); + * ... + * asio::socket_base::keep_alive option; + * socket.get_option(option); + * bool is_set = option.value(); + * @endcode + * + * @par Concepts: + * Socket_Option, Boolean_Socket_Option. + */ +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined keep_alive; +#else + typedef asio::detail::socket_option::boolean< + SOL_SOCKET, SO_KEEPALIVE> keep_alive; +#endif + + /// Socket option for the send buffer size of a socket. + /** + * Implements the SOL_SOCKET/SO_SNDBUF socket option. + * + * @par Examples + * Setting the option: + * @code + * asio::ip::tcp::socket socket(io_service); + * ... + * asio::socket_base::send_buffer_size option(8192); + * socket.set_option(option); + * @endcode + * + * @par + * Getting the current option value: + * @code + * asio::ip::tcp::socket socket(io_service); + * ... + * asio::socket_base::send_buffer_size option; + * socket.get_option(option); + * int size = option.value(); + * @endcode + * + * @par Concepts: + * Socket_Option, Integer_Socket_Option. + */ +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined send_buffer_size; +#else + typedef asio::detail::socket_option::integer< + SOL_SOCKET, SO_SNDBUF> send_buffer_size; +#endif + + /// Socket option for the send low watermark. + /** + * Implements the SOL_SOCKET/SO_SNDLOWAT socket option. + * + * @par Examples + * Setting the option: + * @code + * asio::ip::tcp::socket socket(io_service); + * ... + * asio::socket_base::send_low_watermark option(1024); + * socket.set_option(option); + * @endcode + * + * @par + * Getting the current option value: + * @code + * asio::ip::tcp::socket socket(io_service); + * ... + * asio::socket_base::send_low_watermark option; + * socket.get_option(option); + * int size = option.value(); + * @endcode + * + * @par Concepts: + * Socket_Option, Integer_Socket_Option. + */ +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined send_low_watermark; +#else + typedef asio::detail::socket_option::integer< + SOL_SOCKET, SO_SNDLOWAT> send_low_watermark; +#endif + + /// Socket option for the receive buffer size of a socket. + /** + * Implements the SOL_SOCKET/SO_RCVBUF socket option. + * + * @par Examples + * Setting the option: + * @code + * asio::ip::tcp::socket socket(io_service); + * ... + * asio::socket_base::receive_buffer_size option(8192); + * socket.set_option(option); + * @endcode + * + * @par + * Getting the current option value: + * @code + * asio::ip::tcp::socket socket(io_service); + * ... + * asio::socket_base::receive_buffer_size option; + * socket.get_option(option); + * int size = option.value(); + * @endcode + * + * @par Concepts: + * Socket_Option, Integer_Socket_Option. + */ +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined receive_buffer_size; +#else + typedef asio::detail::socket_option::integer< + SOL_SOCKET, SO_RCVBUF> receive_buffer_size; +#endif + + /// Socket option for the receive low watermark. + /** + * Implements the SOL_SOCKET/SO_RCVLOWAT socket option. + * + * @par Examples + * Setting the option: + * @code + * asio::ip::tcp::socket socket(io_service); + * ... + * asio::socket_base::receive_low_watermark option(1024); + * socket.set_option(option); + * @endcode + * + * @par + * Getting the current option value: + * @code + * asio::ip::tcp::socket socket(io_service); + * ... + * asio::socket_base::receive_low_watermark option; + * socket.get_option(option); + * int size = option.value(); + * @endcode + * + * @par Concepts: + * Socket_Option, Integer_Socket_Option. + */ +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined receive_low_watermark; +#else + typedef asio::detail::socket_option::integer< + SOL_SOCKET, SO_RCVLOWAT> receive_low_watermark; +#endif + + /// Socket option to allow the socket to be bound to an address that is + /// already in use. + /** + * Implements the SOL_SOCKET/SO_REUSEADDR socket option. + * + * @par Examples + * Setting the option: + * @code + * asio::ip::tcp::acceptor acceptor(io_service); + * ... + * asio::socket_base::reuse_address option(true); + * acceptor.set_option(option); + * @endcode + * + * @par + * Getting the current option value: + * @code + * asio::ip::tcp::acceptor acceptor(io_service); + * ... + * asio::socket_base::reuse_address option; + * acceptor.get_option(option); + * bool is_set = option.value(); + * @endcode + * + * @par Concepts: + * Socket_Option, Boolean_Socket_Option. + */ +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined reuse_address; +#else + typedef asio::detail::socket_option::boolean< + SOL_SOCKET, SO_REUSEADDR> reuse_address; +#endif + + /// Socket option to specify whether the socket lingers on close if unsent + /// data is present. + /** + * Implements the SOL_SOCKET/SO_LINGER socket option. + * + * @par Examples + * Setting the option: + * @code + * asio::ip::tcp::socket socket(io_service); + * ... + * asio::socket_base::linger option(true, 30); + * socket.set_option(option); + * @endcode + * + * @par + * Getting the current option value: + * @code + * asio::ip::tcp::socket socket(io_service); + * ... + * asio::socket_base::linger option; + * socket.get_option(option); + * bool is_set = option.enabled(); + * unsigned short timeout = option.timeout(); + * @endcode + * + * @par Concepts: + * Socket_Option, Linger_Socket_Option. + */ +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined linger; +#else + typedef asio::detail::socket_option::linger< + SOL_SOCKET, SO_LINGER> linger; +#endif + + /// Socket option to report aborted connections on accept. + /** + * Implements a custom socket option that determines whether or not an accept + * operation is permitted to fail with asio::error::connection_aborted. + * By default the option is false. + * + * @par Examples + * Setting the option: + * @code + * asio::ip::tcp::acceptor acceptor(io_service); + * ... + * asio::socket_base::enable_connection_aborted option(true); + * acceptor.set_option(option); + * @endcode + * + * @par + * Getting the current option value: + * @code + * asio::ip::tcp::acceptor acceptor(io_service); + * ... + * asio::socket_base::enable_connection_aborted option; + * acceptor.get_option(option); + * bool is_set = option.value(); + * @endcode + * + * @par Concepts: + * Socket_Option, Boolean_Socket_Option. + */ +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined enable_connection_aborted; +#else + typedef asio::detail::socket_option::boolean< + asio::detail::custom_socket_option_level, + asio::detail::enable_connection_aborted_option> + enable_connection_aborted; +#endif + + /// IO control command to set the blocking mode of the socket. + /** + * Implements the FIONBIO IO control command. + * + * @par Example + * @code + * asio::ip::tcp::socket socket(io_service); + * ... + * asio::socket_base::non_blocking_io command(true); + * socket.io_control(command); + * @endcode + * + * @par Concepts: + * IO_Control_Command, Boolean_IO_Control_Command. + */ +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined non_blocking_io; +#else + typedef asio::detail::io_control::non_blocking_io non_blocking_io; +#endif + + /// IO control command to get the amount of data that can be read without + /// blocking. + /** + * Implements the FIONREAD IO control command. + * + * @par Example + * @code + * asio::ip::tcp::socket socket(io_service); + * ... + * asio::socket_base::bytes_readable command(true); + * socket.io_control(command); + * std::size_t bytes_readable = command.get(); + * @endcode + * + * @par Concepts: + * IO_Control_Command, Size_IO_Control_Command. + */ +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined bytes_readable; +#else + typedef asio::detail::io_control::bytes_readable bytes_readable; +#endif + + /// The maximum length of the queue of pending incoming connections. +#if defined(GENERATING_DOCUMENTATION) + static const int max_connections = implementation_defined; +#else + BOOST_STATIC_CONSTANT(int, max_connections = SOMAXCONN); +#endif + +protected: + /// Protected destructor to prevent deletion through this type. + ~socket_base() + { + } + +#if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564)) +private: + // Workaround to enable the empty base optimisation with Borland C++. + char dummy_; +#endif +}; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_SOCKET_BASE_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/ssl.hpp b/encryption/libtorrent/include/libtorrent/asio/ssl.hpp new file mode 100644 index 000000000..3f2d2750c --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/ssl.hpp @@ -0,0 +1,26 @@ +// +// ssl.hpp +// ~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_SSL_HPP +#define ASIO_SSL_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/ssl/basic_context.hpp" +#include "asio/ssl/context.hpp" +#include "asio/ssl/context_base.hpp" +#include "asio/ssl/context_service.hpp" +#include "asio/ssl/stream.hpp" +#include "asio/ssl/stream_base.hpp" +#include "asio/ssl/stream_service.hpp" + +#endif // ASIO_SSL_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/ssl/basic_context.hpp b/encryption/libtorrent/include/libtorrent/asio/ssl/basic_context.hpp new file mode 100755 index 000000000..b85a01c1d --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/ssl/basic_context.hpp @@ -0,0 +1,434 @@ +// +// basic_context.hpp +// ~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2005 Voipster / Indrek dot Juhani at voipster dot com +// Copyright (c) 2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_SSL_BASIC_CONTEXT_HPP +#define ASIO_SSL_BASIC_CONTEXT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/error.hpp" +#include "asio/io_service.hpp" +#include "asio/ssl/context_base.hpp" +#include "asio/detail/throw_error.hpp" + +namespace asio { +namespace ssl { + +/// SSL context. +template +class basic_context + : public context_base, + private boost::noncopyable +{ +public: + /// The type of the service that will be used to provide context operations. + typedef Service service_type; + + /// The native implementation type of the locking dispatcher. + typedef typename service_type::impl_type impl_type; + + /// Constructor. + basic_context(asio::io_service& io_service, method m) + : service_(asio::use_service(io_service)), + impl_(service_.null()) + { + service_.create(impl_, m); + } + + /// Destructor. + ~basic_context() + { + service_.destroy(impl_); + } + + /// Get the underlying implementation in the native type. + /** + * This function may be used to obtain the underlying implementation of the + * context. This is intended to allow access to context functionality that is + * not otherwise provided. + */ + impl_type impl() + { + return impl_; + } + + /// Set options on the context. + /** + * This function may be used to configure the SSL options used by the context. + * + * @param o A bitmask of options. The available option values are defined in + * the context_base class. The options are bitwise-ored with any existing + * value for the options. + * + * @throws asio::system_error Thrown on failure. + */ + void set_options(options o) + { + asio::error_code ec; + service_.set_options(impl_, o, ec); + asio::detail::throw_error(ec); + } + + /// Set options on the context. + /** + * This function may be used to configure the SSL options used by the context. + * + * @param o A bitmask of options. The available option values are defined in + * the context_base class. The options are bitwise-ored with any existing + * value for the options. + * + * @param ec Set to indicate what error occurred, if any. + */ + asio::error_code set_options(options o, + asio::error_code& ec) + { + return service_.set_options(impl_, o, ec); + } + + /// Set the peer verification mode. + /** + * This function may be used to configure the peer verification mode used by + * the context. + * + * @param v A bitmask of peer verification modes. The available verify_mode + * values are defined in the context_base class. + * + * @throws asio::system_error Thrown on failure. + */ + void set_verify_mode(verify_mode v) + { + asio::error_code ec; + service_.set_verify_mode(impl_, v, ec); + asio::detail::throw_error(ec); + } + + /// Set the peer verification mode. + /** + * This function may be used to configure the peer verification mode used by + * the context. + * + * @param v A bitmask of peer verification modes. The available verify_mode + * values are defined in the context_base class. + * + * @param ec Set to indicate what error occurred, if any. + */ + asio::error_code set_verify_mode(verify_mode v, + asio::error_code& ec) + { + return service_.set_verify_mode(impl_, v, ec); + } + + /// Load a certification authority file for performing verification. + /** + * This function is used to load one or more trusted certification authorities + * from a file. + * + * @param filename The name of a file containing certification authority + * certificates in PEM format. + * + * @throws asio::system_error Thrown on failure. + */ + void load_verify_file(const std::string& filename) + { + asio::error_code ec; + service_.load_verify_file(impl_, filename, ec); + asio::detail::throw_error(ec); + } + + /// Load a certification authority file for performing verification. + /** + * This function is used to load the certificates for one or more trusted + * certification authorities from a file. + * + * @param filename The name of a file containing certification authority + * certificates in PEM format. + * + * @param ec Set to indicate what error occurred, if any. + */ + asio::error_code load_verify_file(const std::string& filename, + asio::error_code& ec) + { + return service_.load_verify_file(impl_, filename, ec); + } + + /// Add a directory containing certificate authority files to be used for + /// performing verification. + /** + * This function is used to specify the name of a directory containing + * certification authority certificates. Each file in the directory must + * contain a single certificate. The files must be named using the subject + * name's hash and an extension of ".0". + * + * @param path The name of a directory containing the certificates. + * + * @throws asio::system_error Thrown on failure. + */ + void add_verify_path(const std::string& path) + { + asio::error_code ec; + service_.add_verify_path(impl_, path, ec); + asio::detail::throw_error(ec); + } + + /// Add a directory containing certificate authority files to be used for + /// performing verification. + /** + * This function is used to specify the name of a directory containing + * certification authority certificates. Each file in the directory must + * contain a single certificate. The files must be named using the subject + * name's hash and an extension of ".0". + * + * @param path The name of a directory containing the certificates. + * + * @param ec Set to indicate what error occurred, if any. + */ + asio::error_code add_verify_path(const std::string& path, + asio::error_code& ec) + { + return service_.add_verify_path(impl_, path, ec); + } + + /// Use a certificate from a file. + /** + * This function is used to load a certificate into the context from a file. + * + * @param filename The name of the file containing the certificate. + * + * @param format The file format (ASN.1 or PEM). + * + * @throws asio::system_error Thrown on failure. + */ + void use_certificate_file(const std::string& filename, file_format format) + { + asio::error_code ec; + service_.use_certificate_file(impl_, filename, format, ec); + asio::detail::throw_error(ec); + } + + /// Use a certificate from a file. + /** + * This function is used to load a certificate into the context from a file. + * + * @param filename The name of the file containing the certificate. + * + * @param format The file format (ASN.1 or PEM). + * + * @param ec Set to indicate what error occurred, if any. + */ + asio::error_code use_certificate_file(const std::string& filename, + file_format format, asio::error_code& ec) + { + return service_.use_certificate_file(impl_, filename, format, ec); + } + + /// Use a certificate chain from a file. + /** + * This function is used to load a certificate chain into the context from a + * file. + * + * @param filename The name of the file containing the certificate. The file + * must use the PEM format. + * + * @throws asio::system_error Thrown on failure. + */ + void use_certificate_chain_file(const std::string& filename) + { + asio::error_code ec; + service_.use_certificate_chain_file(impl_, filename, ec); + asio::detail::throw_error(ec); + } + + /// Use a certificate chain from a file. + /** + * This function is used to load a certificate chain into the context from a + * file. + * + * @param filename The name of the file containing the certificate. The file + * must use the PEM format. + * + * @param ec Set to indicate what error occurred, if any. + */ + asio::error_code use_certificate_chain_file( + const std::string& filename, asio::error_code& ec) + { + return service_.use_certificate_chain_file(impl_, filename, ec); + } + + /// Use a private key from a file. + /** + * This function is used to load a private key into the context from a file. + * + * @param filename The name of the file containing the private key. + * + * @param format The file format (ASN.1 or PEM). + * + * @throws asio::system_error Thrown on failure. + */ + void use_private_key_file(const std::string& filename, file_format format) + { + asio::error_code ec; + service_.use_private_key_file(impl_, filename, format, ec); + asio::detail::throw_error(ec); + } + + /// Use a private key from a file. + /** + * This function is used to load a private key into the context from a file. + * + * @param filename The name of the file containing the private key. + * + * @param format The file format (ASN.1 or PEM). + * + * @param ec Set to indicate what error occurred, if any. + */ + asio::error_code use_private_key_file(const std::string& filename, + file_format format, asio::error_code& ec) + { + return service_.use_private_key_file(impl_, filename, format, ec); + } + + /// Use an RSA private key from a file. + /** + * This function is used to load an RSA private key into the context from a + * file. + * + * @param filename The name of the file containing the RSA private key. + * + * @param format The file format (ASN.1 or PEM). + * + * @throws asio::system_error Thrown on failure. + */ + void use_rsa_private_key_file(const std::string& filename, file_format format) + { + asio::error_code ec; + service_.use_rsa_private_key_file(impl_, filename, format, ec); + asio::detail::throw_error(ec); + } + + /// Use an RSA private key from a file. + /** + * This function is used to load an RSA private key into the context from a + * file. + * + * @param filename The name of the file containing the RSA private key. + * + * @param format The file format (ASN.1 or PEM). + * + * @param ec Set to indicate what error occurred, if any. + */ + asio::error_code use_rsa_private_key_file( + const std::string& filename, file_format format, + asio::error_code& ec) + { + return service_.use_rsa_private_key_file(impl_, filename, format, ec); + } + + /// Use the specified file to obtain the temporary Diffie-Hellman parameters. + /** + * This function is used to load Diffie-Hellman parameters into the context + * from a file. + * + * @param filename The name of the file containing the Diffie-Hellman + * parameters. The file must use the PEM format. + * + * @throws asio::system_error Thrown on failure. + */ + void use_tmp_dh_file(const std::string& filename) + { + asio::error_code ec; + service_.use_tmp_dh_file(impl_, filename, ec); + asio::detail::throw_error(ec); + } + + /// Use the specified file to obtain the temporary Diffie-Hellman parameters. + /** + * This function is used to load Diffie-Hellman parameters into the context + * from a file. + * + * @param filename The name of the file containing the Diffie-Hellman + * parameters. The file must use the PEM format. + * + * @param ec Set to indicate what error occurred, if any. + */ + asio::error_code use_tmp_dh_file(const std::string& filename, + asio::error_code& ec) + { + return service_.use_tmp_dh_file(impl_, filename, ec); + } + + /// Set the password callback. + /** + * This function is used to specify a callback function to obtain password + * information about an encrypted key in PEM format. + * + * @param callback The function object to be used for obtaining the password. + * The function signature of the handler must be: + * @code std::string password_callback( + * std::size_t max_length, // The maximum size for a password. + * password_purpose purpose // Whether password is for reading or writing. + * ); @endcode + * The return value of the callback is a string containing the password. + * + * @throws asio::system_error Thrown on failure. + */ + template + void set_password_callback(PasswordCallback callback) + { + asio::error_code ec; + service_.set_password_callback(impl_, callback, ec); + asio::detail::throw_error(ec); + } + + /// Set the password callback. + /** + * This function is used to specify a callback function to obtain password + * information about an encrypted key in PEM format. + * + * @param callback The function object to be used for obtaining the password. + * The function signature of the handler must be: + * @code std::string password_callback( + * std::size_t max_length, // The maximum size for a password. + * password_purpose purpose // Whether password is for reading or writing. + * ); @endcode + * The return value of the callback is a string containing the password. + * + * @param ec Set to indicate what error occurred, if any. + */ + template + asio::error_code set_password_callback(PasswordCallback callback, + asio::error_code& ec) + { + return service_.set_password_callback(impl_, callback, ec); + } + +private: + /// The backend service implementation. + service_type& service_; + + /// The underlying native implementation. + impl_type impl_; +}; + +} // namespace ssl +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_SSL_BASIC_CONTEXT_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/ssl/context.hpp b/encryption/libtorrent/include/libtorrent/asio/ssl/context.hpp new file mode 100644 index 000000000..86e249cbc --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/ssl/context.hpp @@ -0,0 +1,35 @@ +// +// context.hpp +// ~~~~~~~~~~~ +// +// Copyright (c) 2005 Voipster / Indrek dot Juhani at voipster dot com +// Copyright (c) 2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_SSL_CONTEXT_HPP +#define ASIO_SSL_CONTEXT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/ssl/basic_context.hpp" +#include "asio/ssl/context_service.hpp" + +namespace asio { +namespace ssl { + +/// Typedef for the typical usage of context. +typedef basic_context context; + +} // namespace ssl +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_SSL_CONTEXT_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/ssl/context_base.hpp b/encryption/libtorrent/include/libtorrent/asio/ssl/context_base.hpp new file mode 100755 index 000000000..0811d8ba5 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/ssl/context_base.hpp @@ -0,0 +1,164 @@ +// +// context_base.hpp +// ~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_SSL_CONTEXT_BASE_HPP +#define ASIO_SSL_CONTEXT_BASE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/ssl/detail/openssl_types.hpp" + +namespace asio { +namespace ssl { + +/// The context_base class is used as a base for the basic_context class +/// template so that we have a common place to define various enums. +class context_base +{ +public: + /// Different methods supported by a context. + enum method + { + /// Generic SSL version 2. + sslv2, + + /// SSL version 2 client. + sslv2_client, + + /// SSL version 2 server. + sslv2_server, + + /// Generic SSL version 3. + sslv3, + + /// SSL version 3 client. + sslv3_client, + + /// SSL version 3 server. + sslv3_server, + + /// Generic TLS version 1. + tlsv1, + + /// TLS version 1 client. + tlsv1_client, + + /// TLS version 1 server. + tlsv1_server, + + /// Generic SSL/TLS. + sslv23, + + /// SSL/TLS client. + sslv23_client, + + /// SSL/TLS server. + sslv23_server + }; + + /// Bitmask type for SSL options. + typedef int options; + +#if defined(GENERATING_DOCUMENTATION) + /// Implement various bug workarounds. + static const int default_workarounds = implementation_defined; + + /// Always create a new key when using tmp_dh parameters. + static const int single_dh_use = implementation_defined; + + /// Disable SSL v2. + static const int no_sslv2 = implementation_defined; + + /// Disable SSL v3. + static const int no_sslv3 = implementation_defined; + + /// Disable TLS v1. + static const int no_tlsv1 = implementation_defined; +#else + BOOST_STATIC_CONSTANT(int, default_workarounds = SSL_OP_ALL); + BOOST_STATIC_CONSTANT(int, single_dh_use = SSL_OP_SINGLE_DH_USE); + BOOST_STATIC_CONSTANT(int, no_sslv2 = SSL_OP_NO_SSLv2); + BOOST_STATIC_CONSTANT(int, no_sslv3 = SSL_OP_NO_SSLv3); + BOOST_STATIC_CONSTANT(int, no_tlsv1 = SSL_OP_NO_TLSv1); +#endif + + /// File format types. + enum file_format + { + /// ASN.1 file. + asn1, + + /// PEM file. + pem + }; + + /// Bitmask type for peer verification. + typedef int verify_mode; + +#if defined(GENERATING_DOCUMENTATION) + /// No verification. + static const int verify_none = implementation_defined; + + /// Verify the peer. + static const int verify_peer = implementation_defined; + + /// Fail verification if the peer has no certificate. Ignored unless + /// verify_peer is set. + static const int verify_fail_if_no_peer_cert = implementation_defined; + + /// Do not request client certificate on renegotiation. Ignored unless + /// verify_peer is set. + static const int verify_client_once = implementation_defined; +#else + BOOST_STATIC_CONSTANT(int, verify_none = SSL_VERIFY_NONE); + BOOST_STATIC_CONSTANT(int, verify_peer = SSL_VERIFY_PEER); + BOOST_STATIC_CONSTANT(int, + verify_fail_if_no_peer_cert = SSL_VERIFY_FAIL_IF_NO_PEER_CERT); + BOOST_STATIC_CONSTANT(int, verify_client_once = SSL_VERIFY_CLIENT_ONCE); +#endif + + /// Purpose of PEM password. + enum password_purpose + { + /// The password is needed for reading/decryption. + for_reading, + + /// The password is needed for writing/encryption. + for_writing + }; + +protected: + /// Protected destructor to prevent deletion through this type. + ~context_base() + { + } + +#if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564)) +private: + // Workaround to enable the empty base optimisation with Borland C++. + char dummy_; +#endif +}; + +} // namespace ssl +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_SSL_CONTEXT_BASE_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/ssl/context_service.hpp b/encryption/libtorrent/include/libtorrent/asio/ssl/context_service.hpp new file mode 100755 index 000000000..f0698a58f --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/ssl/context_service.hpp @@ -0,0 +1,175 @@ +// +// context_service.hpp +// ~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2005 Voipster / Indrek dot Juhani at voipster dot com +// Copyright (c) 2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_SSL_CONTEXT_SERVICE_HPP +#define ASIO_SSL_CONTEXT_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/error.hpp" +#include "asio/io_service.hpp" +#include "asio/detail/service_base.hpp" +#include "asio/ssl/context_base.hpp" +#include "asio/ssl/detail/openssl_context_service.hpp" + +namespace asio { +namespace ssl { + +/// Default service implementation for a context. +class context_service +#if defined(GENERATING_DOCUMENTATION) + : public asio::io_service::service +#else + : public asio::detail::service_base +#endif +{ +private: + // The type of the platform-specific implementation. + typedef detail::openssl_context_service service_impl_type; + +public: +#if defined(GENERATING_DOCUMENTATION) + /// The unique service identifier. + static asio::io_service::id id; +#endif + + /// The type of the context. +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined impl_type; +#else + typedef service_impl_type::impl_type impl_type; +#endif + + /// Constructor. + explicit context_service(asio::io_service& io_service) + : asio::detail::service_base(io_service), + service_impl_(asio::use_service(io_service)) + { + } + + /// Destroy all user-defined handler objects owned by the service. + void shutdown_service() + { + } + + /// Return a null context implementation. + impl_type null() const + { + return service_impl_.null(); + } + + /// Create a new context implementation. + void create(impl_type& impl, context_base::method m) + { + service_impl_.create(impl, m); + } + + /// Destroy a context implementation. + void destroy(impl_type& impl) + { + service_impl_.destroy(impl); + } + + /// Set options on the context. + asio::error_code set_options(impl_type& impl, + context_base::options o, asio::error_code& ec) + { + return service_impl_.set_options(impl, o, ec); + } + + /// Set peer verification mode. + asio::error_code set_verify_mode(impl_type& impl, + context_base::verify_mode v, asio::error_code& ec) + { + return service_impl_.set_verify_mode(impl, v, ec); + } + + /// Load a certification authority file for performing verification. + asio::error_code load_verify_file(impl_type& impl, + const std::string& filename, asio::error_code& ec) + { + return service_impl_.load_verify_file(impl, filename, ec); + } + + /// Add a directory containing certification authority files to be used for + /// performing verification. + asio::error_code add_verify_path(impl_type& impl, + const std::string& path, asio::error_code& ec) + { + return service_impl_.add_verify_path(impl, path, ec); + } + + /// Use a certificate from a file. + asio::error_code use_certificate_file(impl_type& impl, + const std::string& filename, context_base::file_format format, + asio::error_code& ec) + { + return service_impl_.use_certificate_file(impl, filename, format, ec); + } + + /// Use a certificate chain from a file. + asio::error_code use_certificate_chain_file(impl_type& impl, + const std::string& filename, asio::error_code& ec) + { + return service_impl_.use_certificate_chain_file(impl, filename, ec); + } + + /// Use a private key from a file. + asio::error_code use_private_key_file(impl_type& impl, + const std::string& filename, context_base::file_format format, + asio::error_code& ec) + { + return service_impl_.use_private_key_file(impl, filename, format, ec); + } + + /// Use an RSA private key from a file. + asio::error_code use_rsa_private_key_file(impl_type& impl, + const std::string& filename, context_base::file_format format, + asio::error_code& ec) + { + return service_impl_.use_rsa_private_key_file(impl, filename, format, ec); + } + + /// Use the specified file to obtain the temporary Diffie-Hellman parameters. + asio::error_code use_tmp_dh_file(impl_type& impl, + const std::string& filename, asio::error_code& ec) + { + return service_impl_.use_tmp_dh_file(impl, filename, ec); + } + + /// Set the password callback. + template + asio::error_code set_password_callback(impl_type& impl, + PasswordCallback callback, asio::error_code& ec) + { + return service_impl_.set_password_callback(impl, callback, ec); + } + +private: + // The service that provides the platform-specific implementation. + service_impl_type& service_impl_; +}; + +} // namespace ssl +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_SSL_CONTEXT_SERVICE_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/ssl/detail/openssl_context_service.hpp b/encryption/libtorrent/include/libtorrent/asio/ssl/detail/openssl_context_service.hpp new file mode 100755 index 000000000..96d610801 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/ssl/detail/openssl_context_service.hpp @@ -0,0 +1,379 @@ +// +// openssl_context_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2005 Voipster / Indrek dot Juhani at voipster dot com +// Copyright (c) 2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_SSL_DETAIL_OPENSSL_CONTEXT_SERVICE_HPP +#define ASIO_SSL_DETAIL_OPENSSL_CONTEXT_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/error.hpp" +#include "asio/io_service.hpp" +#include "asio/detail/service_base.hpp" +#include "asio/ssl/context_base.hpp" +#include "asio/ssl/detail/openssl_init.hpp" +#include "asio/ssl/detail/openssl_types.hpp" + +namespace asio { +namespace ssl { +namespace detail { + +class openssl_context_service + : public asio::detail::service_base +{ +public: + // The native type of the context. + typedef ::SSL_CTX* impl_type; + + // The type for the password callback function object. + typedef boost::function password_callback_type; + + // Constructor. + openssl_context_service(asio::io_service& io_service) + : asio::detail::service_base(io_service) + { + } + + // Destroy all user-defined handler objects owned by the service. + void shutdown_service() + { + } + + // Return a null context implementation. + static impl_type null() + { + return 0; + } + + // Create a new context implementation. + void create(impl_type& impl, context_base::method m) + { + ::SSL_METHOD* ssl_method = 0; + switch (m) + { + case context_base::sslv2: + ssl_method = ::SSLv2_method(); + break; + case context_base::sslv2_client: + ssl_method = ::SSLv2_client_method(); + break; + case context_base::sslv2_server: + ssl_method = ::SSLv2_server_method(); + break; + case context_base::sslv3: + ssl_method = ::SSLv3_method(); + break; + case context_base::sslv3_client: + ssl_method = ::SSLv3_client_method(); + break; + case context_base::sslv3_server: + ssl_method = ::SSLv3_server_method(); + break; + case context_base::tlsv1: + ssl_method = ::TLSv1_method(); + break; + case context_base::tlsv1_client: + ssl_method = ::TLSv1_client_method(); + break; + case context_base::tlsv1_server: + ssl_method = ::TLSv1_server_method(); + break; + case context_base::sslv23: + ssl_method = ::SSLv23_method(); + break; + case context_base::sslv23_client: + ssl_method = ::SSLv23_client_method(); + break; + case context_base::sslv23_server: + ssl_method = ::SSLv23_server_method(); + break; + default: + break; + } + impl = ::SSL_CTX_new(ssl_method); + } + + // Destroy a context implementation. + void destroy(impl_type& impl) + { + if (impl != null()) + { + if (impl->default_passwd_callback_userdata) + { + password_callback_type* callback = + static_cast( + impl->default_passwd_callback_userdata); + delete callback; + impl->default_passwd_callback_userdata = 0; + } + + ::SSL_CTX_free(impl); + impl = null(); + } + } + + // Set options on the context. + asio::error_code set_options(impl_type& impl, + context_base::options o, asio::error_code& ec) + { + ::SSL_CTX_set_options(impl, o); + + ec = asio::error_code(); + return ec; + } + + // Set peer verification mode. + asio::error_code set_verify_mode(impl_type& impl, + context_base::verify_mode v, asio::error_code& ec) + { + ::SSL_CTX_set_verify(impl, v, 0); + + ec = asio::error_code(); + return ec; + } + + // Load a certification authority file for performing verification. + asio::error_code load_verify_file(impl_type& impl, + const std::string& filename, asio::error_code& ec) + { + if (::SSL_CTX_load_verify_locations(impl, filename.c_str(), 0) != 1) + { + ec = asio::error::invalid_argument; + return ec; + } + + ec = asio::error_code(); + return ec; + } + + // Add a directory containing certification authority files to be used for + // performing verification. + asio::error_code add_verify_path(impl_type& impl, + const std::string& path, asio::error_code& ec) + { + if (::SSL_CTX_load_verify_locations(impl, 0, path.c_str()) != 1) + { + ec = asio::error::invalid_argument; + return ec; + } + + ec = asio::error_code(); + return ec; + } + + // Use a certificate from a file. + asio::error_code use_certificate_file(impl_type& impl, + const std::string& filename, context_base::file_format format, + asio::error_code& ec) + { + int file_type; + switch (format) + { + case context_base::asn1: + file_type = SSL_FILETYPE_ASN1; + break; + case context_base::pem: + file_type = SSL_FILETYPE_PEM; + break; + default: + { + ec = asio::error::invalid_argument; + return ec; + } + } + + if (::SSL_CTX_use_certificate_file(impl, filename.c_str(), file_type) != 1) + { + ec = asio::error::invalid_argument; + return ec; + } + + ec = asio::error_code(); + return ec; + } + + // Use a certificate chain from a file. + asio::error_code use_certificate_chain_file(impl_type& impl, + const std::string& filename, asio::error_code& ec) + { + if (::SSL_CTX_use_certificate_chain_file(impl, filename.c_str()) != 1) + { + ec = asio::error::invalid_argument; + return ec; + } + + ec = asio::error_code(); + return ec; + } + + // Use a private key from a file. + asio::error_code use_private_key_file(impl_type& impl, + const std::string& filename, context_base::file_format format, + asio::error_code& ec) + { + int file_type; + switch (format) + { + case context_base::asn1: + file_type = SSL_FILETYPE_ASN1; + break; + case context_base::pem: + file_type = SSL_FILETYPE_PEM; + break; + default: + { + ec = asio::error::invalid_argument; + return ec; + } + } + + if (::SSL_CTX_use_PrivateKey_file(impl, filename.c_str(), file_type) != 1) + { + ec = asio::error::invalid_argument; + return ec; + } + + ec = asio::error_code(); + return ec; + } + + // Use an RSA private key from a file. + asio::error_code use_rsa_private_key_file(impl_type& impl, + const std::string& filename, context_base::file_format format, + asio::error_code& ec) + { + int file_type; + switch (format) + { + case context_base::asn1: + file_type = SSL_FILETYPE_ASN1; + break; + case context_base::pem: + file_type = SSL_FILETYPE_PEM; + break; + default: + { + ec = asio::error::invalid_argument; + return ec; + } + } + + if (::SSL_CTX_use_RSAPrivateKey_file( + impl, filename.c_str(), file_type) != 1) + { + ec = asio::error::invalid_argument; + return ec; + } + + ec = asio::error_code(); + return ec; + } + + // Use the specified file to obtain the temporary Diffie-Hellman parameters. + asio::error_code use_tmp_dh_file(impl_type& impl, + const std::string& filename, asio::error_code& ec) + { + ::BIO* bio = ::BIO_new_file(filename.c_str(), "r"); + if (!bio) + { + ec = asio::error::invalid_argument; + return ec; + } + + ::DH* dh = ::PEM_read_bio_DHparams(bio, 0, 0, 0); + if (!dh) + { + ::BIO_free(bio); + ec = asio::error::invalid_argument; + return ec; + } + + ::BIO_free(bio); + int result = ::SSL_CTX_set_tmp_dh(impl, dh); + if (result != 1) + { + ::DH_free(dh); + ec = asio::error::invalid_argument; + return ec; + } + + ec = asio::error_code(); + return ec; + } + + static int password_callback(char* buf, int size, int purpose, void* data) + { + using namespace std; // For strncat and strlen. + + if (data) + { + password_callback_type* callback = + static_cast(data); + std::string passwd = (*callback)(static_cast(size), + purpose ? context_base::for_writing : context_base::for_reading); + *buf = '\0'; + strncat(buf, passwd.c_str(), size); + return strlen(buf); + } + + return 0; + } + + // Set the password callback. + template + asio::error_code set_password_callback(impl_type& impl, + Password_Callback callback, asio::error_code& ec) + { + // Allocate callback function object if not already present. + if (impl->default_passwd_callback_userdata) + { + password_callback_type* callback_function = + static_cast( + impl->default_passwd_callback_userdata); + *callback_function = callback; + } + else + { + password_callback_type* callback_function = + new password_callback_type(callback); + impl->default_passwd_callback_userdata = callback_function; + } + + // Set the password callback. + SSL_CTX_set_default_passwd_cb(impl, + &openssl_context_service::password_callback); + + ec = asio::error_code(); + return ec; + } + +private: + // Ensure openssl is initialised. + openssl_init<> init_; +}; + +} // namespace detail +} // namespace ssl +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_SSL_DETAIL_OPENSSL_CONTEXT_SERVICE_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/ssl/detail/openssl_init.hpp b/encryption/libtorrent/include/libtorrent/asio/ssl/detail/openssl_init.hpp new file mode 100755 index 000000000..06fbf86fe --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/ssl/detail/openssl_init.hpp @@ -0,0 +1,127 @@ +// +// openssl_init.hpp +// ~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2005 Voipster / Indrek dot Juhani at voipster dot com +// Copyright (c) 2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_SSL_DETAIL_OPENSSL_INIT_HPP +#define ASIO_SSL_DETAIL_OPENSSL_INIT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/detail/mutex.hpp" +#include "asio/ssl/detail/openssl_types.hpp" + +namespace asio { +namespace ssl { +namespace detail { + +template +class openssl_init + : private boost::noncopyable +{ +private: + // Structure to perform the actual initialisation. + class do_init + { + public: + do_init() + { + if (Do_Init) + { + ::SSL_library_init(); + ::SSL_load_error_strings(); + ::OpenSSL_add_ssl_algorithms(); + + mutexes_.resize(::CRYPTO_num_locks()); + for (size_t i = 0; i < mutexes_.size(); ++i) + mutexes_[i].reset(new asio::detail::mutex); + ::CRYPTO_set_locking_callback(&do_init::openssl_locking_func); + } + } + + ~do_init() + { + if (Do_Init) + { + ::CRYPTO_set_locking_callback(0); + ::ERR_free_strings(); + ::ERR_remove_state(0); + ::EVP_cleanup(); + ::CRYPTO_cleanup_all_ex_data(); + ::CONF_modules_unload(1); + ::ENGINE_cleanup(); + } + } + + // Helper function to manage a do_init singleton. The static instance of the + // openssl_init object ensures that this function is always called before + // main, and therefore before any other threads can get started. The do_init + // instance must be static in this function to ensure that it gets + // initialised before any other global objects try to use it. + static boost::shared_ptr instance() + { + static boost::shared_ptr init(new do_init); + return init; + } + + private: + static void openssl_locking_func(int mode, int n, + const char *file, int line) + { + if (mode & CRYPTO_LOCK) + instance()->mutexes_[n]->lock(); + else + instance()->mutexes_[n]->unlock(); + } + + // Mutexes to be used in locking callbacks. + std::vector > mutexes_; + }; + +public: + // Constructor. + openssl_init() + : ref_(do_init::instance()) + { + while (&instance_ == 0); // Ensure openssl_init::instance_ is linked in. + } + + // Destructor. + ~openssl_init() + { + } + +private: + // Instance to force initialisation of openssl at global scope. + static openssl_init instance_; + + // Reference to singleton do_init object to ensure that openssl does not get + // cleaned up until the last user has finished with it. + boost::shared_ptr ref_; +}; + +template +openssl_init openssl_init::instance_; + +} // namespace detail +} // namespace ssl +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_SSL_DETAIL_OPENSSL_INIT_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/ssl/detail/openssl_operation.hpp b/encryption/libtorrent/include/libtorrent/asio/ssl/detail/openssl_operation.hpp new file mode 100755 index 000000000..b7a564464 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/ssl/detail/openssl_operation.hpp @@ -0,0 +1,482 @@ +// +// openssl_operation.hpp +// ~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2005 Voipster / Indrek dot Juhani at voipster dot com +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_SSL_DETAIL_OPENSSL_OPERATION_HPP +#define ASIO_SSL_DETAIL_OPENSSL_OPERATION_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/buffer.hpp" +#include "asio/placeholders.hpp" +#include "asio/write.hpp" +#include "asio/detail/socket_ops.hpp" +#include "asio/ssl/detail/openssl_types.hpp" + +namespace asio { +namespace ssl { +namespace detail { + +typedef boost::function ssl_primitive_func; +typedef boost::function + user_handler_func; + +// Network send_/recv buffer implementation +// +// +class net_buffer +{ + static const int NET_BUF_SIZE = 16*1024 + 256; // SSL record size + spare + + unsigned char buf_[NET_BUF_SIZE]; + unsigned char* data_start_; + unsigned char* data_end_; + +public: + net_buffer() + { + data_start_ = data_end_ = buf_; + } + unsigned char* get_unused_start() { return data_end_; } + unsigned char* get_data_start() { return data_start_; } + size_t get_unused_len() { return (NET_BUF_SIZE - (data_end_ - buf_)); } + size_t get_data_len() { return (data_end_ - data_start_); } + void data_added(size_t count) + { + data_end_ += count; + data_end_ = data_end_ > (buf_ + NET_BUF_SIZE)? + (buf_ + NET_BUF_SIZE): + data_end_; + } + void data_removed(size_t count) + { + data_start_ += count; + if (data_start_ >= data_end_) reset(); + } + void reset() { data_start_ = buf_; data_end_ = buf_; } + bool has_data() { return (data_start_ < data_end_); } +}; // class net_buffer + +// +// Operation class +// +// +template +class openssl_operation +{ +public: + + // Constructor for asynchronous operations + openssl_operation(ssl_primitive_func primitive, + Stream& socket, + net_buffer& recv_buf, + SSL* session, + BIO* ssl_bio, + user_handler_func handler + ) + : primitive_(primitive) + , user_handler_(handler) + , recv_buf_(recv_buf) + , socket_(socket) + , ssl_bio_(ssl_bio) + , session_(session) + { + write_ = boost::bind( + &openssl_operation::do_async_write, + this, boost::arg<1>(), boost::arg<2>() + ); + handler_= boost::bind( + &openssl_operation::async_user_handler, + this, boost::arg<1>(), boost::arg<2>() + ); + } + + // Constructor for synchronous operations + openssl_operation(ssl_primitive_func primitive, + Stream& socket, + net_buffer& recv_buf, + SSL* session, + BIO* ssl_bio) + : primitive_(primitive) + , recv_buf_(recv_buf) + , socket_(socket) + , ssl_bio_(ssl_bio) + , session_(session) + { + write_ = boost::bind( + &openssl_operation::do_sync_write, + this, boost::arg<1>(), boost::arg<2>() + ); + handler_ = boost::bind( + &openssl_operation::sync_user_handler, + this, boost::arg<1>(), boost::arg<2>() + ); + } + + // Start operation + // In case of asynchronous it returns 0, in sync mode returns success code + // or throws an error... + int start() + { + int rc = primitive_( session_ ); + int sys_error_code = ERR_get_error(); + bool is_operation_done = (rc > 0); + // For connect/accept/shutdown, the operation + // is done, when return code is 1 + // for write, it is done, when is retcode > 0 + // for read, is is done when retcode > 0 + + int error_code = !is_operation_done ? + ::SSL_get_error( session_, rc ) : + 0; + bool is_read_needed = (error_code == SSL_ERROR_WANT_READ); + bool is_write_needed = (error_code == SSL_ERROR_WANT_WRITE || + ::BIO_ctrl_pending( ssl_bio_ )); + bool is_shut_down_received = + ((::SSL_get_shutdown( session_ ) & SSL_RECEIVED_SHUTDOWN) == + SSL_RECEIVED_SHUTDOWN); + bool is_shut_down_sent = + ((::SSL_get_shutdown( session_ ) & SSL_SENT_SHUTDOWN) == + SSL_SENT_SHUTDOWN); + + if (is_shut_down_sent && is_shut_down_received && is_operation_done) + // SSL connection is shut down cleanly + return handler_(asio::error_code(), 1); + + if (is_shut_down_received && !is_write_needed) + return handler_(asio::error::eof, 0); + + if (is_shut_down_received) + // Shutdown has been requested, while we were reading or writing... + // abort our action... + return handler_(asio::error::shut_down, 0); + + if (!is_operation_done && !is_read_needed && !is_write_needed + && !is_shut_down_sent) + { + // The operation has failed... It is not completed and does + // not want network communication nor does want to send shutdown out... + if (error_code == SSL_ERROR_SYSCALL) + { + return handler_(asio::error_code( + sys_error_code, asio::native_ecat), rc); + } + else + { + return handler_(asio::error_code( + error_code, asio::ssl_ecat), rc); + } + } + + if (!is_operation_done && !is_write_needed) + { + // We may have left over data that we can pass to SSL immediately + if (recv_buf_.get_data_len() > 0) + { + // Pass the buffered data to SSL + int written = ::BIO_write + ( + ssl_bio_, + recv_buf_.get_data_start(), + recv_buf_.get_data_len() + ); + + if (written > 0) + { + recv_buf_.data_removed(written); + } + else if (written < 0) + { + if (!BIO_should_retry(ssl_bio_)) + { + // Some serios error with BIO.... + return handler_(asio::error::no_recovery, 0); + } + } + + return start(); + } + } + + // Continue with operation, flush any SSL data out to network... + return write_(is_operation_done, rc); + } + +// Private implementation +private: + typedef boost::function + int_handler_func; + typedef boost::function write_func; + + ssl_primitive_func primitive_; + user_handler_func user_handler_; + write_func write_; + int_handler_func handler_; + + net_buffer send_buf_; // buffers for network IO + + // The recv buffer is owned by the stream, not the operation, since there can + // be left over bytes after passing the data up to the application, and these + // bytes need to be kept around for the next read operation issued by the + // application. + net_buffer& recv_buf_; + + Stream& socket_; + BIO* ssl_bio_; + SSL* session_; + + // + int sync_user_handler(const asio::error_code& error, int rc) + { + if (!error) + return rc; + + throw asio::system_error(error); + } + + int async_user_handler(const asio::error_code& error, int rc) + { + user_handler_(error, rc); + return 0; + } + + // Writes bytes asynchronously from SSL to NET + int do_async_write(bool is_operation_done, int rc) + { + int len = ::BIO_ctrl_pending( ssl_bio_ ); + if ( len ) + { + // There is something to write into net, do it... + len = (int)send_buf_.get_unused_len() > len? + len: + send_buf_.get_unused_len(); + + if (len == 0) + { + // In case our send buffer is full, we have just to wait until + // previous send to complete... + return 0; + } + + // Read outgoing data from bio + len = ::BIO_read( ssl_bio_, send_buf_.get_unused_start(), len); + + if (len > 0) + { + unsigned char *data_start = send_buf_.get_unused_start(); + send_buf_.data_added(len); + + asio::async_write + ( + socket_, + asio::buffer(data_start, len), + boost::bind + ( + &openssl_operation::async_write_handler, + this, + is_operation_done, + rc, + asio::placeholders::error, + asio::placeholders::bytes_transferred + ) + ); + + return 0; + } + else if (!BIO_should_retry(ssl_bio_)) + { + // Seems like fatal error + // reading from SSL BIO has failed... + handler_(asio::error::no_recovery, 0); + return 0; + } + } + + if (is_operation_done) + { + // Finish the operation, with success + handler_(asio::error_code(), rc); + return 0; + } + + // OPeration is not done and writing to net has been made... + // start reading... + do_async_read(); + + return 0; + } + + void async_write_handler(bool is_operation_done, int rc, + const asio::error_code& error, size_t bytes_sent) + { + if (!error) + { + // Remove data from send buffer + send_buf_.data_removed(bytes_sent); + + if (is_operation_done) + handler_(asio::error_code(), rc); + else + // Since the operation was not completed, try it again... + start(); + } + else + handler_(error, rc); + } + + void do_async_read() + { + // Wait for new data + socket_.async_read_some + ( + asio::buffer(recv_buf_.get_unused_start(), + recv_buf_.get_unused_len()), + boost::bind + ( + &openssl_operation::async_read_handler, + this, + asio::placeholders::error, + asio::placeholders::bytes_transferred + ) + ); + } + + void async_read_handler(const asio::error_code& error, + size_t bytes_recvd) + { + if (!error) + { + recv_buf_.data_added(bytes_recvd); + + // Pass the received data to SSL + int written = ::BIO_write + ( + ssl_bio_, + recv_buf_.get_data_start(), + recv_buf_.get_data_len() + ); + + if (written > 0) + { + recv_buf_.data_removed(written); + } + else if (written < 0) + { + if (!BIO_should_retry(ssl_bio_)) + { + // Some serios error with BIO.... + handler_(asio::error::no_recovery, 0); + return; + } + } + + // and try the SSL primitive again + start(); + } + else + { + // Error in network level... + // SSL can't continue either... + handler_(error, 0); + } + } + + // Syncronous functions... + int do_sync_write(bool is_operation_done, int rc) + { + int len = ::BIO_ctrl_pending( ssl_bio_ ); + if ( len ) + { + // There is something to write into net, do it... + len = (int)send_buf_.get_unused_len() > len? + len: + send_buf_.get_unused_len(); + + // Read outgoing data from bio + len = ::BIO_read( ssl_bio_, send_buf_.get_unused_start(), len); + + if (len > 0) + { + size_t sent_len = asio::write( + socket_, + asio::buffer(send_buf_.get_unused_start(), len) + ); + + send_buf_.data_added(len); + send_buf_.data_removed(sent_len); + } + else if (!BIO_should_retry(ssl_bio_)) + { + // Seems like fatal error + // reading from SSL BIO has failed... + throw asio::system_error(asio::error::no_recovery); + } + } + + if (is_operation_done) + // Finish the operation, with success + return rc; + + // Operation is not finished, read data from net... + return do_sync_read(); + } + + int do_sync_read() + { + size_t len = socket_.read_some + ( + asio::buffer(recv_buf_.get_unused_start(), + recv_buf_.get_unused_len()) + ); + + // Write data to ssl + recv_buf_.data_added(len); + + // Pass the received data to SSL + int written = ::BIO_write + ( + ssl_bio_, + recv_buf_.get_data_start(), + recv_buf_.get_data_len() + ); + + if (written > 0) + { + recv_buf_.data_removed(written); + } + else if (written < 0) + { + if (!BIO_should_retry(ssl_bio_)) + { + // Some serios error with BIO.... + throw asio::system_error(asio::error::no_recovery); + } + } + + // Try the operation again + return start(); + } +}; // class openssl_operation + +} // namespace detail +} // namespace ssl +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_SSL_DETAIL_OPENSSL_OPERATION_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/ssl/detail/openssl_stream_service.hpp b/encryption/libtorrent/include/libtorrent/asio/ssl/detail/openssl_stream_service.hpp new file mode 100644 index 000000000..d90b588ef --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/ssl/detail/openssl_stream_service.hpp @@ -0,0 +1,504 @@ +// +// stream_service.hpp +// ~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2005 Voipster / Indrek dot Juhani at voipster dot com +// Copyright (c) 2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_SSL_DETAIL_OPENSSL_STREAM_SERVICE_HPP +#define ASIO_SSL_DETAIL_OPENSSL_STREAM_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/error.hpp" +#include "asio/io_service.hpp" +#include "asio/detail/service_base.hpp" +#include "asio/ssl/basic_context.hpp" +#include "asio/ssl/stream_base.hpp" +#include "asio/ssl/detail/openssl_operation.hpp" +#include "asio/ssl/detail/openssl_types.hpp" + +namespace asio { +namespace ssl { +namespace detail { + +class openssl_stream_service + : public asio::detail::service_base +{ +private: + //Base handler for asyncrhonous operations + template + class base_handler + { + public: + typedef boost::function< + void (const asio::error_code&, size_t)> func_t; + + base_handler(asio::io_service& io_service) + : op_(NULL) + , io_service_(io_service) + , work_(io_service) + {} + + void do_func(const asio::error_code& error, size_t size) + { + func_(error, size); + } + + void set_operation(openssl_operation* op) { op_ = op; } + void set_func(func_t func) { func_ = func; } + + ~base_handler() + { + delete op_; + } + + private: + func_t func_; + openssl_operation* op_; + asio::io_service& io_service_; + asio::io_service::work work_; + }; // class base_handler + + // Handler for asynchronous IO (write/read) operations + template + class io_handler + : public base_handler + { + public: + io_handler(Handler handler, asio::io_service& io_service) + : base_handler(io_service) + , handler_(handler) + { + set_func(boost::bind( + &io_handler::handler_impl, + this, boost::arg<1>(), boost::arg<2>() )); + } + + private: + Handler handler_; + void handler_impl(const asio::error_code& error, size_t size) + { + handler_(error, size); + delete this; + } + }; // class io_handler + + // Handler for asyncrhonous handshake (connect, accept) functions + template + class handshake_handler + : public base_handler + { + public: + handshake_handler(Handler handler, asio::io_service& io_service) + : base_handler(io_service) + , handler_(handler) + { + set_func(boost::bind( + &handshake_handler::handler_impl, + this, boost::arg<1>(), boost::arg<2>() )); + } + + private: + Handler handler_; + void handler_impl(const asio::error_code& error, size_t) + { + handler_(error); + delete this; + } + + }; // class handshake_handler + + // Handler for asyncrhonous shutdown + template + class shutdown_handler + : public base_handler + { + public: + shutdown_handler(Handler handler, asio::io_service& io_service) + : base_handler(io_service), + handler_(handler) + { + set_func(boost::bind( + &shutdown_handler::handler_impl, + this, boost::arg<1>(), boost::arg<2>() )); + } + + private: + Handler handler_; + void handler_impl(const asio::error_code& error, size_t) + { + handler_(error); + delete this; + } + }; // class shutdown_handler + +public: + // The implementation type. + typedef struct impl_struct + { + ::SSL* ssl; + ::BIO* ext_bio; + net_buffer recv_buf; + } * impl_type; + + // Construct a new stream socket service for the specified io_service. + explicit openssl_stream_service(asio::io_service& io_service) + : asio::detail::service_base(io_service) + { + } + + // Destroy all user-defined handler objects owned by the service. + void shutdown_service() + { + } + + // Return a null stream implementation. + impl_type null() const + { + return 0; + } + + // Create a new stream implementation. + template + void create(impl_type& impl, Stream& next_layer, + basic_context& context) + { + impl = new impl_struct; + impl->ssl = ::SSL_new(context.impl()); + ::SSL_set_mode(impl->ssl, SSL_MODE_ENABLE_PARTIAL_WRITE); + ::SSL_set_mode(impl->ssl, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); + ::BIO* int_bio = 0; + impl->ext_bio = 0; + ::BIO_new_bio_pair(&int_bio, 8192, &impl->ext_bio, 8192); + ::SSL_set_bio(impl->ssl, int_bio, int_bio); + } + + // Destroy a stream implementation. + template + void destroy(impl_type& impl, Stream& next_layer) + { + if (impl != 0) + { + ::BIO_free(impl->ext_bio); + ::SSL_free(impl->ssl); + delete impl; + impl = 0; + } + } + + // Perform SSL handshaking. + template + asio::error_code handshake(impl_type& impl, Stream& next_layer, + stream_base::handshake_type type, asio::error_code& ec) + { + try + { + openssl_operation op( + type == stream_base::client ? + &ssl_wrap::SSL_connect: + &ssl_wrap::SSL_accept, + next_layer, + impl->recv_buf, + impl->ssl, + impl->ext_bio); + op.start(); + } + catch (asio::system_error& e) + { + ec = e.code(); + return ec; + } + + ec = asio::error_code(); + return ec; + } + + // Start an asynchronous SSL handshake. + template + void async_handshake(impl_type& impl, Stream& next_layer, + stream_base::handshake_type type, Handler handler) + { + typedef handshake_handler connect_handler; + + connect_handler* local_handler = + new connect_handler(handler, io_service()); + + openssl_operation* op = new openssl_operation + ( + type == stream_base::client ? + &ssl_wrap::SSL_connect: + &ssl_wrap::SSL_accept, + next_layer, + impl->recv_buf, + impl->ssl, + impl->ext_bio, + boost::bind + ( + &base_handler::do_func, + local_handler, + boost::arg<1>(), + boost::arg<2>() + ) + ); + local_handler->set_operation(op); + + io_service().post(boost::bind(&openssl_operation::start, op)); + } + + // Shut down SSL on the stream. + template + asio::error_code shutdown(impl_type& impl, Stream& next_layer, + asio::error_code& ec) + { + try + { + openssl_operation op( + &ssl_wrap::SSL_shutdown, + next_layer, + impl->recv_buf, + impl->ssl, + impl->ext_bio); + op.start(); + } + catch (asio::system_error& e) + { + ec = e.code(); + return ec; + } + + ec = asio::error_code(); + return ec; + } + + // Asynchronously shut down SSL on the stream. + template + void async_shutdown(impl_type& impl, Stream& next_layer, Handler handler) + { + typedef shutdown_handler disconnect_handler; + + disconnect_handler* local_handler = + new disconnect_handler(handler, io_service()); + + openssl_operation* op = new openssl_operation + ( + &ssl_wrap::SSL_shutdown, + next_layer, + impl->recv_buf, + impl->ssl, + impl->ext_bio, + boost::bind + ( + &base_handler::do_func, + local_handler, + boost::arg<1>(), + boost::arg<2>() + ) + ); + local_handler->set_operation(op); + + io_service().post(boost::bind(&openssl_operation::start, op)); + } + + // Write some data to the stream. + template + std::size_t write_some(impl_type& impl, Stream& next_layer, + const Const_Buffers& buffers, asio::error_code& ec) + { + size_t bytes_transferred = 0; + try + { + boost::function send_func = + boost::bind(&::SSL_write, boost::arg<1>(), + asio::buffer_cast(*buffers.begin()), + static_cast(asio::buffer_size(*buffers.begin()))); + openssl_operation op( + send_func, + next_layer, + impl->recv_buf, + impl->ssl, + impl->ext_bio + ); + bytes_transferred = static_cast(op.start()); + } + catch (asio::system_error& e) + { + ec = e.code(); + return 0; + } + + ec = asio::error_code(); + return bytes_transferred; + } + + // Start an asynchronous write. + template + void async_write_some(impl_type& impl, Stream& next_layer, + const Const_Buffers& buffers, Handler handler) + { + typedef io_handler send_handler; + + send_handler* local_handler = new send_handler(handler, io_service()); + + boost::function send_func = + boost::bind(&::SSL_write, boost::arg<1>(), + asio::buffer_cast(*buffers.begin()), + static_cast(asio::buffer_size(*buffers.begin()))); + + openssl_operation* op = new openssl_operation + ( + send_func, + next_layer, + impl->recv_buf, + impl->ssl, + impl->ext_bio, + boost::bind + ( + &base_handler::do_func, + local_handler, + boost::arg<1>(), + boost::arg<2>() + ) + ); + local_handler->set_operation(op); + + io_service().post(boost::bind(&openssl_operation::start, op)); + } + + // Read some data from the stream. + template + std::size_t read_some(impl_type& impl, Stream& next_layer, + const Mutable_Buffers& buffers, asio::error_code& ec) + { + size_t bytes_transferred = 0; + try + { + boost::function recv_func = + boost::bind(&::SSL_read, boost::arg<1>(), + asio::buffer_cast(*buffers.begin()), + asio::buffer_size(*buffers.begin())); + openssl_operation op(recv_func, + next_layer, + impl->recv_buf, + impl->ssl, + impl->ext_bio + ); + + bytes_transferred = static_cast(op.start()); + } + catch (asio::system_error& e) + { + ec = e.code(); + return 0; + } + + ec = asio::error_code(); + return bytes_transferred; + } + + // Start an asynchronous read. + template + void async_read_some(impl_type& impl, Stream& next_layer, + const Mutable_Buffers& buffers, Handler handler) + { + typedef io_handler recv_handler; + + recv_handler* local_handler = new recv_handler(handler, io_service()); + + boost::function recv_func = + boost::bind(&::SSL_read, boost::arg<1>(), + asio::buffer_cast(*buffers.begin()), + asio::buffer_size(*buffers.begin())); + + openssl_operation* op = new openssl_operation + ( + recv_func, + next_layer, + impl->recv_buf, + impl->ssl, + impl->ext_bio, + boost::bind + ( + &base_handler::do_func, + local_handler, + boost::arg<1>(), + boost::arg<2>() + ) + ); + local_handler->set_operation(op); + + io_service().post(boost::bind(&openssl_operation::start, op)); + } + + // Peek at the incoming data on the stream. + template + std::size_t peek(impl_type& impl, Stream& next_layer, + const Mutable_Buffers& buffers, asio::error_code& ec) + { + ec = asio::error_code(); + return 0; + } + + // Determine the amount of data that may be read without blocking. + template + std::size_t in_avail(impl_type& impl, Stream& next_layer, + asio::error_code& ec) + { + ec = asio::error_code(); + return 0; + } + +private: + typedef asio::detail::mutex mutex_type; + + template + struct ssl_wrap + { + static Mutex ssl_mutex_; + + static int SSL_accept(SSL *ssl) + { + typename Mutex::scoped_lock lock(ssl_mutex_); + return ::SSL_accept(ssl); + } + + static int SSL_connect(SSL *ssl) + { + typename Mutex::scoped_lock lock(ssl_mutex_); + return ::SSL_connect(ssl); + } + + static int SSL_shutdown(SSL *ssl) + { + typename Mutex::scoped_lock lock(ssl_mutex_); + return ::SSL_shutdown(ssl); + } + }; +}; + +template +Mutex openssl_stream_service::ssl_wrap::ssl_mutex_; + +} // namespace detail +} // namespace ssl +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_SSL_DETAIL_OPENSSL_STREAM_SERVICE_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/ssl/detail/openssl_types.hpp b/encryption/libtorrent/include/libtorrent/asio/ssl/detail/openssl_types.hpp new file mode 100755 index 000000000..d193b3a0c --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/ssl/detail/openssl_types.hpp @@ -0,0 +1,29 @@ +// +// openssl_types.hpp +// ~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_SSL_DETAIL_OPENSSL_TYPES_HPP +#define ASIO_SSL_DETAIL_OPENSSL_TYPES_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_SSL_DETAIL_OPENSSL_TYPES_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/ssl/stream.hpp b/encryption/libtorrent/include/libtorrent/asio/ssl/stream.hpp new file mode 100644 index 000000000..a6af16101 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/ssl/stream.hpp @@ -0,0 +1,490 @@ +// +// stream.hpp +// ~~~~~~~~~~ +// +// Copyright (c) 2005 Voipster / Indrek dot Juhani at voipster dot com +// Copyright (c) 2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_SSL_STREAM_HPP +#define ASIO_SSL_STREAM_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/error.hpp" +#include "asio/ssl/basic_context.hpp" +#include "asio/ssl/stream_base.hpp" +#include "asio/ssl/stream_service.hpp" +#include "asio/detail/throw_error.hpp" + +namespace asio { +namespace ssl { + +/// Provides stream-oriented functionality using SSL. +/** + * The stream class template provides asynchronous and blocking stream-oriented + * functionality using SSL. + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + * + * @par Example + * To use the SSL stream template with a stream_socket, you would write: + * @code + * asio::io_service io_service; + * asio::ssl::context context(io_service, asio::ssl::context::sslv23); + * asio::ssl::stream sock(io_service, context); + * @endcode + * + * @par Concepts: + * Async_Object, Async_Read_Stream, Async_Write_Stream, Error_Source, Stream, + * Sync_Read_Stream, Sync_Write_Stream. + */ +template +class stream + : public stream_base, + private boost::noncopyable +{ +public: + /// The type of the next layer. + typedef typename boost::remove_reference::type next_layer_type; + + /// The type of the lowest layer. + typedef typename next_layer_type::lowest_layer_type lowest_layer_type; + + /// The type of the service that will be used to provide stream operations. + typedef Service service_type; + + /// The native implementation type of the stream. + typedef typename service_type::impl_type impl_type; + + /// Construct a stream. + /** + * This constructor creates a stream and initialises the underlying stream + * object. + * + * @param arg The argument to be passed to initialise the underlying stream. + * + * @param context The SSL context to be used for the stream. + */ + template + explicit stream(Arg& arg, basic_context& context) + : next_layer_(arg), + service_(asio::use_service(next_layer_.io_service())), + impl_(service_.null()) + { + service_.create(impl_, next_layer_, context); + } + + /// Destructor. + ~stream() + { + service_.destroy(impl_, next_layer_); + } + + /// Get the io_service associated with the object. + /** + * This function may be used to obtain the io_service object that the stream + * uses to dispatch handlers for asynchronous operations. + * + * @return A reference to the io_service object that stream will use to + * dispatch handlers. Ownership is not transferred to the caller. + */ + asio::io_service& io_service() + { + return next_layer_.io_service(); + } + + /// Get a reference to the next layer. + /** + * This function returns a reference to the next layer in a stack of stream + * layers. + * + * @return A reference to the next layer in the stack of stream layers. + * Ownership is not transferred to the caller. + */ + next_layer_type& next_layer() + { + return next_layer_; + } + + /// Get a reference to the lowest layer. + /** + * This function returns a reference to the lowest layer in a stack of + * stream layers. + * + * @return A reference to the lowest layer in the stack of stream layers. + * Ownership is not transferred to the caller. + */ + lowest_layer_type& lowest_layer() + { + return next_layer_.lowest_layer(); + } + + /// Get the underlying implementation in the native type. + /** + * This function may be used to obtain the underlying implementation of the + * context. This is intended to allow access to stream functionality that is + * not otherwise provided. + */ + impl_type impl() + { + return impl_; + } + + /// Perform SSL handshaking. + /** + * This function is used to perform SSL handshaking on the stream. The + * function call will block until handshaking is complete or an error occurs. + * + * @param type The type of handshaking to be performed, i.e. as a client or as + * a server. + * + * @throws asio::system_error Thrown on failure. + */ + void handshake(handshake_type type) + { + asio::error_code ec; + service_.handshake(impl_, next_layer_, type, ec); + asio::detail::throw_error(ec); + } + + /// Perform SSL handshaking. + /** + * This function is used to perform SSL handshaking on the stream. The + * function call will block until handshaking is complete or an error occurs. + * + * @param type The type of handshaking to be performed, i.e. as a client or as + * a server. + * + * @param ec Set to indicate what error occurred, if any. + */ + asio::error_code handshake(handshake_type type, + asio::error_code& ec) + { + return service_.handshake(impl_, next_layer_, type, ec); + } + + /// Start an asynchronous SSL handshake. + /** + * This function is used to asynchronously perform an SSL handshake on the + * stream. This function call always returns immediately. + * + * @param type The type of handshaking to be performed, i.e. as a client or as + * a server. + * + * @param handler The handler to be called when the handshake operation + * completes. Copies will be made of the handler as required. The equivalent + * function signature of the handler must be: + * @code void handler( + * const asio::error_code& error // Result of operation. + * ); @endcode + */ + template + void async_handshake(handshake_type type, HandshakeHandler handler) + { + service_.async_handshake(impl_, next_layer_, type, handler); + } + + /// Shut down SSL on the stream. + /** + * This function is used to shut down SSL on the stream. The function call + * will block until SSL has been shut down or an error occurs. + * + * @throws asio::system_error Thrown on failure. + */ + void shutdown() + { + asio::error_code ec; + service_.shutdown(impl_, next_layer_, ec); + asio::detail::throw_error(ec); + } + + /// Shut down SSL on the stream. + /** + * This function is used to shut down SSL on the stream. The function call + * will block until SSL has been shut down or an error occurs. + * + * @param ec Set to indicate what error occurred, if any. + */ + asio::error_code shutdown(asio::error_code& ec) + { + return service_.shutdown(impl_, next_layer_, ec); + } + + /// Asynchronously shut down SSL on the stream. + /** + * This function is used to asynchronously shut down SSL on the stream. This + * function call always returns immediately. + * + * @param handler The handler to be called when the handshake operation + * completes. Copies will be made of the handler as required. The equivalent + * function signature of the handler must be: + * @code void handler( + * const asio::error_code& error // Result of operation. + * ); @endcode + */ + template + void async_shutdown(ShutdownHandler handler) + { + service_.async_shutdown(impl_, next_layer_, handler); + } + + /// Write some data to the stream. + /** + * This function is used to write data on the stream. The function call will + * block until one or more bytes of data has been written successfully, or + * until an error occurs. + * + * @param buffers The data to be written. + * + * @returns The number of bytes written. + * + * @throws asio::system_error Thrown on failure. + * + * @note The write_some operation may not transmit all of the data to the + * peer. Consider using the @ref write function if you need to ensure that all + * data is written before the blocking operation completes. + */ + template + std::size_t write_some(const ConstBufferSequence& buffers) + { + asio::error_code ec; + std::size_t s = service_.write_some(impl_, next_layer_, buffers, ec); + asio::detail::throw_error(ec); + return s; + } + + /// Write some data to the stream. + /** + * This function is used to write data on the stream. The function call will + * block until one or more bytes of data has been written successfully, or + * until an error occurs. + * + * @param buffers The data to be written to the stream. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes written. Returns 0 if an error occurred. + * + * @note The write_some operation may not transmit all of the data to the + * peer. Consider using the @ref write function if you need to ensure that all + * data is written before the blocking operation completes. + */ + template + std::size_t write_some(const ConstBufferSequence& buffers, + asio::error_code& ec) + { + return service_.write_some(impl_, next_layer_, buffers, ec); + } + + /// Start an asynchronous write. + /** + * This function is used to asynchronously write one or more bytes of data to + * the stream. The function call always returns immediately. + * + * @param buffers The data to be written to the stream. Although the buffers + * object may be copied as necessary, ownership of the underlying buffers is + * retained by the caller, which must guarantee that they remain valid until + * the handler is called. + * + * @param handler The handler to be called when the write operation completes. + * Copies will be made of the handler as required. The equivalent function + * signature of the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes written. + * ); @endcode + * + * @note The async_write_some operation may not transmit all of the data to + * the peer. Consider using the @ref async_write function if you need to + * ensure that all data is written before the blocking operation completes. + */ + template + void async_write_some(const ConstBufferSequence& buffers, + WriteHandler handler) + { + service_.async_write_some(impl_, next_layer_, buffers, handler); + } + + /// Read some data from the stream. + /** + * This function is used to read data from the stream. The function call will + * block until one or more bytes of data has been read successfully, or until + * an error occurs. + * + * @param buffers The buffers into which the data will be read. + * + * @returns The number of bytes read. + * + * @throws asio::system_error Thrown on failure. + * + * @note The read_some operation may not read all of the requested number of + * bytes. Consider using the @ref read function if you need to ensure that the + * requested amount of data is read before the blocking operation completes. + */ + template + std::size_t read_some(const MutableBufferSequence& buffers) + { + asio::error_code ec; + std::size_t s = service_.read_some(impl_, next_layer_, buffers, ec); + asio::detail::throw_error(ec); + return s; + } + + /// Read some data from the stream. + /** + * This function is used to read data from the stream. The function call will + * block until one or more bytes of data has been read successfully, or until + * an error occurs. + * + * @param buffers The buffers into which the data will be read. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes read. Returns 0 if an error occurred. + * + * @note The read_some operation may not read all of the requested number of + * bytes. Consider using the @ref read function if you need to ensure that the + * requested amount of data is read before the blocking operation completes. + */ + template + std::size_t read_some(const MutableBufferSequence& buffers, + asio::error_code& ec) + { + return service_.read_some(impl_, next_layer_, buffers, ec); + } + + /// Start an asynchronous read. + /** + * This function is used to asynchronously read one or more bytes of data from + * the stream. The function call always returns immediately. + * + * @param buffers The buffers into which the data will be read. Although the + * buffers object may be copied as necessary, ownership of the underlying + * buffers is retained by the caller, which must guarantee that they remain + * valid until the handler is called. + * + * @param handler The handler to be called when the read operation completes. + * Copies will be made of the handler as required. The equivalent function + * signature of the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes read. + * ); @endcode + * + * @note The async_read_some operation may not read all of the requested + * number of bytes. Consider using the @ref async_read function if you need to + * ensure that the requested amount of data is read before the asynchronous + * operation completes. + */ + template + void async_read_some(const MutableBufferSequence& buffers, + ReadHandler handler) + { + service_.async_read_some(impl_, next_layer_, buffers, handler); + } + + /// Peek at the incoming data on the stream. + /** + * This function is used to peek at the incoming data on the stream, without + * removing it from the input queue. The function call will block until data + * has been read successfully or an error occurs. + * + * @param buffers The buffers into which the data will be read. + * + * @returns The number of bytes read. + * + * @throws asio::system_error Thrown on failure. + */ + template + std::size_t peek(const MutableBufferSequence& buffers) + { + asio::error_code ec; + std::size_t s = service_.peek(impl_, next_layer_, buffers, ec); + asio::detail::throw_error(ec); + return s; + } + + /// Peek at the incoming data on the stream. + /** + * This function is used to peek at the incoming data on the stream, withoutxi + * removing it from the input queue. The function call will block until data + * has been read successfully or an error occurs. + * + * @param buffers The buffers into which the data will be read. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes read. Returns 0 if an error occurred. + */ + template + std::size_t peek(const MutableBufferSequence& buffers, + asio::error_code& ec) + { + return service_.peek(impl_, next_layer_, buffers, ec); + } + + /// Determine the amount of data that may be read without blocking. + /** + * This function is used to determine the amount of data, in bytes, that may + * be read from the stream without blocking. + * + * @returns The number of bytes of data that can be read without blocking. + * + * @throws asio::system_error Thrown on failure. + */ + std::size_t in_avail() + { + asio::error_code ec; + std::size_t s = service_.in_avail(impl_, next_layer_, ec); + asio::detail::throw_error(ec); + return s; + } + + /// Determine the amount of data that may be read without blocking. + /** + * This function is used to determine the amount of data, in bytes, that may + * be read from the stream without blocking. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes of data that can be read without blocking. + */ + std::size_t in_avail(asio::error_code& ec) + { + return service_.in_avail(impl_, next_layer_, ec); + } + +private: + /// The next layer. + Stream next_layer_; + + /// The backend service implementation. + service_type& service_; + + /// The underlying native implementation. + impl_type impl_; +}; + +} // namespace ssl +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_SSL_STREAM_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/ssl/stream_base.hpp b/encryption/libtorrent/include/libtorrent/asio/ssl/stream_base.hpp new file mode 100755 index 000000000..89c4b65a9 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/ssl/stream_base.hpp @@ -0,0 +1,60 @@ +// +// stream_base.hpp +// ~~~~~~~~~~~~~~~ +// +// Copyright (c) 2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_SSL_STREAM_BASE_HPP +#define ASIO_SSL_STREAM_BASE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include "asio/detail/pop_options.hpp" + +namespace asio { +namespace ssl { + +/// The stream_base class is used as a base for the asio::ssl::stream +/// class template so that we have a common place to define various enums. +class stream_base +{ +public: + /// Different handshake types. + enum handshake_type + { + /// Perform handshaking as a client. + client, + + /// Perform handshaking as a server. + server + }; + +protected: + /// Protected destructor to prevent deletion through this type. + ~stream_base() + { + } + +#if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564)) +private: + // Workaround to enable the empty base optimisation with Borland C++. + char dummy_; +#endif +}; + +} // namespace ssl +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_SSL_STREAM_BASE_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/ssl/stream_service.hpp b/encryption/libtorrent/include/libtorrent/asio/ssl/stream_service.hpp new file mode 100644 index 000000000..d96e68aec --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/ssl/stream_service.hpp @@ -0,0 +1,186 @@ +// +// stream_service.hpp +// ~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2005 Voipster / Indrek dot Juhani at voipster dot com +// Copyright (c) 2005 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_SSL_STREAM_SERVICE_HPP +#define ASIO_SSL_STREAM_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/io_service.hpp" +#include "asio/detail/service_base.hpp" +#include "asio/ssl/basic_context.hpp" +#include "asio/ssl/stream_base.hpp" +#include "asio/ssl/detail/openssl_stream_service.hpp" + +namespace asio { +namespace ssl { + +/// Default service implementation for an SSL stream. +class stream_service +#if defined(GENERATING_DOCUMENTATION) + : public asio::io_service::service +#else + : public asio::detail::service_base +#endif +{ +private: + // The type of the platform-specific implementation. + typedef detail::openssl_stream_service service_impl_type; + +public: +#if defined(GENERATING_DOCUMENTATION) + /// The unique service identifier. + static asio::io_service::id id; +#endif + + /// The type of a stream implementation. +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined impl_type; +#else + typedef service_impl_type::impl_type impl_type; +#endif + + /// Construct a new stream service for the specified io_service. + explicit stream_service(asio::io_service& io_service) + : asio::detail::service_base(io_service), + service_impl_(asio::use_service(io_service)) + { + } + + /// Destroy all user-defined handler objects owned by the service. + void shutdown_service() + { + } + + /// Return a null stream implementation. + impl_type null() const + { + return service_impl_.null(); + } + + /// Create a new stream implementation. + template + void create(impl_type& impl, Stream& next_layer, + basic_context& context) + { + service_impl_.create(impl, next_layer, context); + } + + /// Destroy a stream implementation. + template + void destroy(impl_type& impl, Stream& next_layer) + { + service_impl_.destroy(impl, next_layer); + } + + /// Perform SSL handshaking. + template + asio::error_code handshake(impl_type& impl, Stream& next_layer, + stream_base::handshake_type type, asio::error_code& ec) + { + return service_impl_.handshake(impl, next_layer, type, ec); + } + + /// Start an asynchronous SSL handshake. + template + void async_handshake(impl_type& impl, Stream& next_layer, + stream_base::handshake_type type, HandshakeHandler handler) + { + service_impl_.async_handshake(impl, next_layer, type, handler); + } + + /// Shut down SSL on the stream. + template + asio::error_code shutdown(impl_type& impl, Stream& next_layer, + asio::error_code& ec) + { + return service_impl_.shutdown(impl, next_layer, ec); + } + + /// Asynchronously shut down SSL on the stream. + template + void async_shutdown(impl_type& impl, Stream& next_layer, + ShutdownHandler handler) + { + service_impl_.async_shutdown(impl, next_layer, handler); + } + + /// Write some data to the stream. + template + std::size_t write_some(impl_type& impl, Stream& next_layer, + const ConstBufferSequence& buffers, asio::error_code& ec) + { + return service_impl_.write_some(impl, next_layer, buffers, ec); + } + + /// Start an asynchronous write. + template + void async_write_some(impl_type& impl, Stream& next_layer, + const ConstBufferSequence& buffers, WriteHandler handler) + { + service_impl_.async_write_some(impl, next_layer, buffers, handler); + } + + /// Read some data from the stream. + template + std::size_t read_some(impl_type& impl, Stream& next_layer, + const MutableBufferSequence& buffers, asio::error_code& ec) + { + return service_impl_.read_some(impl, next_layer, buffers, ec); + } + + /// Start an asynchronous read. + template + void async_read_some(impl_type& impl, Stream& next_layer, + const MutableBufferSequence& buffers, ReadHandler handler) + { + service_impl_.async_read_some(impl, next_layer, buffers, handler); + } + + /// Peek at the incoming data on the stream. + template + std::size_t peek(impl_type& impl, Stream& next_layer, + const MutableBufferSequence& buffers, asio::error_code& ec) + { + return service_impl_.peek(impl, next_layer, buffers, ec); + } + + /// Determine the amount of data that may be read without blocking. + template + std::size_t in_avail(impl_type& impl, Stream& next_layer, + asio::error_code& ec) + { + return service_impl_.in_avail(impl, next_layer, ec); + } + +private: + // The service that provides the platform-specific implementation. + service_impl_type& service_impl_; +}; + +} // namespace ssl +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_SSL_STREAM_SERVICE_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/strand.hpp b/encryption/libtorrent/include/libtorrent/asio/strand.hpp new file mode 100644 index 000000000..948921e10 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/strand.hpp @@ -0,0 +1,166 @@ +// +// strand.hpp +// ~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_STRAND_HPP +#define ASIO_STRAND_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/io_service.hpp" +#include "asio/detail/strand_service.hpp" +#include "asio/detail/wrapped_handler.hpp" + +namespace asio { + +/// Provides serialised handler execution. +/** + * The io_service::strand class provides the ability to post and dispatch + * handlers with the guarantee that none of those handlers will execute + * concurrently. + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Safe. + * + * @par Concepts: + * Dispatcher. + */ +class io_service::strand +{ +public: + /// Constructor. + /** + * Constructs the strand. + * + * @param io_service The io_service object that the strand will use to + * dispatch handlers that are ready to be run. + */ + explicit strand(asio::io_service& io_service) + : service_(asio::use_service< + asio::detail::strand_service>(io_service)) + { + service_.construct(impl_); + } + + /// Destructor. + ~strand() + { + service_.destroy(impl_); + } + + /// Get the io_service associated with the strand. + /** + * This function may be used to obtain the io_service object that the strand + * uses to dispatch handlers for asynchronous operations. + * + * @return A reference to the io_service object that the strand will use to + * dispatch handlers. Ownership is not transferred to the caller. + */ + asio::io_service& io_service() + { + return service_.io_service(); + } + + /// Request the strand to invoke the given handler. + /** + * This function is used to ask the strand to execute the given handler. + * + * The strand object guarantees that handlers posted or dispatched through + * the strand will not be executed concurrently. The handler may be executed + * inside this function if the guarantee can be met. If this function is + * called from within a handler that was posted or dispatched through the same + * strand, then the new handler will be executed immediately. + * + * The strand's guarantee is in addition to the guarantee provided by the + * underlying io_service. The io_service guarantees that the handler will only + * be called in a thread in which the io_service's run member function is + * currently being invoked. + * + * @param handler The handler to be called. The strand will make a copy of the + * handler object as required. The function signature of the handler must be: + * @code void handler(); @endcode + */ + template + void dispatch(Handler handler) + { + service_.dispatch(impl_, handler); + } + + /// Request the strand to invoke the given handler and return + /// immediately. + /** + * This function is used to ask the strand to execute the given handler, but + * without allowing the strand to call the handler from inside this function. + * + * The strand object guarantees that handlers posted or dispatched through + * the strand will not be executed concurrently. The strand's guarantee is in + * addition to the guarantee provided by the underlying io_service. The + * io_service guarantees that the handler will only be called in a thread in + * which the io_service's run member function is currently being invoked. + * + * @param handler The handler to be called. The strand will make a copy of the + * handler object as required. The function signature of the handler must be: + * @code void handler(); @endcode + */ + template + void post(Handler handler) + { + service_.post(impl_, handler); + } + + /// Create a new handler that automatically dispatches the wrapped handler + /// on the strand. + /** + * This function is used to create a new handler function object that, when + * invoked, will automatically pass the wrapped handler to the strand's + * dispatch function. + * + * @param handler The handler to be wrapped. The strand will make a copy of + * the handler object as required. The function signature of the handler must + * be: @code void handler(A1 a1, ... An an); @endcode + * + * @return A function object that, when invoked, passes the wrapped handler to + * the strand's dispatch function. Given a function object with the signature: + * @code R f(A1 a1, ... An an); @endcode + * If this function object is passed to the wrap function like so: + * @code strand.wrap(f); @endcode + * then the return value is a function object with the signature + * @code void g(A1 a1, ... An an); @endcode + * that, when invoked, executes code equivalent to: + * @code strand.dispatch(boost::bind(f, a1, ... an)); @endcode + */ + template +#if defined(GENERATING_DOCUMENTATION) + unspecified +#else + detail::wrapped_handler +#endif + wrap(Handler handler) + { + return detail::wrapped_handler(*this, handler); + } + +private: + asio::detail::strand_service& service_; + asio::detail::strand_service::implementation_type impl_; +}; + +/// Typedef for backwards compatibility. +typedef asio::io_service::strand strand; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_STRAND_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/stream_socket_service.hpp b/encryption/libtorrent/include/libtorrent/asio/stream_socket_service.hpp new file mode 100644 index 000000000..d7915aaf4 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/stream_socket_service.hpp @@ -0,0 +1,283 @@ +// +// stream_socket_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_STREAM_SOCKET_SERVICE_HPP +#define ASIO_STREAM_SOCKET_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/error.hpp" +#include "asio/io_service.hpp" +#include "asio/detail/epoll_reactor.hpp" +#include "asio/detail/kqueue_reactor.hpp" +#include "asio/detail/select_reactor.hpp" +#include "asio/detail/service_base.hpp" +#include "asio/detail/win_iocp_socket_service.hpp" +#include "asio/detail/reactive_socket_service.hpp" + +namespace asio { + +/// Default service implementation for a stream socket. +template +class stream_socket_service +#if defined(GENERATING_DOCUMENTATION) + : public asio::io_service::service +#else + : public asio::detail::service_base > +#endif +{ +public: +#if defined(GENERATING_DOCUMENTATION) + /// The unique service identifier. + static asio::io_service::id id; +#endif + + /// The protocol type. + typedef Protocol protocol_type; + + /// The endpoint type. + typedef typename Protocol::endpoint endpoint_type; + +private: + // The type of the platform-specific implementation. +#if defined(ASIO_HAS_IOCP) + typedef detail::win_iocp_socket_service service_impl_type; +#elif defined(ASIO_HAS_EPOLL) + typedef detail::reactive_socket_service< + Protocol, detail::epoll_reactor > service_impl_type; +#elif defined(ASIO_HAS_KQUEUE) + typedef detail::reactive_socket_service< + Protocol, detail::kqueue_reactor > service_impl_type; +#else + typedef detail::reactive_socket_service< + Protocol, detail::select_reactor > service_impl_type; +#endif + +public: + /// The type of a stream socket implementation. +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined implementation_type; +#else + typedef typename service_impl_type::implementation_type implementation_type; +#endif + + /// The native socket type. +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined native_type; +#else + typedef typename service_impl_type::native_type native_type; +#endif + + /// Construct a new stream socket service for the specified io_service. + explicit stream_socket_service(asio::io_service& io_service) + : asio::detail::service_base< + stream_socket_service >(io_service), + service_impl_(asio::use_service(io_service)) + { + } + + /// Destroy all user-defined handler objects owned by the service. + void shutdown_service() + { + } + + /// Construct a new stream socket implementation. + void construct(implementation_type& impl) + { + service_impl_.construct(impl); + } + + /// Destroy a stream socket implementation. + void destroy(implementation_type& impl) + { + service_impl_.destroy(impl); + } + + /// Open a stream socket. + asio::error_code open(implementation_type& impl, + const protocol_type& protocol, asio::error_code& ec) + { + if (protocol.type() == SOCK_STREAM) + service_impl_.open(impl, protocol, ec); + else + ec = asio::error::invalid_argument; + return ec; + } + + /// Assign an existing native socket to a stream socket. + asio::error_code assign(implementation_type& impl, + const protocol_type& protocol, const native_type& native_socket, + asio::error_code& ec) + { + return service_impl_.assign(impl, protocol, native_socket, ec); + } + + /// Determine whether the socket is open. + bool is_open(const implementation_type& impl) const + { + return service_impl_.is_open(impl); + } + + /// Close a stream socket implementation. + asio::error_code close(implementation_type& impl, + asio::error_code& ec) + { + return service_impl_.close(impl, ec); + } + + /// Get the native socket implementation. + native_type native(implementation_type& impl) + { + return service_impl_.native(impl); + } + + /// Cancel all asynchronous operations associated with the socket. + asio::error_code cancel(implementation_type& impl, + asio::error_code& ec) + { + return service_impl_.cancel(impl, ec); + } + + /// Determine whether the socket is at the out-of-band data mark. + bool at_mark(const implementation_type& impl, + asio::error_code& ec) const + { + return service_impl_.at_mark(impl, ec); + } + + /// Determine the number of bytes available for reading. + std::size_t available(const implementation_type& impl, + asio::error_code& ec) const + { + return service_impl_.available(impl, ec); + } + + /// Bind the stream socket to the specified local endpoint. + asio::error_code bind(implementation_type& impl, + const endpoint_type& endpoint, asio::error_code& ec) + { + return service_impl_.bind(impl, endpoint, ec); + } + + /// Connect the stream socket to the specified endpoint. + asio::error_code connect(implementation_type& impl, + const endpoint_type& peer_endpoint, asio::error_code& ec) + { + return service_impl_.connect(impl, peer_endpoint, ec); + } + + /// Start an asynchronous connect. + template + void async_connect(implementation_type& impl, + const endpoint_type& peer_endpoint, ConnectHandler handler) + { + service_impl_.async_connect(impl, peer_endpoint, handler); + } + + /// Set a socket option. + template + asio::error_code set_option(implementation_type& impl, + const SettableSocketOption& option, asio::error_code& ec) + { + return service_impl_.set_option(impl, option, ec); + } + + /// Get a socket option. + template + asio::error_code get_option(const implementation_type& impl, + GettableSocketOption& option, asio::error_code& ec) const + { + return service_impl_.get_option(impl, option, ec); + } + + /// Perform an IO control command on the socket. + template + asio::error_code io_control(implementation_type& impl, + IoControlCommand& command, asio::error_code& ec) + { + return service_impl_.io_control(impl, command, ec); + } + + /// Get the local endpoint. + endpoint_type local_endpoint(const implementation_type& impl, + asio::error_code& ec) const + { + return service_impl_.local_endpoint(impl, ec); + } + + /// Get the remote endpoint. + endpoint_type remote_endpoint(const implementation_type& impl, + asio::error_code& ec) const + { + return service_impl_.remote_endpoint(impl, ec); + } + + /// Disable sends or receives on the socket. + asio::error_code shutdown(implementation_type& impl, + socket_base::shutdown_type what, asio::error_code& ec) + { + return service_impl_.shutdown(impl, what, ec); + } + + /// Send the given data to the peer. + template + std::size_t send(implementation_type& impl, + const ConstBufferSequence& buffers, + socket_base::message_flags flags, asio::error_code& ec) + { + return service_impl_.send(impl, buffers, flags, ec); + } + + /// Start an asynchronous send. + template + void async_send(implementation_type& impl, + const ConstBufferSequence& buffers, + socket_base::message_flags flags, WriteHandler handler) + { + service_impl_.async_send(impl, buffers, flags, handler); + } + + /// Receive some data from the peer. + template + std::size_t receive(implementation_type& impl, + const MutableBufferSequence& buffers, + socket_base::message_flags flags, asio::error_code& ec) + { + return service_impl_.receive(impl, buffers, flags, ec); + } + + /// Start an asynchronous receive. + template + void async_receive(implementation_type& impl, + const MutableBufferSequence& buffers, + socket_base::message_flags flags, ReadHandler handler) + { + service_impl_.async_receive(impl, buffers, flags, handler); + } + +private: + // The service that provides the platform-specific implementation. + service_impl_type& service_impl_; +}; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_STREAM_SOCKET_SERVICE_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/streambuf.hpp b/encryption/libtorrent/include/libtorrent/asio/streambuf.hpp new file mode 100644 index 000000000..fdcc94a00 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/streambuf.hpp @@ -0,0 +1,31 @@ +// +// streambuf.hpp +// ~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_STREAMBUF_HPP +#define ASIO_STREAMBUF_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/basic_streambuf.hpp" + +namespace asio { + +/// Typedef for the typical usage of basic_streambuf. +typedef basic_streambuf<> streambuf; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_STREAMBUF_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/system_error.hpp b/encryption/libtorrent/include/libtorrent/asio/system_error.hpp new file mode 100644 index 000000000..95f9f540e --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/system_error.hpp @@ -0,0 +1,117 @@ +// +// system_error.hpp +// ~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_SYSTEM_ERROR_HPP +#define ASIO_SYSTEM_ERROR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/error_code.hpp" + +namespace asio { + +/// The system_error class is used to represent system conditions that +/// prevent the library from operating correctly. +class system_error + : public std::exception +{ +public: + /// Construct with an error code. + system_error(const error_code& code) + : code_(code), + context_() + { + } + + /// Construct with an error code and context. + system_error(const error_code& code, const std::string& context) + : code_(code), + context_(context) + { + } + + /// Copy constructor. + system_error(const system_error& other) + : std::exception(other), + code_(other.code_), + context_(other.context_), + what_() + { + } + + /// Destructor. + virtual ~system_error() throw () + { + } + + /// Assignment operator. + system_error& operator=(const system_error& e) + { + context_ = e.context_; + code_ = e.code_; + what_.reset(); + return *this; + } + + /// Get a string representation of the exception. + virtual const char* what() const throw () + { + try + { + if (!what_) + { + std::string tmp(context_); + if (tmp.length()) + tmp += ": "; + tmp += code_.message(); + what_.reset(new std::string(tmp)); + } + return what_->c_str(); + } + catch (std::exception&) + { + return "system_error"; + } + } + + /// Get the error code associated with the exception. + error_code code() const + { + return code_; + } + +private: + // The code associated with the error. + error_code code_; + + // The context associated with the error. + std::string context_; + + // The string representation of the error. + mutable boost::scoped_ptr what_; +}; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_SYSTEM_ERROR_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/system_exception.hpp b/encryption/libtorrent/include/libtorrent/asio/system_exception.hpp new file mode 100644 index 000000000..599e22712 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/system_exception.hpp @@ -0,0 +1,198 @@ +// +// error.hpp +// ~~~~~~~~~ +// +// Copyright (c) 2003-2006 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_SYSTEM_EXCEPTION_HPP +#define ASIO_SYSTEM_EXCEPTION_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include +#include +#include +#include +#include +#if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564)) +# include +#endif // BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564)) +#include "asio/detail/pop_options.hpp" + +#include "asio/detail/win_local_free_on_block_exit.hpp" + +namespace asio { + +/// The system_exception class is used to represent system conditions that +/// prevent the library from operating correctly. +class system_exception + : public std::exception +{ +public: + /// Construct with a specific context and error code. + system_exception(const std::string& context, int code) + : context_(context), + code_(code) + { + } + + /// Copy constructor. + system_exception(const system_exception& e) + : std::exception(e), + context_(e.context_), + code_(e.code_) + { + } + + /// Destructor. + virtual ~system_exception() throw () + { + } + + /// Assignment operator. + system_exception& operator=(const system_exception& e) + { + context_ = e.context_; + code_ = e.code_; + what_.reset(); + return *this; + } + + /// Get a string representation of the exception. + virtual const char* what() const throw () + { +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + try + { + if (!what_) + { + char* msg = 0; + DWORD length = ::FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER + | FORMAT_MESSAGE_FROM_SYSTEM + | FORMAT_MESSAGE_IGNORE_INSERTS, 0, code_, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (char*)&msg, 0, 0); + detail::win_local_free_on_block_exit local_free_obj(msg); + if (length && msg[length - 1] == '\n') + msg[--length] = '\0'; + if (length && msg[length - 1] == '\r') + msg[--length] = '\0'; + if (length) + { + std::string tmp(context_); + tmp += ": "; + tmp += msg; + what_.reset(new std::string(tmp)); + } + else + { + return "asio system_exception"; + } + } + return what_->c_str(); + } + catch (std::exception&) + { + return "asio system_exception"; + } +#elif defined(__sun) || defined(__QNX__) + return strerror(code_); +#elif defined(__MACH__) && defined(__APPLE__) \ + || defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__) + try + { + char buf[256] = ""; + strerror_r(code_, buf, sizeof(buf)); + std::string tmp(context_); + tmp += ": "; + tmp += buf; + what_.reset(new std::string(tmp)); + return what_->c_str(); + } + catch (std::exception&) + { + return "asio system_exception"; + } +#else + try + { + char buf[256] = ""; + std::string tmp(context_); + tmp += ": "; + tmp += strerror_r(code_, buf, sizeof(buf)); + what_.reset(new std::string(tmp)); + return what_->c_str(); + } + catch (std::exception&) + { + return "asio system_exception"; + } +#endif + } + + /// Get the implementation-defined context associated with the exception. + const std::string& context() const + { + return context_; + } + + /// Get the implementation-defined code associated with the exception. + int code() const + { + return code_; + } + +private: + // The context associated with the error. + std::string context_; + + // The code associated with the error. + int code_; + + // The string representation of the error. + mutable boost::scoped_ptr what_; +}; + +/// Output the string associated with a system exception. +/** + * Used to output a human-readable string that is associated with a system + * exception. + * + * @param os The output stream to which the string will be written. + * + * @param e The exception to be written. + * + * @return The output stream. + * + * @relates asio::system_exception + */ +#if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564)) +std::ostream& operator<<(std::ostream& os, const system_exception& e) +{ + os << e.what(); + return os; +} +#else // BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564)) +template +Ostream& operator<<(Ostream& os, const system_exception& e) +{ + os << e.what(); + return os; +} +#endif // BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564)) + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_SYSTEM_EXCEPTION_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/thread.hpp b/encryption/libtorrent/include/libtorrent/asio/thread.hpp new file mode 100644 index 000000000..b8bc81bab --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/thread.hpp @@ -0,0 +1,91 @@ +// +// thread.hpp +// ~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_THREAD_HPP +#define ASIO_THREAD_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/thread.hpp" + +namespace asio { + +/// A simple abstraction for starting threads. +/** + * The asio::thread class implements the smallest possible subset of the + * functionality of boost::thread. It is intended to be used only for starting + * a thread and waiting for it to exit. If more extensive threading + * capabilities are required, you are strongly advised to use something else. + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + * + * @par Example + * A typical use of asio::thread would be to launch a thread to run an + * io_service's event processing loop: + * + * @par + * @code asio::io_service io_service; + * // ... + * asio::thread t(boost::bind(&asio::io_service::run, &io_service)); + * // ... + * t.join(); @endcode + */ +class thread + : private noncopyable +{ +public: + /// Start a new thread that executes the supplied function. + /** + * This constructor creates a new thread that will execute the given function + * or function object. + * + * @param f The function or function object to be run in the thread. The + * function signature must be: @code void f(); @endcode + */ + template + explicit thread(Function f) + : impl_(f) + { + } + + /// Destructor. + ~thread() + { + } + + /// Wait for the thread to exit. + /** + * This function will block until the thread has exited. + * + * If this function is not called before the thread object is destroyed, the + * thread itself will continue to run until completion. You will, however, + * no longer have the ability to wait for it to exit. + */ + void join() + { + impl_.join(); + } + +private: + detail::thread impl_; +}; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_THREAD_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/time_traits.hpp b/encryption/libtorrent/include/libtorrent/asio/time_traits.hpp new file mode 100644 index 000000000..d3c910e53 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/time_traits.hpp @@ -0,0 +1,78 @@ +// +// time_traits.hpp +// ~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_TIME_TRAITS_HPP +#define ASIO_TIME_TRAITS_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/socket_types.hpp" // Must come before posix_time. + +#include "asio/detail/push_options.hpp" +#include +#include "asio/detail/pop_options.hpp" + +namespace asio { + +/// Time traits suitable for use with the deadline timer. +template +struct time_traits; + +/// Time traits specialised for posix_time. +template <> +struct time_traits +{ + /// The time type. + typedef boost::posix_time::ptime time_type; + + /// The duration type. + typedef boost::posix_time::time_duration duration_type; + + /// Get the current time. + static time_type now() + { + return boost::posix_time::microsec_clock::universal_time(); + } + + /// Add a duration to a time. + static time_type add(const time_type& t, const duration_type& d) + { + return t + d; + } + + /// Subtract one time from another. + static duration_type subtract(const time_type& t1, const time_type& t2) + { + return t1 - t2; + } + + /// Test whether one time is less than another. + static bool less_than(const time_type& t1, const time_type& t2) + { + return t1 < t2; + } + + /// Convert to POSIX duration type. + static boost::posix_time::time_duration to_posix_duration( + const duration_type& d) + { + return d; + } +}; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_TIME_TRAITS_HPP diff --git a/encryption/libtorrent/include/libtorrent/asio/write.hpp b/encryption/libtorrent/include/libtorrent/asio/write.hpp new file mode 100644 index 000000000..5d643626a --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/asio/write.hpp @@ -0,0 +1,515 @@ +// +// write.hpp +// ~~~~~~~~~ +// +// Copyright (c) 2003-2007 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_WRITE_HPP +#define ASIO_WRITE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/basic_streambuf.hpp" +#include "asio/error.hpp" + +namespace asio { + +/** + * @defgroup write asio::write + */ +/*@{*/ + +/// Write all of the supplied data to a stream before returning. +/** + * This function is used to write a certain number of bytes of data to a stream. + * The call will block until one of the following conditions is true: + * + * @li All of the data in the supplied buffers has been written. That is, the + * bytes transferred is equal to the sum of the buffer sizes. + * + * @li An error occurred. + * + * This operation is implemented in terms of one or more calls to the stream's + * write_some function. + * + * @param s The stream to which the data is to be written. The type must support + * the SyncWriteStream concept. + * + * @param buffers One or more buffers containing the data to be written. The sum + * of the buffer sizes indicates the maximum number of bytes to write to the + * stream. + * + * @returns The number of bytes transferred. + * + * @throws asio::system_error Thrown on failure. + * + * @par Example + * To write a single data buffer use the @ref buffer function as follows: + * @code asio::write(s, asio::buffer(data, size)); @endcode + * See the @ref buffer documentation for information on writing multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + * + * @note This overload is equivalent to calling: + * @code asio::write( + * s, buffers, + * asio::transfer_all()); @endcode + */ +template +std::size_t write(SyncWriteStream& s, const ConstBufferSequence& buffers); + +/// Write a certain amount of data to a stream before returning. +/** + * This function is used to write a certain number of bytes of data to a stream. + * The call will block until one of the following conditions is true: + * + * @li All of the data in the supplied buffers has been written. That is, the + * bytes transferred is equal to the sum of the buffer sizes. + * + * @li The completion_condition function object returns true. + * + * This operation is implemented in terms of one or more calls to the stream's + * write_some function. + * + * @param s The stream to which the data is to be written. The type must support + * the SyncWriteStream concept. + * + * @param buffers One or more buffers containing the data to be written. The sum + * of the buffer sizes indicates the maximum number of bytes to write to the + * stream. + * + * @param completion_condition The function object to be called to determine + * whether the write operation is complete. The signature of the function object + * must be: + * @code bool completion_condition( + * const asio::error_code& error, // Result of latest write_some + * // operation. + * + * std::size_t bytes_transferred // Number of bytes transferred + * // so far. + * ); @endcode + * A return value of true indicates that the write operation is complete. False + * indicates that further calls to the stream's write_some function are + * required. + * + * @returns The number of bytes transferred. + * + * @throws asio::system_error Thrown on failure. + * + * @par Example + * To write a single data buffer use the @ref buffer function as follows: + * @code asio::write(s, asio::buffer(data, size), + * asio::transfer_at_least(32)); @endcode + * See the @ref buffer documentation for information on writing multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ +template +std::size_t write(SyncWriteStream& s, const ConstBufferSequence& buffers, + CompletionCondition completion_condition); + +/// Write a certain amount of data to a stream before returning. +/** + * This function is used to write a certain number of bytes of data to a stream. + * The call will block until one of the following conditions is true: + * + * @li All of the data in the supplied buffers has been written. That is, the + * bytes transferred is equal to the sum of the buffer sizes. + * + * @li The completion_condition function object returns true. + * + * This operation is implemented in terms of one or more calls to the stream's + * write_some function. + * + * @param s The stream to which the data is to be written. The type must support + * the SyncWriteStream concept. + * + * @param buffers One or more buffers containing the data to be written. The sum + * of the buffer sizes indicates the maximum number of bytes to write to the + * stream. + * + * @param completion_condition The function object to be called to determine + * whether the write operation is complete. The signature of the function object + * must be: + * @code bool completion_condition( + * const asio::error_code& error, // Result of latest write_some + * // operation. + * + * std::size_t bytes_transferred // Number of bytes transferred + * // so far. + * ); @endcode + * A return value of true indicates that the write operation is complete. False + * indicates that further calls to the stream's write_some function are + * required. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes written. If an error occurs, returns the total + * number of bytes successfully transferred prior to the error. + */ +template +std::size_t write(SyncWriteStream& s, const ConstBufferSequence& buffers, + CompletionCondition completion_condition, asio::error_code& ec); + +/// Write a certain amount of data to a stream before returning. +/** + * This function is used to write a certain number of bytes of data to a stream. + * The call will block until one of the following conditions is true: + * + * @li All of the data in the supplied basic_streambuf has been written. + * + * @li An error occurred. + * + * This operation is implemented in terms of one or more calls to the stream's + * write_some function. + * + * @param s The stream to which the data is to be written. The type must support + * the SyncWriteStream concept. + * + * @param b The basic_streambuf object from which data will be written. + * + * @returns The number of bytes transferred. + * + * @throws asio::system_error Thrown on failure. + * + * @note This overload is equivalent to calling: + * @code asio::write( + * s, b, + * asio::transfer_all()); @endcode + */ +template +std::size_t write(SyncWriteStream& s, basic_streambuf& b); + +/// Write a certain amount of data to a stream before returning. +/** + * This function is used to write a certain number of bytes of data to a stream. + * The call will block until one of the following conditions is true: + * + * @li All of the data in the supplied basic_streambuf has been written. + * + * @li The completion_condition function object returns true. + * + * This operation is implemented in terms of one or more calls to the stream's + * write_some function. + * + * @param s The stream to which the data is to be written. The type must support + * the SyncWriteStream concept. + * + * @param b The basic_streambuf object from which data will be written. + * + * @param completion_condition The function object to be called to determine + * whether the write operation is complete. The signature of the function object + * must be: + * @code bool completion_condition( + * const asio::error_code& error, // Result of latest write_some + * // operation. + * + * std::size_t bytes_transferred // Number of bytes transferred + * // so far. + * ); @endcode + * A return value of true indicates that the write operation is complete. False + * indicates that further calls to the stream's write_some function are + * required. + * + * @returns The number of bytes transferred. + * + * @throws asio::system_error Thrown on failure. + */ +template +std::size_t write(SyncWriteStream& s, basic_streambuf& b, + CompletionCondition completion_condition); + +/// Write a certain amount of data to a stream before returning. +/** + * This function is used to write a certain number of bytes of data to a stream. + * The call will block until one of the following conditions is true: + * + * @li All of the data in the supplied basic_streambuf has been written. + * + * @li The completion_condition function object returns true. + * + * This operation is implemented in terms of one or more calls to the stream's + * write_some function. + * + * @param s The stream to which the data is to be written. The type must support + * the SyncWriteStream concept. + * + * @param b The basic_streambuf object from which data will be written. + * + * @param completion_condition The function object to be called to determine + * whether the write operation is complete. The signature of the function object + * must be: + * @code bool completion_condition( + * const asio::error_code& error, // Result of latest write_some + * // operation. + * + * std::size_t bytes_transferred // Number of bytes transferred + * // so far. + * ); @endcode + * A return value of true indicates that the write operation is complete. False + * indicates that further calls to the stream's write_some function are + * required. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns The number of bytes written. If an error occurs, returns the total + * number of bytes successfully transferred prior to the error. + */ +template +std::size_t write(SyncWriteStream& s, basic_streambuf& b, + CompletionCondition completion_condition, asio::error_code& ec); + +/*@}*/ +/** + * @defgroup async_write asio::async_write + */ +/*@{*/ + +/// Start an asynchronous operation to write of all of the supplied data to a +/// stream. +/** + * This function is used to asynchronously write a certain number of bytes of + * data to a stream. The function call always returns immediately. The + * asynchronous operation will continue until one of the following conditions + * is true: + * + * @li All of the data in the supplied buffers has been written. That is, the + * bytes transferred is equal to the sum of the buffer sizes. + * + * @li An error occurred. + * + * This operation is implemented in terms of one or more calls to the stream's + * async_write_some function. + * + * @param s The stream to which the data is to be written. The type must support + * the AsyncWriteStream concept. + * + * @param buffers One or more buffers containing the data to be written. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param handler The handler to be called when the write operation completes. + * Copies will be made of the handler as required. The function signature of + * the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * + * std::size_t bytes_transferred // Number of bytes written from the + * // buffers. If an error occurred, + * // this will be less than the sum + * // of the buffer sizes. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation of + * the handler will be performed in a manner equivalent to using + * asio::io_service::post(). + * + * @par Example + * To write a single data buffer use the @ref buffer function as follows: + * @code + * asio::async_write(s, asio::buffer(data, size), handler); + * @endcode + * See the @ref buffer documentation for information on writing multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ +template +void async_write(AsyncWriteStream& s, const ConstBufferSequence& buffers, + WriteHandler handler); + +/// Start an asynchronous operation to write a certain amount of data to a +/// stream. +/** + * This function is used to asynchronously write a certain number of bytes of + * data to a stream. The function call always returns immediately. The + * asynchronous operation will continue until one of the following conditions + * is true: + * + * @li All of the data in the supplied buffers has been written. That is, the + * bytes transferred is equal to the sum of the buffer sizes. + * + * @li The completion_condition function object returns true. + * + * This operation is implemented in terms of one or more calls to the stream's + * async_write_some function. + * + * @param s The stream to which the data is to be written. The type must support + * the AsyncWriteStream concept. + * + * @param buffers One or more buffers containing the data to be written. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param completion_condition The function object to be called to determine + * whether the write operation is complete. The signature of the function object + * must be: + * @code bool completion_condition( + * const asio::error_code& error, // Result of latest write_some + * // operation. + * + * std::size_t bytes_transferred // Number of bytes transferred + * // so far. + * ); @endcode + * A return value of true indicates that the write operation is complete. False + * indicates that further calls to the stream's async_write_some function are + * required. + * + * @param handler The handler to be called when the write operation completes. + * Copies will be made of the handler as required. The function signature of the + * handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * + * std::size_t bytes_transferred // Number of bytes written from the + * // buffers. If an error occurred, + * // this will be less than the sum + * // of the buffer sizes. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation of + * the handler will be performed in a manner equivalent to using + * asio::io_service::post(). + * + * @par Example + * To write a single data buffer use the @ref buffer function as follows: + * @code asio::async_write(s, + * asio::buffer(data, size), + * asio::transfer_at_least(32), + * handler); @endcode + * See the @ref buffer documentation for information on writing multiple + * buffers in one go, and how to use it with arrays, boost::array or + * std::vector. + */ +template +void async_write(AsyncWriteStream& s, const ConstBufferSequence& buffers, + CompletionCondition completion_condition, WriteHandler handler); + +/// Start an asynchronous operation to write a certain amount of data to a +/// stream. +/** + * This function is used to asynchronously write a certain number of bytes of + * data to a stream. The function call always returns immediately. The + * asynchronous operation will continue until one of the following conditions + * is true: + * + * @li All of the data in the supplied basic_streambuf has been written. + * + * @li An error occurred. + * + * This operation is implemented in terms of one or more calls to the stream's + * async_write_some function. + * + * @param s The stream to which the data is to be written. The type must support + * the AsyncWriteStream concept. + * + * @param b A basic_streambuf object from which data will be written. Ownership + * of the streambuf is retained by the caller, which must guarantee that it + * remains valid until the handler is called. + * + * @param handler The handler to be called when the write operation completes. + * Copies will be made of the handler as required. The function signature of the + * handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * + * std::size_t bytes_transferred // Number of bytes written from the + * // buffers. If an error occurred, + * // this will be less than the sum + * // of the buffer sizes. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation of + * the handler will be performed in a manner equivalent to using + * asio::io_service::post(). + */ +template +void async_write(AsyncWriteStream& s, basic_streambuf& b, + WriteHandler handler); + +/// Start an asynchronous operation to write a certain amount of data to a +/// stream. +/** + * This function is used to asynchronously write a certain number of bytes of + * data to a stream. The function call always returns immediately. The + * asynchronous operation will continue until one of the following conditions + * is true: + * + * @li All of the data in the supplied basic_streambuf has been written. + * + * @li The completion_condition function object returns true. + * + * This operation is implemented in terms of one or more calls to the stream's + * async_write_some function. + * + * @param s The stream to which the data is to be written. The type must support + * the AsyncWriteStream concept. + * + * @param b A basic_streambuf object from which data will be written. Ownership + * of the streambuf is retained by the caller, which must guarantee that it + * remains valid until the handler is called. + * + * @param completion_condition The function object to be called to determine + * whether the write operation is complete. The signature of the function object + * must be: + * @code bool completion_condition( + * const asio::error_code& error, // Result of latest write_some + * // operation. + * + * std::size_t bytes_transferred // Number of bytes transferred + * // so far. + * ); @endcode + * A return value of true indicates that the write operation is complete. False + * indicates that further calls to the stream's async_write_some function are + * required. + * + * @param handler The handler to be called when the write operation completes. + * Copies will be made of the handler as required. The function signature of the + * handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * + * std::size_t bytes_transferred // Number of bytes written from the + * // buffers. If an error occurred, + * // this will be less than the sum + * // of the buffer sizes. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation of + * the handler will be performed in a manner equivalent to using + * asio::io_service::post(). + */ +template +void async_write(AsyncWriteStream& s, basic_streambuf& b, + CompletionCondition completion_condition, WriteHandler handler); + +/*@}*/ + +} // namespace asio + +#include "asio/impl/write.ipp" + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_WRITE_HPP diff --git a/encryption/libtorrent/include/libtorrent/aux_/allocate_resources_impl.hpp b/encryption/libtorrent/include/libtorrent/aux_/allocate_resources_impl.hpp new file mode 100644 index 000000000..31865d40a --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/aux_/allocate_resources_impl.hpp @@ -0,0 +1,328 @@ +/* + +Copyright (c) 2003, Magnus Jonsson +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_ALLOCATE_RESOURCES_IMPL_HPP_INCLUDED +#define TORRENT_ALLOCATE_RESOURCES_IMPL_HPP_INCLUDED + +#include +#include + +#include + +#include "libtorrent/resource_request.hpp" +#include "libtorrent/peer_id.hpp" +#include "libtorrent/socket.hpp" +#include "libtorrent/size_type.hpp" + +#ifdef min +#undef min +#endif + +#ifdef max +#undef max +#endif + +namespace libtorrent +{ + + int saturated_add(int a, int b); + + namespace aux + { + // give num_resources to r, + // return how how many were actually accepted. + inline int give(resource_request& r, int num_resources) + { + assert(num_resources >= 0); + assert(r.given <= r.max); + + int accepted = (std::min)(num_resources, r.max - r.given); + assert(accepted >= 0); + + r.given += accepted; + assert(r.given <= r.max); + + return accepted; + } + + inline int div_round_up(int numerator, int denominator) + { + return (numerator + denominator - 1) / denominator; + } + +#ifndef NDEBUG + + template + class allocate_resources_contract_check + { + int m_resources; + It m_start; + It m_end; + resource_request T::* m_res; + + public: + allocate_resources_contract_check( + int resources + , It start + , It end + , resource_request T::* res) + : m_resources(resources) + , m_start(start) + , m_end(end) + , m_res(res) + { + assert(m_resources >= 0); + for (It i = m_start, end(m_end); i != end; ++i) + { + assert(((*i).*m_res).max >= 0); + assert(((*i).*m_res).given >= 0); + } + } + + ~allocate_resources_contract_check() + { + int sum_given = 0; + int sum_max = 0; + int sum_min = 0; + for (It i = m_start, end(m_end); i != end; ++i) + { + assert(((*i).*m_res).max >= 0); + assert(((*i).*m_res).min >= 0); + assert(((*i).*m_res).max >= ((*i).*m_res).min); + assert(((*i).*m_res).given >= 0); + assert(((*i).*m_res).given <= ((*i).*m_res).max); + + sum_given = saturated_add(sum_given, ((*i).*m_res).given); + sum_max = saturated_add(sum_max, ((*i).*m_res).max); + sum_min = saturated_add(sum_min, ((*i).*m_res).min); + } + if (sum_given != (std::min)(std::max(m_resources, sum_min), sum_max)) + { + std::cerr << sum_given << " " << m_resources << " " << sum_min << " " << sum_max << std::endl; + assert(false); + } + } + }; + +#endif + + template + void allocate_resources_impl( + int resources + , It start + , It end + , resource_request T::* res) + { + assert(resources >= 0); + #ifndef NDEBUG + allocate_resources_contract_check contract_check( + resources + , start + , end + , res); + #endif + + for (It i = start; i != end; ++i) + { + resource_request& r = (*i).*res; + r.leftovers = (std::max)(r.used - r.given, 0); + } + + if (resources == resource_request::inf) + { + // No competition for resources. + // Just give everyone what they want. + for (It i = start; i != end; ++i) + { + ((*i).*res).given = ((*i).*res).max; + } + return; + } + + // Resources are scarce + + int sum_max = 0; + int sum_min = 0; + // the number of consumer that saturated their + // quota last time slice + int num_saturated = 0; + // the total resources that those saturated their + // quota used. This is used to calculate the mean + // of the saturating consumers, in order to + // balance their quotas for the next time slice. + size_type saturated_sum = 0; + for (It i = start; i != end; ++i) + { + resource_request& r = (*i).*res; + sum_max = saturated_add(sum_max, r.max); + assert(r.min < resource_request::inf); + assert(r.min >= 0); + assert(r.min <= r.max); + sum_min += r.min; + + // a consumer that uses 95% or more of its assigned + // quota is considered saturating + size_type used = r.used; + if (r.given == 0) continue; + if (used * 20 / r.given >= 19) + { + ++num_saturated; + saturated_sum += r.given; + } + } + + if (sum_max <= resources) + { + // it turns out that there's no competition for resources + // after all. + for (It i = start; i != end; ++i) + { + ((*i).*res).given = ((*i).*res).max; + } + return; + } + + if (sum_min >= resources) + { + // the amount of resources is smaller than + // the minimum resources to distribute, so + // give everyone the minimum + for (It i = start; i != end; ++i) + { + ((*i).*res).given = ((*i).*res).min; + } + return; + } + + // now, the "used" field will be used as a target value. + // the algorithm following this loop will then scale the + // used values to fit the available resources and store + // the scaled values as given. So, the ratios of the + // used values will be maintained. + for (It i = start; i != end; ++i) + { + resource_request& r = (*i).*res; + + int target; + size_type used = r.used; + if (r.given > 0 && used * 20 / r.given >= 19) + { + assert(num_saturated > 0); + target = div_round_up(saturated_sum, num_saturated); + target += div_round_up(target, 10); + } + else + { + target = r.used; + } + if (target > r.max) target = r.max; + else if (target < r.min) target = r.min; + + // move 12.5% towards the the target value + r.used = r.given + div_round_up(target - r.given, 8); + r.given = r.min; + } + + + resources = (std::max)(resources, sum_min); + int resources_to_distribute = (std::min)(resources, sum_max) - sum_min; + assert(resources_to_distribute >= 0); +#ifndef NDEBUG + int prev_resources_to_distribute = resources_to_distribute; +#endif + while (resources_to_distribute > 0) + { + // in order to scale, we need to calculate the sum of + // all the used values. + size_type total_used = 0; + size_type max_used = 0; + for (It i = start; i != end; ++i) + { + resource_request& r = (*i).*res; + if (r.given == r.max) continue; + + assert(r.given < r.max); + + max_used = (std::max)(max_used, (size_type)r.used + 1); + total_used += (size_type)r.used + 1; + } + + + size_type kNumer = resources_to_distribute; + size_type kDenom = total_used; + assert(kNumer >= 0); + assert(kDenom >= 0); + assert(kNumer <= (std::numeric_limits::max)()); + + if (kNumer * max_used <= kDenom) + { + kNumer = 1; + kDenom = max_used; + assert(kDenom >= 0); + } + + for (It i = start; i != end && resources_to_distribute > 0; ++i) + { + resource_request& r = (*i).*res; + if (r.given == r.max) continue; + + assert(r.given < r.max); + + size_type used = (size_type)r.used + 1; + if (used < 1) used = 1; + size_type to_give = used * kNumer / kDenom; + if (to_give > resources_to_distribute) + to_give = resources_to_distribute; + assert(to_give >= 0); + assert(to_give <= resources_to_distribute); +#ifndef NDEBUG + int tmp = resources_to_distribute; +#endif + resources_to_distribute -= give(r, (int)to_give); + assert(resources_to_distribute <= tmp); + assert(resources_to_distribute >= 0); + } + + assert(resources_to_distribute >= 0); + assert(resources_to_distribute < prev_resources_to_distribute); +#ifndef NDEBUG + prev_resources_to_distribute = resources_to_distribute; +#endif + } + assert(resources_to_distribute == 0); + } + + } // namespace libtorrent::aux +} + + +#endif diff --git a/encryption/libtorrent/include/libtorrent/aux_/session_impl.hpp b/encryption/libtorrent/include/libtorrent/aux_/session_impl.hpp new file mode 100644 index 000000000..fd24ef8f7 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/aux_/session_impl.hpp @@ -0,0 +1,542 @@ +/* + +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 +#include +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/torrent_handle.hpp" +#include "libtorrent/entry.hpp" +#include "libtorrent/torrent_info.hpp" +#include "libtorrent/socket.hpp" +#include "libtorrent/peer_connection.hpp" +#include "libtorrent/peer_id.hpp" +#include "libtorrent/policy.hpp" +#include "libtorrent/tracker_manager.hpp" +#include "libtorrent/peer_info.hpp" +#include "libtorrent/alert.hpp" +#include "libtorrent/fingerprint.hpp" +#include "libtorrent/debug.hpp" +#include "libtorrent/peer_request.hpp" +#include "libtorrent/piece_block_progress.hpp" +#include "libtorrent/ip_filter.hpp" +#include "libtorrent/config.hpp" +#include "libtorrent/session_settings.hpp" +#include "libtorrent/kademlia/dht_tracker.hpp" +#include "libtorrent/session_status.hpp" +#include "libtorrent/session.hpp" +#include "libtorrent/stat.hpp" +#include "libtorrent/file_pool.hpp" +#include "libtorrent/bandwidth_manager.hpp" +#include "libtorrent/natpmp.hpp" +#include "libtorrent/upnp.hpp" +#include "libtorrent/lsd.hpp" +#include "libtorrent/socket_type.hpp" +#include "libtorrent/connection_queue.hpp" + +namespace libtorrent +{ + + namespace aux + { + struct session_impl; + + // this data is shared between the main thread and the + // thread that initialize pieces + struct piece_checker_data + { + piece_checker_data() + : processing(false), progress(0.f), abort(false) {} + + boost::shared_ptr torrent_ptr; + boost::filesystem::path save_path; + + sha1_hash info_hash; + + void parse_resume_data( + const entry& rd + , const torrent_info& info + , std::string& error); + + std::vector piece_map; + std::vector unfinished_pieces; + std::vector block_info; + std::vector peers; + entry resume_data; + + // this is true if this torrent is being processed (checked) + // if it is not being processed, then it can be removed from + // the queue without problems, otherwise the abort flag has + // to be set. + bool processing; + + // is filled in by storage::initialize_pieces() + // and represents the progress. It should be a + // value in the range [0, 1] + float progress; + + // abort defaults to false and is typically + // filled in by torrent_handle when the user + // aborts the torrent + bool abort; + }; + + struct checker_impl: boost::noncopyable + { + checker_impl(session_impl& s): m_ses(s), m_abort(false) {} + void operator()(); + piece_checker_data* find_torrent(const sha1_hash& info_hash); + void remove_torrent(sha1_hash const& info_hash); + +#ifndef NDEBUG + void check_invariant() const; +#endif + + // when the files has been checked + // the torrent is added to the session + session_impl& m_ses; + + mutable boost::mutex m_mutex; + boost::condition m_cond; + + // a list of all torrents that are currently in queue + // or checking their files + std::deque > m_torrents; + std::deque > m_processing; + + bool m_abort; + }; + +#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING) + struct tracker_logger; +#endif + + // this is the link between the main thread and the + // thread started to run the main downloader loop + struct session_impl: boost::noncopyable + { +#ifndef NDEBUG + friend class ::libtorrent::peer_connection; +#endif + friend struct checker_impl; + friend class invariant_access; + typedef std::map + , boost::intrusive_ptr > + connection_map; + typedef std::map > torrent_map; + + session_impl( + std::pair listen_port_range + , fingerprint const& cl_fprint + , char const* listen_interface = "0.0.0.0"); + ~session_impl(); + +#ifndef TORRENT_DISABLE_EXTENSIONS + void add_extension(boost::function(torrent*)> ext); +#endif + void operator()(); + + void open_listen_port(); + + void async_accept(); + void on_incoming_connection(boost::shared_ptr const& s + , boost::weak_ptr const& as, asio::error_code const& e); + + // must be locked to access the data + // in this struct + typedef boost::recursive_mutex mutex_t; + mutable mutex_t m_mutex; + + boost::weak_ptr find_torrent(const sha1_hash& info_hash); + peer_id const& get_peer_id() const { return m_peer_id; } + + void close_connection(boost::intrusive_ptr const& p); + void connection_failed(boost::shared_ptr const& s + , tcp::endpoint const& a, char const* message); + + void set_settings(session_settings const& s); + session_settings const& settings() const { return m_settings; } + +#ifndef TORRENT_DISABLE_DHT + void add_dht_node(std::pair const& node); + void add_dht_node(udp::endpoint n); + 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 start_dht(entry const& startup_state); + void stop_dht(); + entry dht_state() const; +#endif + +#ifndef TORRENT_DISABLE_ENCRYPTION + void set_pe_settings(pe_settings const& settings); + pe_settings const& get_pe_settings() const { return m_pe_settings; } +#endif + + // called when a port mapping is successful, or a router returns + // a failure to map a port + void on_port_mapping(int tcp_port, int udp_port, std::string const& errmsg); + + bool is_aborted() const { return m_abort; } + + void set_ip_filter(ip_filter const& f); + + bool listen_on( + std::pair const& port_range + , const char* net_interface = 0); + bool is_listening() const; + + torrent_handle add_torrent( + torrent_info const& ti + , boost::filesystem::path const& save_path + , entry const& resume_data + , bool compact_mode + , int block_size + , storage_constructor_type sc); + + torrent_handle add_torrent( + char const* tracker_url + , sha1_hash const& info_hash + , char const* name + , boost::filesystem::path const& save_path + , entry const& resume_data + , bool compact_mode + , int block_size + , storage_constructor_type sc); + + void remove_torrent(torrent_handle const& h); + + std::vector get_torrents(); + + void set_severity_level(alert::severity_t s); + std::auto_ptr pop_alert(); + + int upload_rate_limit() const; + int download_rate_limit() const; + + void set_download_rate_limit(int bytes_per_second); + void set_upload_rate_limit(int bytes_per_second); + void set_max_half_open_connections(int limit); + void set_max_connections(int limit); + void set_max_uploads(int limit); + + int num_uploads() const; + int num_connections() const; + + session_status status() const; + void set_peer_id(peer_id const& id); + void set_key(int key); + unsigned short listen_port() const; + + void abort(); + + torrent_handle find_torrent_handle(sha1_hash const& info_hash); + + void announce_lsd(sha1_hash const& ih); + + void set_peer_proxy(proxy_settings const& s) + { m_peer_proxy = s; } + void set_web_seed_proxy(proxy_settings const& s) + { m_web_seed_proxy = s; } + void set_tracker_proxy(proxy_settings const& s) + { m_tracker_proxy = s; } + + proxy_settings const& peer_proxy() const + { return m_peer_proxy; } + proxy_settings const& web_seed_proxy() const + { return m_web_seed_proxy; } + proxy_settings const& tracker_proxy() const + { return m_tracker_proxy; } + +#ifndef TORRENT_DISABLE_DHT + void set_dht_proxy(proxy_settings const& s) + { m_dht_proxy = s; } + proxy_settings const& dht_proxy() const + { return m_dht_proxy; } +#endif + + // handles delayed alerts + alert_manager m_alerts; + +// private: + + void on_lsd_peer(tcp::endpoint peer, sha1_hash const& ih); + + // this is where all active sockets are stored. + // the selector can sleep while there's no activity on + // them + io_service m_io_service; + asio::strand m_strand; + + // the file pool that all storages in this session's + // torrents uses. It sets a limit on the number of + // open files by this session. + // file pool must be destructed after the torrents + // since they will still have references to it + // when they are destructed. + file_pool m_files; + + // this is a list of half-open tcp connections + // (only outgoing connections) + // this has to be one of the last + // members to be destructed + connection_queue m_half_open; + + // the bandwidth manager is responsible for + // handing out bandwidth to connections that + // asks for it, it can also throttle the + // rate. + bandwidth_manager m_dl_bandwidth_manager; + bandwidth_manager m_ul_bandwidth_manager; + + tracker_manager m_tracker_manager; + torrent_map m_torrents; + + // this maps sockets to their peer_connection + // object. It is the complete list of all connected + // peers. + connection_map m_connections; + + // filters incoming connections + ip_filter m_ip_filter; + + // the peer id that is generated at the start of the session + peer_id m_peer_id; + + // the key is an id that is used to identify the + // client with the tracker only. It is randomized + // at startup + int m_key; + + // the range of ports we try to listen on + std::pair m_listen_port_range; + + // the ip-address of the interface + // we are supposed to listen on. + // if the ip is set to zero, it means + // that we should let the os decide which + // interface to listen on + tcp::endpoint m_listen_interface; + + // 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 m_external_listen_port; + + boost::shared_ptr m_listen_socket; + + // the settings for the client + session_settings m_settings; + // the proxy settings for different + // kinds of connections + proxy_settings m_peer_proxy; + proxy_settings m_web_seed_proxy; + proxy_settings m_tracker_proxy; +#ifndef TORRENT_DISABLE_DHT + proxy_settings m_dht_proxy; +#endif + + // set to true when the session object + // is being destructed and the thread + // should exit + volatile bool m_abort; + + int m_max_uploads; + int m_max_connections; + + // statistics gathered from all torrents. + stat m_stat; + + // is false by default and set to true when + // the first incoming connection is established + // this is used to know if the client is behind + // NAT or not. + bool m_incoming_connection; + + void second_tick(asio::error_code const& e); + ptime m_last_tick; + +#ifndef TORRENT_DISABLE_DHT + boost::intrusive_ptr m_dht; + dht_settings m_dht_settings; + // if this is set to true, the dht listen port + // will be set to the same as the tcp listen port + // and will be synchronlized with it as it changes + // it defaults to true + bool m_dht_same_port; + + // see m_external_listen_port. This is the same + // but for the udp port used by the DHT. + int m_external_udp_port; +#endif + +#ifndef TORRENT_DISABLE_ENCRYPTION + pe_settings m_pe_settings; +#endif + + natpmp m_natpmp; + upnp m_upnp; + lsd m_lsd; + + // the timer used to fire the second_tick + deadline_timer m_timer; + + // the index of the torrent that will be offered to + // connect to a peer next time second_tick is called. + // This implements a round robin. + int m_next_connect_torrent; +#ifndef NDEBUG + void check_invariant(const char *place = 0); +#endif + +#ifdef TORRENT_STATS + // logger used to write bandwidth usage statistics + std::ofstream m_stats_logger; + int m_second_counter; +#endif +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + boost::shared_ptr create_log(std::string const& name + , int instance, bool append = true); + + // 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; + + public: + boost::shared_ptr m_logger; + private: +#endif + +#ifndef TORRENT_DISABLE_EXTENSIONS + typedef std::list(torrent*)> > extension_list_t; + + extension_list_t m_extensions; +#endif + + // data shared between the main thread + // and the checker thread + checker_impl m_checker_impl; + + // the main working thread + boost::scoped_ptr m_thread; + + // the thread that calls initialize_pieces() + // on all torrents before they start downloading + boost::scoped_ptr m_checker_thread; + }; + +#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING) + struct tracker_logger : request_callback + { + tracker_logger(session_impl& ses): m_ses(ses) {} + void tracker_warning(std::string const& str) + { + debug_log("*** tracker warning: " + str); + } + + void tracker_response(tracker_request const& + , std::vector& peers + , int interval + , int complete + , int incomplete) + { + std::stringstream s; + s << "TRACKER RESPONSE:\n" + "interval: " << interval << "\n" + "peers:\n"; + for (std::vector::const_iterator i = peers.begin(); + i != peers.end(); ++i) + { + s << " " << std::setfill(' ') << std::setw(16) << i->ip + << " " << std::setw(5) << std::dec << i->port << " "; + if (!i->pid.is_all_zeros()) s << " " << i->pid; + s << "\n"; + } + debug_log(s.str()); + } + + void tracker_request_timed_out( + tracker_request const&) + { + debug_log("*** tracker timed out"); + } + + void tracker_request_error( + tracker_request const& + , int response_code + , const std::string& str) + { + debug_log(std::string("*** tracker error: ") + + boost::lexical_cast(response_code) + ": " + + str); + } + + void debug_log(const std::string& line) + { + (*m_ses.m_logger) << line << "\n"; + } + session_impl& m_ses; + }; +#endif + + } +} + + +#endif + diff --git a/encryption/libtorrent/include/libtorrent/bandwidth_manager.hpp b/encryption/libtorrent/include/libtorrent/bandwidth_manager.hpp new file mode 100644 index 000000000..dbf93aa77 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/bandwidth_manager.hpp @@ -0,0 +1,228 @@ +/* + +Copyright (c) 2007, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_BANDWIDTH_MANAGER_HPP_INCLUDED +#define TORRENT_BANDWIDTH_MANAGER_HPP_INCLUDED + +#include "libtorrent/socket.hpp" + +#include +#include +#include +#include +#include +#include +#include + +using boost::weak_ptr; +using boost::shared_ptr; +using boost::intrusive_ptr; +using boost::bind; + +namespace libtorrent +{ + + class peer_connection; + class torrent; + + // the maximum block of bandwidth quota to + // hand out is 33kB. The block size may + // be smaller on lower limits + const int max_bandwidth_block_size = 33000; + const int min_bandwidth_block_size = 4000; + +#if defined TORRENT_LOGGING || defined TORRENT_VERBOSE_LOGGING + namespace aux + { + struct session_impl; + } +#endif + +struct history_entry +{ + history_entry(intrusive_ptr p, weak_ptr t + , int a, ptime exp); + ptime expires_at; + int amount; + intrusive_ptr peer; + weak_ptr tor; +}; + +struct bw_queue_entry +{ + bw_queue_entry(boost::intrusive_ptr const& pe, bool no_prio); + boost::intrusive_ptr peer; + bool non_prioritized; +}; + +// member of peer_connection +struct bandwidth_limit +{ + static const int inf = boost::integer_traits::const_max; + + bandwidth_limit() + : m_quota_left(0) + , m_local_limit(inf) + , m_current_rate(0) + {} + + void throttle(int limit) + { + m_local_limit = limit; + } + + int throttle() const + { + return m_local_limit; + } + + void assign(int amount) + { + assert(amount > 0); + m_current_rate += amount; + m_quota_left += amount; + } + + void use_quota(int amount) + { + assert(amount <= m_quota_left); + m_quota_left -= amount; + } + + int quota_left() const + { + return (std::max)(m_quota_left, 0); + } + + void expire(int amount) + { + assert(amount >= 0); + m_current_rate -= amount; + } + + int max_assignable() const + { + if (m_local_limit == inf) return inf; + if (m_local_limit <= m_current_rate) return 0; + return m_local_limit - m_current_rate; + } + +private: + + // this is the amount of bandwidth we have + // been assigned without using yet. i.e. + // the bandwidth that we use up every time + // we receive or send a message. Once this + // hits zero, we need to request more + // bandwidth from the torrent which + // in turn will request bandwidth from + // the bandwidth manager + int m_quota_left; + + // the local limit is the number of bytes + // per window size we are allowed to use. + int m_local_limit; + + // the current rate is the number of + // bytes we have been assigned within + // the window size. + int m_current_rate; +}; + +struct bandwidth_manager +{ + bandwidth_manager(io_service& ios, int channel); + + void throttle(int limit) + { + mutex_t::scoped_lock l(m_mutex); + assert(limit >= 0); + m_limit = limit; + } + + int throttle() const + { + mutex_t::scoped_lock l(m_mutex); + return m_limit; + } + + // 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 + void request_bandwidth(intrusive_ptr peer + , bool non_prioritized); + +#ifndef NDEBUG + void check_invariant() const; +#endif +#if defined TORRENT_LOGGING || defined TORRENT_VERBOSE_LOGGING + aux::session_impl* m_ses; +#endif + +private: + + void add_history_entry(history_entry const& e); + void on_history_expire(asio::error_code const& e); + void hand_out_bandwidth(); + + typedef boost::mutex mutex_t; + mutable mutex_t m_mutex; + + // the io_service used for the timer + io_service& m_ios; + + // the timer that is waiting for the entries + // in the history queue to expire (slide out + // of the history window) + deadline_timer m_history_timer; + + // the rate limit (bytes per second) + int m_limit; + + // the sum of all recently handed out bandwidth blocks + int m_current_quota; + + // these are the consumers that want bandwidth + std::deque m_queue; + + // these are the consumers that have received bandwidth + // that will expire + std::deque m_history; + + // this is the channel within the consumers + // that bandwidth is assigned to (upload or download) + int m_channel; +}; + +} + +#endif diff --git a/encryption/libtorrent/include/libtorrent/bencode.hpp b/encryption/libtorrent/include/libtorrent/bencode.hpp new file mode 100755 index 000000000..a142b5864 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/bencode.hpp @@ -0,0 +1,299 @@ +/* + +Copyright (c) 2003, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + + +#ifndef TORRENT_BENCODE_HPP_INCLUDED +#define TORRENT_BENCODE_HPP_INCLUDED + + + +/* + * This file declares the following functions: + * + *---------------------------------- + * template + * void libtorrent::bencode(OutIt out, const libtorrent::entry& e); + * + * Encodes a message entry with bencoding into the output + * iterator given. The bencoding is described in the BitTorrent + * protocol description document OutIt must be an OutputIterator + * of type char. This may throw libtorrent::invalid_encoding if + * the entry contains invalid nodes (undefined_t for example). + * + *---------------------------------- + * template + * libtorrent::entry libtorrent::bdecode(InIt start, InIt end); + * + * Decodes the buffer given by the start and end iterators + * and returns the decoded entry. InIt must be an InputIterator + * of type char. May throw libtorrent::invalid_encoding if + * the string is not correctly bencoded. + * + */ + + + + +#include + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/entry.hpp" +#include "libtorrent/config.hpp" + +#if defined(_MSC_VER) +namespace std +{ + using ::isdigit; + using ::atoi; +}; + +#define for if (false) {} else for +#endif + + +namespace libtorrent +{ + + struct TORRENT_EXPORT invalid_encoding: std::exception + { + virtual const char* what() const throw() { return "invalid bencoding"; } + }; + + namespace detail + { + template + void write_string(OutIt& out, const std::string& val) + { + std::string::const_iterator end = val.begin() + val.length(); + std::copy(val.begin(), end, out); + } + + TORRENT_EXPORT char const* integer_to_str(char* buf, int size, entry::integer_type val); + + template + void write_integer(OutIt& out, entry::integer_type val) + { + // the stack allocated buffer for keeping the + // decimal representation of the number can + // not hold number bigger than this: + BOOST_STATIC_ASSERT(sizeof(entry::integer_type) <= 8); + char buf[21]; + for (char const* str = integer_to_str(buf, 21, val); + *str != 0; ++str) + { + *out = *str; + ++out; + } + } + + template + void write_char(OutIt& out, char c) + { + *out = c; + ++out; + } + + template + std::string read_until(InIt& in, InIt end, char end_token) + { + if (in == end) throw invalid_encoding(); + std::string ret; + while (*in != end_token) + { + ret += *in; + ++in; + if (in == end) throw invalid_encoding(); + } + return ret; + } + + template + void read_string(InIt& in, InIt end, int len, std::string& str) + { + assert(len >= 0); + for (int i = 0; i < len; ++i) + { + if (in == end) throw invalid_encoding(); + str += *in; + ++in; + } + } + + template + void bencode_recursive(OutIt& out, const entry& e) + { + switch(e.type()) + { + case entry::int_t: + write_char(out, 'i'); + write_integer(out, e.integer()); + write_char(out, 'e'); + break; + case entry::string_t: + write_integer(out, e.string().length()); + write_char(out, ':'); + write_string(out, e.string()); + break; + case entry::list_t: + write_char(out, 'l'); + for (entry::list_type::const_iterator i = e.list().begin(); i != e.list().end(); ++i) + bencode_recursive(out, *i); + write_char(out, 'e'); + break; + case entry::dictionary_t: + write_char(out, 'd'); + for (entry::dictionary_type::const_iterator i = e.dict().begin(); + i != e.dict().end(); ++i) + { + // write key + write_integer(out, i->first.length()); + write_char(out, ':'); + write_string(out, i->first); + // write value + bencode_recursive(out, i->second); + } + write_char(out, 'e'); + break; + default: + // do nothing + break; + } + } + + template + void bdecode_recursive(InIt& in, InIt end, entry& ret) + { + if (in == end) throw invalid_encoding(); + switch (*in) + { + + // ---------------------------------------------- + // integer + case 'i': + { + ++in; // 'i' + std::string val = read_until(in, end, 'e'); + assert(*in == 'e'); + ++in; // 'e' + ret = entry(entry::int_t); + ret.integer() = boost::lexical_cast(val); + } break; + + // ---------------------------------------------- + // list + case 'l': + { + ret = entry(entry::list_t); + ++in; // 'l' + while (*in != 'e') + { + ret.list().push_back(entry()); + entry& e = ret.list().back(); + bdecode_recursive(in, end, e); + if (in == end) throw invalid_encoding(); + } + assert(*in == 'e'); + ++in; // 'e' + } break; + + // ---------------------------------------------- + // dictionary + case 'd': + { + ret = entry(entry::dictionary_t); + ++in; // 'd' + while (*in != 'e') + { + entry key; + bdecode_recursive(in, end, key); + entry& e = ret[key.string()]; + bdecode_recursive(in, end, e); + if (in == end) throw invalid_encoding(); + } + assert(*in == 'e'); + ++in; // 'e' + } break; + + // ---------------------------------------------- + // string + default: + if (isdigit((unsigned char)*in)) + { + std::string len_s = read_until(in, end, ':'); + assert(*in == ':'); + ++in; // ':' + int len = std::atoi(len_s.c_str()); + ret = entry(entry::string_t); + read_string(in, end, len, ret.string()); + } + else + { + throw invalid_encoding(); + } + } + } + } + + template + void bencode(OutIt out, const entry& e) + { + detail::bencode_recursive(out, e); + } + + template + entry bdecode(InIt start, InIt end) + { + try + { + entry e; + detail::bdecode_recursive(start, end, e); + return e; + } + catch(type_error&) + { + throw invalid_encoding(); + } + } + +} + +#endif // TORRENT_BENCODE_HPP_INCLUDED diff --git a/encryption/libtorrent/include/libtorrent/bt_peer_connection.hpp b/encryption/libtorrent/include/libtorrent/bt_peer_connection.hpp new file mode 100755 index 000000000..2a5000011 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/bt_peer_connection.hpp @@ -0,0 +1,379 @@ +/* + +Copyright (c) 2003 - 2006, Arvid Norberg +Copyright (c) 2007, 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 + +#include "libtorrent/debug.hpp" + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/buffer.hpp" +#include "libtorrent/peer_connection.hpp" +#include "libtorrent/socket.hpp" +#include "libtorrent/peer_id.hpp" +#include "libtorrent/storage.hpp" +#include "libtorrent/stat.hpp" +#include "libtorrent/alert.hpp" +#include "libtorrent/torrent_handle.hpp" +#include "libtorrent/torrent.hpp" +#include "libtorrent/allocate_resources.hpp" +#include "libtorrent/peer_request.hpp" +#include "libtorrent/piece_block_progress.hpp" +#include "libtorrent/config.hpp" +#include "libtorrent/pe_crypto.hpp" + +namespace libtorrent +{ + class torrent; + + namespace detail + { + struct session_impl; + } + + class TORRENT_EXPORT bt_peer_connection + : public peer_connection + { + friend class invariant_access; + public: + + // this is the constructor where the we are the active part. + // The peer_conenction should handshake and verify that the + // other end has the correct id + bt_peer_connection( + aux::session_impl& ses + , boost::weak_ptr t + , boost::shared_ptr s + , tcp::endpoint const& remote + , policy::peer* peerinfo); + + // with this constructor we have been contacted and we still don't + // know which torrent the connection belongs to + bt_peer_connection( + aux::session_impl& ses + , boost::shared_ptr s + , policy::peer* peerinfo); + + ~bt_peer_connection(); + +#ifndef TORRENT_DISABLE_ENCRYPTION + bool supports_encryption() const + { return m_encrypted; } +#endif + + enum message_type + { + // standard messages + msg_choke = 0, + msg_unchoke, + msg_interested, + msg_not_interested, + msg_have, + msg_bitfield, + msg_request, + msg_piece, + msg_cancel, + msg_dht_port, + // extension protocol message + msg_extended = 20, + + num_supported_messages + }; + + // called from the main loop when this connection has any + // work to do. + + void on_sent(asio::error_code const& error + , std::size_t bytes_transferred); + void on_receive(asio::error_code const& error + , std::size_t bytes_transferred); + + virtual void get_specific_peer_info(peer_info& p) const; + virtual bool in_handshake() const; + +#ifndef TORRENT_DISABLE_EXTENSIONS + bool support_extensions() const { return m_supports_extensions; } + + template + T* supports_extension() const + { + for (extension_list_t::const_iterator i = m_extensions.begin() + , end(m_extensions.end()); i != end; ++i) + { + T* ret = dynamic_cast(i->get()); + if (ret) return ret; + } + return 0; + } +#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 on_keepalive(); + void on_choke(int received); + void on_unchoke(int received); + void on_interested(int received); + void on_not_interested(int received); + void on_have(int received); + void on_bitfield(int received); + void on_request(int received); + void on_piece(int received); + void on_cancel(int received); + void on_dht_port(int received); + + void on_extended(int received); + + void on_extended_handshake(); + + typedef void (bt_peer_connection::*message_handler)(int received); + + // the following functions appends messages + // to the send buffer + void write_choke(); + void write_unchoke(); + void write_interested(); + void write_not_interested(); + void write_request(peer_request const& r); + void write_cancel(peer_request const& r); + void write_bitfield(std::vector const& bitfield); + void write_have(int index); + void write_piece(peer_request const& r); + void write_handshake(); +#ifndef TORRENT_DISABLE_EXTENSIONS + void write_extensions(); +#endif + void write_chat_message(const std::string& msg); + void write_metadata(std::pair req); + void write_metadata_request(std::pair req); + void write_keepalive(); + void write_dht_port(int listen_port); + void on_connected() {} + void on_metadata(); + +#ifndef NDEBUG + void check_invariant() const; + ptime m_last_choke; +#endif + + private: + + bool dispatch_message(int received); + // returns the block currently being + // downloaded. And the progress of that + // block. If the peer isn't downloading + // a piece for the moment, the boost::optional + // will be invalid. + boost::optional downloading_piece_progress() const; + +#ifndef TORRENT_DISABLE_ENCRYPTION + + // 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(buffer::interval& write_buf, + int crypto_field, int pad_size); + + // stream key (info hash of attached torrent) + // secret is the DH shared secret + // initializes m_RC4_handler + void init_pe_RC4_handler(char const* secret, sha1_hash const& stream_key); + + // 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 + void send_buffer(char* begin, char* end); + buffer::interval allocate_send_buffer(int size); + void setup_send(); + + // 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; +#endif + + enum state + { +#ifndef TORRENT_DISABLE_ENCRYPTION + 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 + }; + +#ifndef TORRENT_DISABLE_ENCRYPTION + enum + { + handshake_len = 68, + dh_key_len = 96 + }; +#endif + + std::string m_client_version; + + // state of on_receive + state m_state; + + // the timeout in seconds + int m_timeout; + + static const message_handler m_message_handler[num_supported_messages]; + + // this is a queue of ranges that describes + // where in the send buffer actual payload + // data is located. This is currently + // only used to be able to gather statistics + // seperately on payload and protocol data. + struct range + { + range(int s, int l) + : start(s) + , length(l) + { + assert(s >= 0); + assert(l > 0); + } + int start; + int length; + }; + static bool range_below_zero(const range& r) + { return r.start < 0; } + std::deque m_payloads; + +#ifndef TORRENT_DISABLE_EXTENSIONS + // this is set to true if the handshake from + // the peer indicated that it supports the + // extension protocol + bool m_supports_extensions; +#endif + bool m_supports_dht_port; + +#ifndef TORRENT_DISABLE_ENCRYPTION + // this is set to true after the encryption method has been + // succesfully negotiated (either plaintext or rc4), to signal + // automatic encryption/decryption. + bool m_encrypted; + + // true if rc4, false if plaintext + bool m_rc4_encrypted; + + // used to disconnect peer if sync points are not found within + // the maximum number of bytes + int m_sync_bytes_read; + + // hold information about latest allocated send buffer + // need to check for non zero (begin, end) for operations with this + buffer::interval m_enc_send_buffer; + + // initialized during write_pe1_2_dhkey, and destroyed on + // creation of m_RC4_handler. Cannot reinitialize once + // initialized. + boost::scoped_ptr m_DH_key_exchange; + + // if RC4 is negotiated, this is used for + // encryption/decryption during the entire session. Destroyed + // if plaintext is selected + boost::scoped_ptr m_RC4_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 // #ifndef TORRENT_DISABLE_ENCRYPTION + +#ifndef NDEBUG + // this is set to true when the client's + // bitfield is sent to this peer + bool m_sent_bitfield; + + bool m_in_constructor; +#endif + + }; +} + +#endif // TORRENT_BT_PEER_CONNECTION_HPP_INCLUDED + diff --git a/encryption/libtorrent/include/libtorrent/buffer.hpp b/encryption/libtorrent/include/libtorrent/buffer.hpp new file mode 100644 index 000000000..0cb44225a --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/buffer.hpp @@ -0,0 +1,453 @@ +/* +Copyright (c) 2003 - 2005, Arvid Norberg, Daniel Wallin +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of Rasterbar Software nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef LIBTORRENT_BUFFER_HPP +#define LIBTORRENT_BUFFER_HPP + +//#define TORRENT_BUFFER_DEBUG + +#include "libtorrent/invariant_check.hpp" +#include + +namespace libtorrent { + +class buffer +{ +public: + struct interval + { + interval(char* begin, char* end) + : begin(begin) + , end(end) + {} + + char operator[](int index) const + { + assert(begin + index < end); + return begin[index]; + } + + int left() const { assert(end >= begin); return end - begin; } + + char* begin; + char* end; + }; + + struct const_interval + { + const_interval(char const* begin, char const* end) + : begin(begin) + , end(end) + {} + + char operator[](int index) const + { + assert(begin + index < end); + return begin[index]; + } + + bool operator==(const const_interval& p_interval) + { + return (begin == p_interval.begin + && end == p_interval.end); + } + + int left() const { assert(end >= begin); return end - begin; } + + char const* begin; + char const* end; + }; + + typedef std::pair interval_type; + + buffer(std::size_t n = 0); + ~buffer(); + + interval allocate(std::size_t n); + void insert(char const* first, char const* last); + void erase(std::size_t n); + std::size_t size() const; + std::size_t capacity() const; + void reserve(std::size_t n); + interval_type data() const; + bool empty() const; + + std::size_t space_left() const; + + char const* raw_data() const + { + return m_first; + } + +#ifndef NDEBUG + void check_invariant() const; +#endif + +private: + char* m_first; + char* m_last; + char* m_write_cursor; + char* m_read_cursor; + char* m_read_end; + bool m_empty; +#ifdef TORRENT_BUFFER_DEBUG + mutable std::vector m_debug; + mutable int m_pending_copy; +#endif +}; + +inline buffer::buffer(std::size_t n) + : m_first((char*)::operator new(n)) + , m_last(m_first + n) + , m_write_cursor(m_first) + , m_read_cursor(m_first) + , m_read_end(m_last) + , m_empty(true) +{ +#ifdef TORRENT_BUFFER_DEBUG + m_pending_copy = 0; +#endif +} + +inline buffer::~buffer() +{ + ::operator delete (m_first); +} + +inline buffer::interval buffer::allocate(std::size_t n) +{ + assert(m_read_cursor <= m_read_end || m_empty); + + INVARIANT_CHECK; + +#ifdef TORRENT_BUFFER_DEBUG + if (m_pending_copy) + { + std::copy(m_write_cursor - m_pending_copy, m_write_cursor + , m_debug.end() - m_pending_copy); + m_pending_copy = 0; + } + m_debug.resize(m_debug.size() + n); + m_pending_copy = n; +#endif + if (m_read_cursor < m_write_cursor || m_empty) + { + // ..R***W.. + if (m_last - m_write_cursor >= (std::ptrdiff_t)n) + { + interval ret(m_write_cursor, m_write_cursor + n); + m_write_cursor += n; + m_read_end = m_write_cursor; + assert(m_read_cursor <= m_read_end); + if (n) m_empty = false; + return ret; + } + + if (m_read_cursor - m_first >= (std::ptrdiff_t)n) + { + m_read_end = m_write_cursor; + interval ret(m_first, m_first + n); + m_write_cursor = m_first + n; + assert(m_read_cursor <= m_read_end); + if (n) m_empty = false; + return ret; + } + + reserve(capacity() + n - (m_last - m_write_cursor)); + assert(m_last - m_write_cursor >= (std::ptrdiff_t)n); + interval ret(m_write_cursor, m_write_cursor + n); + m_write_cursor += n; + m_read_end = m_write_cursor; + if (n) m_empty = false; + assert(m_read_cursor <= m_read_end); + return ret; + + } + //**W...R** + if (m_read_cursor - m_write_cursor >= (std::ptrdiff_t)n) + { + interval ret(m_write_cursor, m_write_cursor + n); + m_write_cursor += n; + if (n) m_empty = false; + return ret; + } + reserve(capacity() + n - (m_read_cursor - m_write_cursor)); + assert(m_read_cursor - m_write_cursor >= (std::ptrdiff_t)n); + interval ret(m_write_cursor, m_write_cursor + n); + m_write_cursor += n; + if (n) m_empty = false; + return ret; +} + +inline void buffer::insert(char const* first, char const* last) +{ + INVARIANT_CHECK; + + std::size_t n = last - first; + +#ifdef TORRENT_BUFFER_DEBUG + if (m_pending_copy) + { + std::copy(m_write_cursor - m_pending_copy, m_write_cursor + , m_debug.end() - m_pending_copy); + m_pending_copy = 0; + } + m_debug.insert(m_debug.end(), first, last); +#endif + + if (space_left() < n) + { + reserve(capacity() + n); + } + + m_empty = false; + + char const* end = (m_last - m_write_cursor) < (std::ptrdiff_t)n ? + m_last : m_write_cursor + n; + + std::size_t copied = end - m_write_cursor; + std::memcpy(m_write_cursor, first, copied); + + m_write_cursor += copied; + if (m_write_cursor > m_read_end) m_read_end = m_write_cursor; + first += copied; + n -= copied; + + if (n == 0) return; + + assert(m_write_cursor == m_last); + m_write_cursor = m_first; + + memcpy(m_write_cursor, first, n); + m_write_cursor += n; +} + +inline void buffer::erase(std::size_t n) +{ + INVARIANT_CHECK; + + if (n == 0) return; + assert(!m_empty); + +#ifndef NDEBUG + int prev_size = size(); +#endif + assert(m_read_cursor <= m_read_end); + m_read_cursor += n; + if (m_read_cursor > m_read_end) + { + m_read_cursor = m_first + (m_read_cursor - m_read_end); + assert(m_read_cursor <= m_write_cursor); + } + + m_empty = m_read_cursor == m_write_cursor; + + assert(prev_size - n == size()); + +#ifdef TORRENT_BUFFER_DEBUG + m_debug.erase(m_debug.begin(), m_debug.begin() + n); +#endif +} + +inline std::size_t buffer::size() const +{ + // ...R***W. + if (m_read_cursor < m_write_cursor) + { + return m_write_cursor - m_read_cursor; + } + // ***W..R* + else + { + if (m_empty) return 0; + return (m_write_cursor - m_first) + (m_read_end - m_read_cursor); + } +} + +inline std::size_t buffer::capacity() const +{ + return m_last - m_first; +} + +inline void buffer::reserve(std::size_t size) +{ + std::size_t n = (std::size_t)(capacity() * 1.f); + if (n < size) n = size; + + char* buf = (char*)::operator new(n); + char* old = m_first; + + if (m_read_cursor < m_write_cursor) + { + // ...R***W.<>. + std::memcpy( + buf + (m_read_cursor - m_first) + , m_read_cursor + , m_write_cursor - m_read_cursor + ); + + m_write_cursor = buf + (m_write_cursor - m_first); + m_read_cursor = buf + (m_read_cursor - m_first); + m_read_end = m_write_cursor; + m_first = buf; + m_last = buf + n; + } + else + { + // **W..<>.R** + std::size_t skip = n - (m_last - m_first); + + std::memcpy(buf, m_first, m_write_cursor - m_first); + std::memcpy( + buf + (m_read_cursor - m_first) + skip + , m_read_cursor + , m_last - m_read_cursor + ); + + m_write_cursor = buf + (m_write_cursor - m_first); + + if (!m_empty) + { + m_read_cursor = buf + (m_read_cursor - m_first) + skip; + m_read_end = buf + (m_read_end - m_first) + skip; + } + else + { + m_read_cursor = m_write_cursor; + m_read_end = m_write_cursor; + } + + m_first = buf; + m_last = buf + n; + } + + ::operator delete (old); +} + +#ifndef NDEBUG +inline void buffer::check_invariant() const +{ + assert(m_read_end >= m_read_cursor); + assert(m_last >= m_read_cursor); + assert(m_last >= m_write_cursor); + assert(m_last >= m_first); + assert(m_first <= m_read_cursor); + assert(m_first <= m_write_cursor); +#ifdef TORRENT_BUFFER_DEBUG + int a = m_debug.size(); + int b = size(); + (void)a; + (void)b; + assert(m_debug.size() == size()); +#endif +} +#endif + +inline buffer::interval_type buffer::data() const +{ + INVARIANT_CHECK; + +#ifdef TORRENT_BUFFER_DEBUG + if (m_pending_copy) + { + std::copy(m_write_cursor - m_pending_copy, m_write_cursor + , m_debug.end() - m_pending_copy); + m_pending_copy = 0; + } +#endif + + // ...R***W. + if (m_read_cursor < m_write_cursor) + { +#ifdef TORRENT_BUFFER_DEBUG + assert(m_debug.size() == size()); + assert(std::equal(m_debug.begin(), m_debug.end(), m_read_cursor)); +#endif + return interval_type( + const_interval(m_read_cursor, m_write_cursor) + , const_interval(m_last, m_last) + ); + } + // **W...R** + else + { + if (m_read_cursor == m_read_end) + { +#ifdef TORRENT_BUFFER_DEBUG + assert(m_debug.size() == size()); + assert(std::equal(m_debug.begin(), m_debug.end(), m_first)); +#endif + + return interval_type( + const_interval(m_first, m_write_cursor) + , const_interval(m_last, m_last)); + } +#ifdef TORRENT_BUFFER_DEBUG + assert(m_debug.size() == size()); + assert(std::equal(m_debug.begin(), m_debug.begin() + (m_read_end + - m_read_cursor), m_read_cursor)); + assert(std::equal(m_debug.begin() + (m_read_end - m_read_cursor), m_debug.end() + , m_first)); +#endif + + assert(m_read_cursor <= m_read_end || m_empty); + return interval_type( + const_interval(m_read_cursor, m_read_end) + , const_interval(m_first, m_write_cursor) + ); + } +} + +inline bool buffer::empty() const +{ + return m_empty; +} + +inline std::size_t buffer::space_left() const +{ + if (m_empty) return m_last - m_first; + + // ...R***W. + if (m_read_cursor < m_write_cursor) + { + return (m_last - m_write_cursor) + (m_read_cursor - m_first); + } + // ***W..R* + else + { + return m_read_cursor - m_write_cursor; + } +} + +} + +#endif // LIBTORRENT_BUFFER_HPP + diff --git a/encryption/libtorrent/include/libtorrent/config.hpp b/encryption/libtorrent/include/libtorrent/config.hpp new file mode 100755 index 000000000..c8d86955e --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/config.hpp @@ -0,0 +1,66 @@ +/* + +Copyright (c) 2005, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_CONFIG_HPP_INCLUDED +#define TORRENT_CONFIG_HPP_INCLUDED + +#include + +#if defined(__GNUC__) && __GNUC__ >= 4 + +# if defined(TORRENT_BUILDING_SHARED) || defined(TORRENT_LINKING_SHARED) +# define TORRENT_EXPORT __attribute__ ((visibility("default"))) +# else +# define TORRENT_EXPORT +# endif + +#elif defined(__GNUC__) + +# define TORRENT_EXPORT + +#elif defined(BOOST_MSVC) + +# if defined(TORRENT_BUILDING_SHARED) +# define TORRENT_EXPORT __declspec(dllexport) +# elif defined(TORRENT_LINKING_SHARED) +# define TORRENT_EXPORT __declspec(dllimport) +# else +# define TORRENT_EXPORT +# endif + +#else +# define TORRENT_EXPORT +#endif + + +#endif // TORRENT_CONFIG_HPP_INCLUDED + diff --git a/encryption/libtorrent/include/libtorrent/connection_queue.hpp b/encryption/libtorrent/include/libtorrent/connection_queue.hpp new file mode 100644 index 000000000..05a8a61fe --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/connection_queue.hpp @@ -0,0 +1,96 @@ +/* + +Copyright (c) 2007, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_CONNECTION_QUEUE +#define TORRENT_CONNECTION_QUEUE + +#include +#include +#include +#include "libtorrent/socket.hpp" +#include "libtorrent/time.hpp" + +namespace libtorrent +{ + +class connection_queue : public boost::noncopyable +{ +public: + connection_queue(io_service& ios); + + bool free_slots() const; + + void enqueue(boost::function const& on_connect + , boost::function const& on_timeout + , time_duration timeout); + void done(int ticket); + void limit(int limit); + int limit() const; + +#ifndef NDEBUG + + void check_invariant() const; + +#endif + +private: + + void try_connect(); + void on_timeout(asio::error_code const& e); + + struct entry + { + entry(): connecting(false), ticket(0), expires(max_time()) {} + // called when the connection is initiated + boost::function on_connect; + // called if done hasn't been called within the timeout + boost::function on_timeout; + bool connecting; + int ticket; + ptime expires; + time_duration timeout; + }; + + std::list m_queue; + + // the next ticket id a connection will be given + int m_next_ticket; + int m_num_connecting; + int m_half_open_limit; + + deadline_timer m_timer; +}; + +} + +#endif + diff --git a/encryption/libtorrent/include/libtorrent/debug.hpp b/encryption/libtorrent/include/libtorrent/debug.hpp new file mode 100755 index 000000000..39c4b0222 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/debug.hpp @@ -0,0 +1,81 @@ +/* + +Copyright (c) 2003, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_DEBUG_HPP_INCLUDED +#define TORRENT_DEBUG_HPP_INCLUDED + +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + + +namespace libtorrent +{ + // DEBUG API + + struct logger + { + logger(boost::filesystem::path const& filename, int instance, bool append = true) + { + using namespace boost::filesystem; + path dir(complete("libtorrent_logs" + boost::lexical_cast(instance))); + if (!exists(dir)) create_directories(dir); + m_file.open(dir / filename, std::ios_base::out | (append ? std::ios_base::app : std::ios_base::out)); + *this << "\n\n\n*** starting log ***\n"; + } + + template + logger& operator<<(T const& v) + { + m_file << v; + m_file.flush(); + return *this; + } + + boost::filesystem::ofstream m_file; + }; + +} + +#endif // TORRENT_DEBUG_HPP_INCLUDED diff --git a/encryption/libtorrent/include/libtorrent/entry.hpp b/encryption/libtorrent/include/libtorrent/entry.hpp new file mode 100755 index 000000000..31a78b972 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/entry.hpp @@ -0,0 +1,276 @@ +/* + +Copyright (c) 2003, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_ENTRY_HPP_INCLUDED +#define TORRENT_ENTRY_HPP_INCLUDED + +/* + * + * This file declares the entry class. It is a + * variant-type that can be an integer, list, + * dictionary (map) or a string. This type is + * used to hold bdecoded data (which is the + * encoding BitTorrent messages uses). + * + * it has 4 accessors to access the actual + * type of the object. They are: + * integer() + * string() + * list() + * dict() + * The actual type has to match the type you + * are asking for, otherwise you will get an + * assertion failure. + * When you default construct an entry, it is + * uninitialized. You can initialize it through the + * assignment operator, copy-constructor or + * the constructor that takes a data_type enum. + * + * + */ + + +#include +#include +#include +#include +#include +#include + +#include "libtorrent/size_type.hpp" +#include "libtorrent/config.hpp" + +namespace libtorrent +{ + + struct TORRENT_EXPORT type_error: std::runtime_error + { + type_error(const char* error): std::runtime_error(error) {} + }; + + namespace detail + { + template + struct max2 { enum { value = v1>v2?v1:v2 }; }; + + template + struct max3 + { + enum + { + temp = max2::value, + value = temp>v3?temp:v3 + }; + }; + + template + struct max4 + { + enum + { + temp = max3::value, + value = temp>v4?temp:v4 + }; + }; + } + + class entry; + + class TORRENT_EXPORT entry + { + public: + + // the key is always a string. If a generic entry would be allowed + // as a key, sorting would become a problem (e.g. to compare a string + // to a list). The definition doesn't mention such a limit though. + typedef std::map dictionary_type; + typedef std::string string_type; + typedef std::list list_type; + typedef size_type integer_type; + + enum data_type + { + int_t, + string_t, + list_t, + dictionary_t, + undefined_t + }; + + data_type type() const; + + entry(dictionary_type const&); + entry(string_type const&); + entry(list_type const&); + entry(integer_type const&); + + entry(); + entry(data_type t); + entry(entry const& e); + ~entry(); + + bool operator==(entry const& e) const; + + void operator=(entry const&); + void operator=(dictionary_type const&); + void operator=(string_type const&); + void operator=(list_type const&); + void operator=(integer_type const&); + + integer_type& integer(); + const integer_type& integer() const; + string_type& string(); + const string_type& string() const; + list_type& list(); + const list_type& list() const; + dictionary_type& dict(); + const dictionary_type& dict() const; + + // these functions requires that the entry + // is a dictionary, otherwise they will throw + entry& operator[](char const* key); + entry& operator[](std::string const& key); + const entry& operator[](char const* key) const; + const entry& operator[](std::string const& key) const; + entry* find_key(char const* key); + entry const* find_key(char const* key) const; + + void print(std::ostream& os, int indent = 0) const; + + private: + + void construct(data_type t); + void copy(const entry& e); + void destruct(); + + data_type m_type; + +#if defined(_MSC_VER) && _MSC_VER < 1310 + // workaround for msvc-bug. + // assumes sizeof(map) == sizeof(map) + // and sizeof(list) == sizeof(list) + union + { + char data[ + detail::max4) + , sizeof(std::map) + , sizeof(string_type) + , sizeof(integer_type)>::value]; + integer_type dummy_aligner; + }; +#else + union + { + char data[detail::max4::value]; + integer_type dummy_aligner; + }; +#endif + + }; + + inline std::ostream& operator<<(std::ostream& os, const entry& e) + { + e.print(os, 0); + return os; + } + + inline entry::data_type entry::type() const { return m_type; } + + inline entry::entry(): m_type(undefined_t) {} + inline entry::entry(data_type t): m_type(t) { construct(t); } + inline entry::entry(const entry& e) { copy(e); } + inline entry::~entry() { destruct(); } + + inline void entry::operator=(const entry& e) + { + destruct(); + copy(e); + } + + inline entry::integer_type& entry::integer() + { + if (m_type == undefined_t) construct(int_t); + if (m_type != int_t) throw type_error("invalid type requested from entry"); + return *reinterpret_cast(data); + } + + inline entry::integer_type const& entry::integer() const + { + if (m_type != int_t) throw type_error("invalid type requested from entry"); + return *reinterpret_cast(data); + } + + inline entry::string_type& entry::string() + { + if (m_type == undefined_t) construct(string_t); + if (m_type != string_t) throw type_error("invalid type requested from entry"); + return *reinterpret_cast(data); + } + + inline entry::string_type const& entry::string() const + { + if (m_type != string_t) throw type_error("invalid type requested from entry"); + return *reinterpret_cast(data); + } + + inline entry::list_type& entry::list() + { + if (m_type == undefined_t) construct(list_t); + if (m_type != list_t) throw type_error("invalid type requested from entry"); + return *reinterpret_cast(data); + } + + inline entry::list_type const& entry::list() const + { + if (m_type != list_t) throw type_error("invalid type requested from entry"); + return *reinterpret_cast(data); + } + + inline entry::dictionary_type& entry::dict() + { + if (m_type == undefined_t) construct(dictionary_t); + if (m_type != dictionary_t) throw type_error("invalid type requested from entry"); + return *reinterpret_cast(data); + } + + inline entry::dictionary_type const& entry::dict() const + { + if (m_type != dictionary_t) throw type_error("invalid type requested from entry"); + return *reinterpret_cast(data); + } + +} + +#endif // TORRENT_ENTRY_HPP_INCLUDED diff --git a/encryption/libtorrent/include/libtorrent/escape_string.hpp b/encryption/libtorrent/include/libtorrent/escape_string.hpp new file mode 100755 index 000000000..e0e743e1e --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/escape_string.hpp @@ -0,0 +1,46 @@ +/* + +Copyright (c) 2003, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_ESCAPE_STRING_HPP_INCLUDED +#define TORRENT_ESCAPE_STRING_HPP_INCLUDED + +#include +#include "libtorrent/config.hpp" + +namespace libtorrent +{ + std::string TORRENT_EXPORT unescape_string(std::string const& s); + std::string TORRENT_EXPORT escape_string(const char* str, int len); + std::string TORRENT_EXPORT escape_path(const char* str, int len); +} + +#endif // TORRENT_ESCAPE_STRING_HPP_INCLUDED diff --git a/encryption/libtorrent/include/libtorrent/extensions.hpp b/encryption/libtorrent/include/libtorrent/extensions.hpp new file mode 100644 index 000000000..5f8172649 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/extensions.hpp @@ -0,0 +1,176 @@ +/* + +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_EXTENSIONS_HPP_INCLUDED +#define TORRENT_EXTENSIONS_HPP_INCLUDED + +#ifndef TORRENT_DISABLE_EXTENSIONS + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include +#include "libtorrent/config.hpp" +#include "libtorrent/buffer.hpp" + +namespace libtorrent +{ + struct peer_plugin; + class bt_peer_connection; + struct peer_request; + class peer_connection; + class entry; + + struct TORRENT_EXPORT torrent_plugin + { + virtual ~torrent_plugin() {} + // throwing an exception closes the connection + // returning a 0 pointer is valid and will not add + // the peer_plugin to the peer_connection + virtual boost::shared_ptr new_connection(peer_connection*) + { return boost::shared_ptr(); } + + virtual void on_piece_pass(int index) {} + virtual void on_piece_failed(int index) {} + + // called aproximately once every second + virtual void tick() {} + + // if true is returned, it means the handler handled the event, + // and no other plugins will have their handlers called, and the + // default behavior will be skipped + virtual bool on_pause() { return false; } + virtual bool on_resume() { return false;} + + // this is called when the initial checking of + // files is completed. + virtual void on_files_checked() {} + }; + + struct TORRENT_EXPORT peer_plugin + { + virtual ~peer_plugin() {} + + // can add entries to the extension handshake + // this is not called for web seeds + virtual void add_handshake(entry&) {} + + // throwing an exception from any of the handlers (except add_handshake) + // closes the connection + + // this is called when the initial BT 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() { 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(entry const& h) { return true; } + + // returning true from any of the message handlers + // indicates that the plugin has handeled 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_bitfield(std::vector const& bitfield) + { return false; } + + virtual bool on_request(peer_request const& req) + { return false; } + + virtual bool on_piece(peer_request const& piece, char const* data) + { return false; } + + virtual bool on_cancel(peer_request const& req) + { return false; } + + // 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 + 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 aproximately 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& r) { return false; } + }; + +} + +#endif + +#endif // TORRENT_EXTENSIONS_HPP_INCLUDED + diff --git a/encryption/libtorrent/include/libtorrent/extensions/logger.hpp b/encryption/libtorrent/include/libtorrent/extensions/logger.hpp new file mode 100644 index 000000000..42e08fcf6 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/extensions/logger.hpp @@ -0,0 +1,54 @@ +/* + +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_LOGGER_HPP_INCLUDED +#define TORRENT_LOGGER_HPP_INCLUDED + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +namespace libtorrent +{ + struct torrent_plugin; + class torrent; + boost::shared_ptr create_logger_plugin(torrent*); +} + +#endif // TORRENT_LOGGER_HPP_INCLUDED + diff --git a/encryption/libtorrent/include/libtorrent/extensions/metadata_transfer.hpp b/encryption/libtorrent/include/libtorrent/extensions/metadata_transfer.hpp new file mode 100644 index 000000000..210642161 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/extensions/metadata_transfer.hpp @@ -0,0 +1,55 @@ +/* + +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_METADATA_TRANSFER_HPP_INCLUDED +#define TORRENT_METADATA_TRANSFER_HPP_INCLUDED + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include +#include "libtorrent/config.hpp" + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +namespace libtorrent +{ + struct torrent_plugin; + class torrent; + TORRENT_EXPORT boost::shared_ptr create_metadata_plugin(torrent*); +} + +#endif // TORRENT_METADATA_TRANSFER_HPP_INCLUDED + diff --git a/encryption/libtorrent/include/libtorrent/extensions/ut_pex.hpp b/encryption/libtorrent/include/libtorrent/extensions/ut_pex.hpp new file mode 100644 index 000000000..efd9ab4f6 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/extensions/ut_pex.hpp @@ -0,0 +1,54 @@ +/* + +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 + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include +#include "libtorrent/config.hpp" + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +namespace libtorrent +{ + struct torrent_plugin; + class torrent; + TORRENT_EXPORT boost::shared_ptr create_ut_pex_plugin(torrent*); +} + +#endif // TORRENT_UT_PEX_EXTENSION_HPP_INCLUDED diff --git a/encryption/libtorrent/include/libtorrent/file.hpp b/encryption/libtorrent/include/libtorrent/file.hpp new file mode 100755 index 000000000..d11496b28 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/file.hpp @@ -0,0 +1,131 @@ +/* + +Copyright (c) 2003, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_FILE_HPP_INCLUDED +#define TORRENT_FILE_HPP_INCLUDED + +#include +#include + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/size_type.hpp" +#include "libtorrent/config.hpp" + +namespace libtorrent +{ + + struct TORRENT_EXPORT file_error: std::runtime_error + { + file_error(std::string const& msg): std::runtime_error(msg) {} + }; + + class TORRENT_EXPORT file: public boost::noncopyable + { + public: + + class seek_mode + { + friend class file; + private: + seek_mode(int v): m_val(v) {} + int m_val; + }; + + static const seek_mode begin; + static const seek_mode end; + + class open_mode + { + friend class file; + public: + + open_mode(): m_mask(0) {} + + open_mode operator|(open_mode m) const + { return open_mode(m.m_mask | m_mask); } + + open_mode operator&(open_mode m) const + { return open_mode(m.m_mask & m_mask); } + + open_mode operator|=(open_mode m) + { + m_mask |= m.m_mask; + return *this; + } + + bool operator==(open_mode m) const { return m_mask == m.m_mask; } + bool operator!=(open_mode m) const { return m_mask != m.m_mask; } + + private: + + open_mode(int val): m_mask(val) {} + int m_mask; + }; + + static const open_mode in; + static const open_mode out; + + file(); + file(boost::filesystem::path const& p, open_mode m); + ~file(); + + void open(boost::filesystem::path const& p, open_mode m); + void close(); + void set_size(size_type size); + + size_type write(const char*, size_type num_bytes); + size_type read(char*, size_type num_bytes); + + size_type seek(size_type pos, seek_mode m = begin); + size_type tell(); + + private: + + struct impl; + const std::auto_ptr m_impl; + + }; + +} + +#endif // TORRENT_FILE_HPP_INCLUDED + diff --git a/encryption/libtorrent/include/libtorrent/file_pool.hpp b/encryption/libtorrent/include/libtorrent/file_pool.hpp new file mode 100644 index 000000000..a22c26538 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/file_pool.hpp @@ -0,0 +1,103 @@ +/* + +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_FILE_POOL_HPP +#define TORRENT_FILE_POOL_HPP + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include +#include +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/file.hpp" +#include "libtorrent/time.hpp" + +namespace libtorrent +{ + + using boost::multi_index::multi_index_container; + using boost::multi_index::ordered_non_unique; + using boost::multi_index::ordered_unique; + using boost::multi_index::indexed_by; + using boost::multi_index::member; + namespace fs = boost::filesystem; + + struct TORRENT_EXPORT file_pool : boost::noncopyable + { + file_pool(int size = 40): m_size(size) {} + + boost::shared_ptr open_file(void* st, fs::path const& p, file::open_mode m); + void release(void* st); + void resize(int size); + + private: + int m_size; + + struct lru_file_entry + { + lru_file_entry(boost::shared_ptr const& f) + : file_ptr(f) + , last_use(time_now()) {} + mutable boost::shared_ptr file_ptr; + fs::path file_path; + void* key; + ptime last_use; + file::open_mode mode; + }; + + typedef multi_index_container< + lru_file_entry, indexed_by< + ordered_unique > + , ordered_non_unique > + , ordered_non_unique > + > + > file_set; + + file_set m_files; + boost::mutex m_mutex; + }; +} + +#endif diff --git a/encryption/libtorrent/include/libtorrent/fingerprint.hpp b/encryption/libtorrent/include/libtorrent/fingerprint.hpp new file mode 100755 index 000000000..d7e5a5fc6 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/fingerprint.hpp @@ -0,0 +1,93 @@ +/* + +Copyright (c) 2003, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_FINGERPRINT_HPP_INCLUDED +#define TORRENT_FINGERPRINT_HPP_INCLUDED + +#include +#include + +#include "libtorrent/peer_id.hpp" + +namespace libtorrent +{ + + struct fingerprint + { + fingerprint(const char* id_string, int major, int minor, int revision, int tag) + : major_version(major) + , minor_version(minor) + , revision_version(revision) + , tag_version(tag) + { + assert(id_string); + assert(major >= 0); + assert(minor >= 0); + assert(revision >= 0); + assert(tag >= 0); + assert(std::strlen(id_string) == 2); + name[0] = id_string[0]; + name[1] = id_string[1]; + } + + std::string to_string() const + { + std::stringstream s; + s << "-" << name[0] << name[1] + << version_to_char(major_version) + << version_to_char(minor_version) + << version_to_char(revision_version) + << version_to_char(tag_version) << "-"; + return s.str(); + } + + char name[2]; + int major_version; + int minor_version; + int revision_version; + int tag_version; + + private: + + char version_to_char(int v) const + { + if (v >= 0 && v < 10) return '0' + v; + else if (v >= 10) return 'A' + (v - 10); + assert(false); + return '0'; + } + + }; + +} + +#endif // TORRENT_FINGERPRINT_HPP_INCLUDED diff --git a/encryption/libtorrent/include/libtorrent/hasher.hpp b/encryption/libtorrent/include/libtorrent/hasher.hpp new file mode 100755 index 000000000..84b009844 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/hasher.hpp @@ -0,0 +1,106 @@ +/* + +Copyright (c) 2003, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_HASHER_HPP_INCLUDED +#define TORRENT_HASHER_HPP_INCLUDED + +#include +#include + +#include "libtorrent/peer_id.hpp" +#include "libtorrent/config.hpp" +#include "zlib.h" + +extern "C" +{ +#include "openssl/sha.h" +#include "openssl/bn.h" +} +namespace libtorrent +{ + + class adler32_crc + { + public: + adler32_crc(): m_adler(adler32(0, 0, 0)) {} + + void update(const char* data, int len) + { + assert(data != 0); + assert(len > 0); + m_adler = adler32(m_adler, (const Bytef*)data, len); + } + unsigned long final() const { return m_adler; } + void reset() { m_adler = adler32(0, 0, 0); } + + private: + + unsigned long m_adler; + + }; + + class hasher + { + public: + + hasher() { SHA1_Init(&m_context); } + hasher(const char* data, int len) + { + SHA1_Init(&m_context); + assert(data != 0); + assert(len > 0); + SHA1_Update(&m_context, reinterpret_cast(data), len); + } + void update(const char* data, int len) + { + assert(data != 0); + assert(len > 0); + SHA1_Update(&m_context, reinterpret_cast(data), len); + } + + sha1_hash final() + { + sha1_hash digest; + SHA1_Final(digest.begin(), &m_context); + return digest; + } + + void reset() { SHA1_Init(&m_context); } + + private: + + SHA_CTX m_context; + + }; +} + +#endif // TORRENT_HASHER_HPP_INCLUDED diff --git a/encryption/libtorrent/include/libtorrent/http_connection.hpp b/encryption/libtorrent/include/libtorrent/http_connection.hpp new file mode 100644 index 000000000..409213857 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/http_connection.hpp @@ -0,0 +1,154 @@ +/* + +Copyright (c) 2007, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_HTTP_CONNECTION +#define TORRENT_HTTP_CONNECTION + +#include +#include +#include +#include +#include +#include +#include + +#include "libtorrent/socket.hpp" +#include "libtorrent/http_tracker_connection.hpp" +#include "libtorrent/time.hpp" + +namespace libtorrent +{ + +typedef boost::function http_handler; + +// TODO: add bind interface + +// when bottled, the last two arguments to the handler +// will always be 0 +struct http_connection : boost::enable_shared_from_this, boost::noncopyable +{ + http_connection(asio::io_service& ios, connection_queue& cc + , http_handler handler, bool bottled = true) + : m_sock(ios) + , m_read_pos(0) + , m_resolver(ios) + , m_handler(handler) + , m_timer(ios) + , m_last_receive(time_now()) + , m_bottled(bottled) + , m_called(false) + , m_rate_limit(0) + , m_download_quota(0) + , m_limiter_timer_active(false) + , m_limiter_timer(ios) + , m_redirect(true) + , m_connection_ticket(-1) + , m_cc(cc) + { + assert(!m_handler.empty()); + } + + void rate_limit(int limit); + + int rate_limit() const + { return m_rate_limit; } + + std::string sendbuffer; + + void get(std::string const& url, time_duration timeout = seconds(30) + , bool handle_redirect = true); + + void start(std::string const& hostname, std::string const& port + , time_duration timeout, bool handle_redirect = true); + void close(); + +private: + + void on_resolve(asio::error_code const& e + , tcp::resolver::iterator i); + void connect(int ticket, tcp::endpoint target_address); + void on_connect_timeout(); + void on_connect(asio::error_code const& e +/* , tcp::resolver::iterator i*/); + void on_write(asio::error_code const& e); + void on_read(asio::error_code const& e, std::size_t bytes_transferred); + static void on_timeout(boost::weak_ptr p + , asio::error_code const& e); + void on_assign_bandwidth(asio::error_code const& e); + + std::vector m_recvbuffer; + tcp::socket m_sock; + int m_read_pos; + tcp::resolver m_resolver; + http_parser m_parser; + http_handler m_handler; + deadline_timer m_timer; + time_duration m_timeout; + ptime m_last_receive; + // 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; + std::string m_hostname; + std::string m_port; + + // 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; + + // 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; + + // the timer fires every 250 millisecond as long + // as all the quota was used. + deadline_timer m_limiter_timer; + + // if set to true, the connection should handle + // HTTP redirects. + bool m_redirect; + + int m_connection_ticket; + connection_queue& m_cc; +}; + +} + +#endif diff --git a/encryption/libtorrent/include/libtorrent/http_stream.hpp b/encryption/libtorrent/include/libtorrent/http_stream.hpp new file mode 100644 index 000000000..2bd124b43 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/http_stream.hpp @@ -0,0 +1,193 @@ +#include "libtorrent/io.hpp" +#include "libtorrent/socket.hpp" +#include +#include +#include +#include +#include + + +namespace libtorrent { + +class http_stream : boost::noncopyable +{ +public: + + typedef stream_socket::lowest_layer_type lowest_layer_type; + typedef stream_socket::endpoint_type endpoint_type; + typedef stream_socket::protocol_type protocol_type; + + explicit http_stream(asio::io_service& io_service) + : m_sock(io_service) + , m_resolver(io_service) + , m_no_connect(false) + {} + + void set_no_connect(bool c) { m_no_connect = c; } + + void set_proxy(std::string hostname, int port) + { + m_hostname = hostname; + m_port = port; + } + + void set_username(std::string const& user + , std::string const& password) + { + m_user = user; + m_password = password; + } + + 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, asio::error_code& ec) + { + return m_sock.read_some(buffers, ec); + } + + 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.io_control(ioc); + } + + template + void io_control(IO_Control_Command& ioc, asio::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); + } + + void bind(endpoint_type const& endpoint) + { + m_sock.bind(endpoint); + } + + template + void bind(endpoint_type const& endpoint, Error_Handler const& error_handler) + { + m_sock.bind(endpoint, error_handler); + } + + void open(protocol_type const& p) + { + m_sock.open(p); + } + + template + void open(protocol_type const& p, Error_Handler const& error_handler) + { + m_sock.open(p, error_handler); + } + + void close() + { + m_remote_endpoint = endpoint_type(); + m_sock.close(); + } + + template + void close(Error_Handler const& error_handler) + { + m_sock.close(error_handler); + } + + endpoint_type remote_endpoint() + { + return m_remote_endpoint; + } + + template + endpoint_type remote_endpoint(Error_Handler const& error_handler) + { + return m_remote_endpoint; + } + + endpoint_type local_endpoint() + { + return m_sock.local_endpoint(); + } + + template + endpoint_type local_endpoint(Error_Handler const& error_handler) + { + return m_sock.local_endpoint(error_handler); + } + + asio::io_service& io_service() + { + return m_sock.io_service(); + } + + lowest_layer_type& lowest_layer() + { + return m_sock.lowest_layer(); + } + + typedef boost::function handler_type; + + 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 shaed_ptr + boost::shared_ptr h(new handler_type(handler)); + + tcp::resolver::query q(m_hostname + , boost::lexical_cast(m_port)); + m_resolver.async_resolve(q, boost::bind( + &http_stream::name_lookup, this, _1, _2, h)); + } + +private: + + void name_lookup(asio::error_code const& e, tcp::resolver::iterator i + , boost::shared_ptr h); + void connected(asio::error_code const& e, boost::shared_ptr h); + void handshake1(asio::error_code const& e, boost::shared_ptr h); + void handshake2(asio::error_code const& e, boost::shared_ptr h); + + stream_socket m_sock; + // the http proxy + std::string m_hostname; + int m_port; + // send and receive buffer + std::vector m_buffer; + // proxy authentication + std::string m_user; + std::string m_password; + + endpoint_type m_remote_endpoint; + + tcp::resolver m_resolver; + // this is true if the connection is HTTP based and + // want to talk directly to the proxy + bool m_no_connect; +}; + +} + diff --git a/encryption/libtorrent/include/libtorrent/http_tracker_connection.hpp b/encryption/libtorrent/include/libtorrent/http_tracker_connection.hpp new file mode 100755 index 000000000..35d529504 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/http_tracker_connection.hpp @@ -0,0 +1,178 @@ +/* + +Copyright (c) 2003, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_HTTP_TRACKER_CONNECTION_HPP_INCLUDED +#define TORRENT_HTTP_TRACKER_CONNECTION_HPP_INCLUDED + +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/socket.hpp" +#include "libtorrent/entry.hpp" +#include "libtorrent/session_settings.hpp" +#include "libtorrent/peer_id.hpp" +#include "libtorrent/peer.hpp" +#include "libtorrent/tracker_manager.hpp" +#include "libtorrent/config.hpp" +#include "libtorrent/buffer.hpp" +#include "libtorrent/socket_type.hpp" +#include "libtorrent/connection_queue.hpp" + +namespace libtorrent +{ + + class http_parser + { + public: + http_parser(); + template + T header(char const* key) const; + std::string const& protocol() const { return m_protocol; } + int status_code() const { return m_status_code; } + std::string message() const { return m_server_message; } + buffer::const_interval get_body() const; + bool header_finished() const { return m_state == read_body; } + bool finished() const { return m_finished; } + boost::tuple incoming(buffer::const_interval recv_buffer); + int body_start() const { return m_body_start_pos; } + int content_length() const { return m_content_length; } + + void reset(); + private: + int m_recv_pos; + int m_status_code; + std::string m_protocol; + std::string m_server_message; + + int m_content_length; + + enum { read_status, read_header, read_body } m_state; + + std::map m_header; + buffer::const_interval m_recv_buffer; + int m_body_start_pos; + + bool m_finished; + }; + + template + T http_parser::header(char const* key) const + { + std::map::const_iterator i + = m_header.find(key); + if (i == m_header.end()) return T(); + return boost::lexical_cast(i->second); + } + + class TORRENT_EXPORT http_tracker_connection + : public tracker_connection + { + friend class tracker_manager; + public: + + http_tracker_connection( + asio::strand& str + , connection_queue& cc + , tracker_manager& man + , tracker_request const& req + , std::string const& hostname + , unsigned short port + , std::string request + , address bind_infc + , boost::weak_ptr c + , session_settings const& stn + , proxy_settings const& ps + , std::string const& password = ""); + + private: + + boost::intrusive_ptr self() + { return boost::intrusive_ptr(this); } + + void on_response(); + + void init_send_buffer( + std::string const& hostname + , std::string const& request); + + void name_lookup(asio::error_code const& error, tcp::resolver::iterator i); + void connect(int ticket, tcp::endpoint target_address); + void connected(asio::error_code const& error); + void sent(asio::error_code const& error); + void receive(asio::error_code const& error + , std::size_t bytes_transferred); + + virtual void on_timeout(); + + void parse(const entry& e); + peer_entry extract_peer_info(const entry& e); + + tracker_manager& m_man; + http_parser m_parser; + + asio::strand& m_strand; + tcp::resolver m_name_lookup; + int m_port; + boost::shared_ptr m_socket; + int m_recv_pos; + std::vector m_buffer; + std::string m_send_buffer; + + session_settings const& m_settings; + proxy_settings const& m_proxy; + std::string m_password; + + bool m_timed_out; + + int m_connection_ticket; + connection_queue& m_cc; + }; + +} + +#endif // TORRENT_HTTP_TRACKER_CONNECTION_HPP_INCLUDED + diff --git a/encryption/libtorrent/include/libtorrent/identify_client.hpp b/encryption/libtorrent/include/libtorrent/identify_client.hpp new file mode 100755 index 000000000..e8cb3b930 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/identify_client.hpp @@ -0,0 +1,58 @@ +/* + +Copyright (c) 2003, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_IDENTIFY_CLIENT_HPP_INCLUDED +#define TORRENT_IDENTIFY_CLIENT_HPP_INCLUDED + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/peer_id.hpp" +#include "libtorrent/fingerprint.hpp" +#include "libtorrent/config.hpp" + +namespace libtorrent +{ + + TORRENT_EXPORT std::string identify_client(const peer_id& p); + TORRENT_EXPORT boost::optional client_fingerprint(peer_id const& p); + +} + +#endif // TORRENT_IDENTIFY_CLIENT_HPP_INCLUDED diff --git a/encryption/libtorrent/include/libtorrent/instantiate_connection.hpp b/encryption/libtorrent/include/libtorrent/instantiate_connection.hpp new file mode 100644 index 000000000..49cb1fe18 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/instantiate_connection.hpp @@ -0,0 +1,49 @@ +/* + +Copyright (c) 2007, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_INSTANTIATE_CONNECTION +#define TORRENT_INSTANTIATE_CONNECTION + +#include "libtorrent/socket_type.hpp" +#include +#include + +namespace libtorrent +{ + struct proxy_settings; + + boost::shared_ptr instantiate_connection( + asio::io_service& ios, proxy_settings const& ps); +} + +#endif + diff --git a/encryption/libtorrent/include/libtorrent/invariant_check.hpp b/encryption/libtorrent/include/libtorrent/invariant_check.hpp new file mode 100755 index 000000000..c6eacf338 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/invariant_check.hpp @@ -0,0 +1,78 @@ +// Copyright Daniel Wallin 2004. Use, modification and distribution is +// subject to the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#ifndef TORRENT_INVARIANT_ACCESS_HPP_INCLUDED +#define TORRENT_INVARIANT_ACCESS_HPP_INCLUDED + +#include + +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_) + { + try + { + check_invariant(self); + } + catch (...) + { + assert(false); + } + } + + ~invariant_checker_impl() + { + try + { + check_invariant(self); + } + catch (...) + { + assert(false); + } + } + + T const& self; + }; + + template + invariant_checker_impl make_invariant_checker(T const& x) + { + return invariant_checker_impl(x); + } +} + +#ifndef NDEBUG +#define INVARIANT_CHECK \ + invariant_checker const& _invariant_check = make_invariant_checker(*this); \ + (void)_invariant_check; \ + do {} while (false) +#else +#define INVARIANT_CHECK do {} while (false) +#endif + +#endif // TORRENT_INVARIANT_ACCESS_HPP_INCLUDED diff --git a/encryption/libtorrent/include/libtorrent/io.hpp b/encryption/libtorrent/include/libtorrent/io.hpp new file mode 100755 index 000000000..f73c3e290 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/io.hpp @@ -0,0 +1,153 @@ +/* + +Copyright (c) 2003, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_IO_HPP_INCLUDED +#define TORRENT_IO_HPP_INCLUDED + +#include +#include + +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 + 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 void write_string(std::string const& str, char*& start) + { + std::copy(str.begin(), str.end(), start); + start += str.size(); + } + + template + void write_string(std::string const& str, OutIt& start) + { + std::copy(str.begin(), str.end(), start); + } + + } +} + +#endif // TORRENT_IO_HPP_INCLUDED diff --git a/encryption/libtorrent/include/libtorrent/ip_filter.hpp b/encryption/libtorrent/include/libtorrent/ip_filter.hpp new file mode 100644 index 000000000..1c62c553b --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/ip_filter.hpp @@ -0,0 +1,276 @@ +/* + +Copyright (c) 2005, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_IP_FILTER_HPP +#define TORRENT_IP_FILTER_HPP + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + + +#include "libtorrent/config.hpp" +#include "libtorrent/socket.hpp" +#include +#include + +namespace libtorrent +{ + +inline bool operator<=(address const& lhs + , address const& rhs) +{ + return lhs < rhs || lhs == rhs; +} + +template +struct ip_range +{ + Addr first; + Addr last; + int flags; +}; + +namespace detail +{ + + // this is the generic implementation of + // a filter for a specific address type. + // it works with IPv4 and IPv6 + template + class TORRENT_EXPORT filter_impl + { + public: + + filter_impl() + { + typename Addr::bytes_type zero; + std::fill(zero.begin(), zero.end(), 0); + // make the entire ip-range non-blocked + m_access_list.insert(range(Addr(zero), 0)); + } + + void add_rule(Addr first, Addr last, int flags) + { + using boost::next; + using boost::prior; + + assert(!m_access_list.empty()); + assert(first < last || first == last); + + typename range_t::iterator i = m_access_list.upper_bound(first); + typename range_t::iterator j = m_access_list.upper_bound(last); + + if (i != m_access_list.begin()) --i; + + assert(j != m_access_list.begin()); + assert(j != i); + + int first_access = i->access; + int last_access = prior(j)->access; + + if (i->start != first && first_access != flags) + { + i = m_access_list.insert(i, range(first, flags)); + } + else if (i != m_access_list.begin() && prior(i)->access == flags) + { + --i; + first_access = i->access; + } + assert(!m_access_list.empty()); + assert(i != m_access_list.end()); + + if (i != j) m_access_list.erase(next(i), j); + if (i->start == first) + { + // we can do this const-cast because we know that the new + // start address will keep the set correctly ordered + const_cast(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())) + { + assert(j == m_access_list.end() || last < minus_one(j->start)); + if (last_access != flags) + j = m_access_list.insert(j, range(plus_one(last), last_access)); + } + + if (j != m_access_list.end() && j->access == flags) m_access_list.erase(j); + assert(!m_access_list.empty()); + } + + int access(Addr const& addr) const + { + assert(!m_access_list.empty()); + typename range_t::const_iterator i = m_access_list.upper_bound(addr); + if (i != m_access_list.begin()) --i; + assert(i != m_access_list.end()); + assert(i->start <= addr && (boost::next(i) == m_access_list.end() + || addr < boost::next(i)->start)); + return i->access; + } + + std::vector > 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 = i->start; + r.flags = i->access; + + ++i; + if (i == end) + r.last = max_addr(); + else + r.last = minus_one(i->start); + + ret.push_back(r); + } + return ret; + } + + private: + + Addr plus_one(Addr const& a) const + { + typename Addr::bytes_type tmp(a.to_bytes()); + typedef typename Addr::bytes_type::reverse_iterator iter; + for (iter i = tmp.rbegin() + , end(tmp.rend()); i != end; ++i) + { + if (*i < (std::numeric_limits::max)()) + { + *i += 1; + break; + } + *i = 0; + } + return Addr(tmp); + } + + Addr minus_one(Addr const& a) const + { + typename Addr::bytes_type tmp(a.to_bytes()); + typedef typename Addr::bytes_type::reverse_iterator iter; + for (iter i = tmp.rbegin() + , end(tmp.rend()); i != end; ++i) + { + if (*i > 0) + { + *i -= 1; + break; + } + *i = (std::numeric_limits::max)(); + } + return Addr(tmp); + } + + Addr max_addr() const + { + typename Addr::bytes_type tmp; + std::fill(tmp.begin(), tmp.end() + , (std::numeric_limits::max)()); + return Addr(tmp); + } + + struct range + { + range(Addr addr, int access = 0): start(addr), access(access) {} + bool operator<(range const& r) const + { return start < r.start; } + bool operator<(Addr const& a) const + { return start < a; } + Addr start; + // the end of the range is implicit + // and given by the next entry in the set + int access; + }; + + typedef std::set range_t; + range_t m_access_list; + + }; + +} + +class TORRENT_EXPORT ip_filter +{ +public: + + enum access_flags + { + blocked = 1 + }; + + // both addresses MUST be of the same type (i.e. both must + // be either IPv4 or both must be IPv6) + void add_rule(address first, address last, int flags); + int access(address const& addr) const; + + typedef boost::tuple > + , std::vector > > filter_tuple_t; + + filter_tuple_t export_filter() const; + +// void print() const; + +private: + + detail::filter_impl m_filter4; + detail::filter_impl m_filter6; +}; + +} + +#endif + diff --git a/encryption/libtorrent/include/libtorrent/kademlia/closest_nodes.hpp b/encryption/libtorrent/include/libtorrent/kademlia/closest_nodes.hpp new file mode 100644 index 000000000..244e4bb38 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/kademlia/closest_nodes.hpp @@ -0,0 +1,117 @@ +/* + +Copyright (c) 2006, Arvid Norberg & Daniel Wallin +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef CLOSEST_NODES_050323_HPP +#define CLOSEST_NODES_050323_HPP + +#include + +#include +#include +#include +#include +#include + +#include + +namespace libtorrent { namespace dht +{ + +class rpc_manager; + +// -------- closest nodes ----------- + +class closest_nodes : public traversal_algorithm +{ +public: + typedef boost::function< + void(std::vector const&) + > done_callback; + + static void initiate( + node_id target + , int branch_factor + , int max_results + , routing_table& table + , rpc_manager& rpc + , done_callback const& callback + ); + +private: + void done(); + void invoke(node_id const& id, asio::ip::udp::endpoint addr); + + closest_nodes( + node_id target + , int branch_factor + , int max_results + , routing_table& table + , rpc_manager& rpc + , done_callback const& callback + ); + + done_callback m_done_callback; +}; + +class closest_nodes_observer : public observer +{ +public: + closest_nodes_observer( + boost::intrusive_ptr const& algorithm + , node_id self + , node_id target) + : observer(algorithm->allocator()) + , m_algorithm(algorithm) + , m_target(target) + , m_self(self) + {} + ~closest_nodes_observer(); + + void send(msg& p) + { + p.info_hash = m_target; + } + + void timeout(); + void reply(msg const&); + void abort() { m_algorithm = 0; } + +private: + boost::intrusive_ptr m_algorithm; + node_id const m_target; + node_id const m_self; +}; + +} } // namespace libtorrent::dht + +#endif // CLOSEST_NODES_050323_HPP + diff --git a/encryption/libtorrent/include/libtorrent/kademlia/dht_tracker.hpp b/encryption/libtorrent/include/libtorrent/kademlia/dht_tracker.hpp new file mode 100644 index 000000000..f61364707 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/kademlia/dht_tracker.hpp @@ -0,0 +1,159 @@ +/* + +Copyright (c) 2006, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_DISABLE_DHT + +#ifndef TORRENT_DHT_TRACKER +#define TORRENT_DHT_TRACKER + +#include +#include +#include +#include +#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/packet_iterator.hpp" +#include "libtorrent/session_settings.hpp" +#include "libtorrent/session_status.hpp" + +namespace libtorrent { namespace dht +{ + +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_DECLARE_LOG(dht_tracker); +#endif + + struct dht_tracker; + + TORRENT_EXPORT void intrusive_ptr_add_ref(dht_tracker const*); + TORRENT_EXPORT void intrusive_ptr_release(dht_tracker const*); + + struct dht_tracker + { + friend void intrusive_ptr_add_ref(dht_tracker const*); + friend void intrusive_ptr_release(dht_tracker const*); + dht_tracker(asio::io_service& ios, dht_settings const& settings + , asio::ip::address listen_interface, entry const& bootstrap); + void stop(); + + void add_node(udp::endpoint node); + void add_node(std::pair const& node); + void add_router_node(std::pair const& node); + + void rebind(asio::ip::address listen_interface, int listen_port); + + entry state() const; + + void announce(sha1_hash const& ih, int listen_port + , boost::function const& + , sha1_hash const&)> f); + + void dht_status(session_status& s); + + private: + + boost::intrusive_ptr self() + { return boost::intrusive_ptr(this); } + + void on_name_lookup(asio::error_code const& e + , udp::resolver::iterator host); + void on_router_name_lookup(asio::error_code const& e + , udp::resolver::iterator host); + void connection_timeout(asio::error_code const& e); + void refresh_timeout(asio::error_code const& e); + void tick(asio::error_code const& e); + + // translate bittorrent kademlia message into the generic kademlia message + // used by the library + void on_receive(asio::error_code const& error, size_t bytes_transferred); + void on_bootstrap(); + void send_packet(msg const& m); + + asio::strand m_strand; + asio::ip::udp::socket m_socket; + + node_impl m_dht; + + // this is the index of the receive buffer we are currently receiving to + // the other buffer is the one containing the last message + int m_buffer; + std::vector m_in_buf[2]; + udp::endpoint m_remote_endpoint[2]; + std::vector m_send_buf; + + ptime m_last_new_key; + deadline_timer m_timer; + deadline_timer m_connection_timer; + deadline_timer m_refresh_timer; + dht_settings const& m_settings; + int m_refresh_bucket; + + // used to resolve hostnames for nodes + udp::resolver m_host_resolver; + + // reference counter for intrusive_ptr + mutable boost::detail::atomic_count m_refs; + +#ifdef TORRENT_DHT_VERBOSE_LOGGING + int m_replies_sent[5]; + int m_queries_received[5]; + int m_replies_bytes_sent[5]; + int m_queries_bytes_received[5]; + int m_counter; + int m_announces; + int m_failed_announces; + + int m_total_message_input; + int m_ut_message_input; + int m_lt_message_input; + int m_mp_message_input; + int m_gr_message_input; + int m_mo_message_input; + + int m_total_in_bytes; + int m_total_out_bytes; + + int m_queries_out_bytes; +#endif + }; +}} + +#endif +#endif diff --git a/encryption/libtorrent/include/libtorrent/kademlia/find_data.hpp b/encryption/libtorrent/include/libtorrent/kademlia/find_data.hpp new file mode 100644 index 000000000..17d77c9d8 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/kademlia/find_data.hpp @@ -0,0 +1,128 @@ +/* + +Copyright (c) 2006, Arvid Norberg & Daniel Wallin +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef FIND_DATA_050323_HPP +#define FIND_DATA_050323_HPP + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace libtorrent { namespace dht +{ + +using asio::ip::udp; + +typedef std::vector packet_t; + +class rpc_manager; + +// -------- find data ----------- + +class find_data : public traversal_algorithm +{ +public: + typedef boost::function done_callback; + + static void initiate( + node_id target + , int branch_factor + , int max_results + , routing_table& table + , rpc_manager& rpc + , done_callback const& callback + ); + + void got_data(msg const* m); + +private: + void done(); + void invoke(node_id const& id, udp::endpoint addr); + + find_data( + node_id target + , int branch_factor + , int max_results + , routing_table& table + , rpc_manager& rpc + , done_callback const& callback + ); + + done_callback m_done_callback; + boost::shared_ptr m_packet; + bool m_done; +}; + +class find_data_observer : public observer +{ +public: + find_data_observer( + boost::intrusive_ptr const& algorithm + , node_id self + , node_id target) + : observer(algorithm->allocator()) + , m_algorithm(algorithm) + , m_target(target) + , m_self(self) + {} + ~find_data_observer(); + + void send(msg& m) + { + m.reply = false; + m.message_id = messages::get_peers; + m.info_hash = m_target; + } + + void timeout(); + void reply(msg const&); + void abort() { m_algorithm = 0; } + +private: + boost::intrusive_ptr m_algorithm; + node_id const m_target; + node_id const m_self; +}; + +} } // namespace libtorrent::dht + +#endif // FIND_DATA_050323_HPP + diff --git a/encryption/libtorrent/include/libtorrent/kademlia/logging.hpp b/encryption/libtorrent/include/libtorrent/kademlia/logging.hpp new file mode 100644 index 000000000..c0cbb31a4 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/kademlia/logging.hpp @@ -0,0 +1,146 @@ +/* + +Copyright (c) 2006, Arvid Norberg & Daniel Wallin +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_LOGGING_HPP +#define TORRENT_LOGGING_HPP + +#include +#include + +namespace libtorrent { namespace dht +{ + +class log +{ +public: + log(char const* id, std::ostream& stream) + : m_id(id) + , m_enabled(true) + , m_stream(stream) + { + } + + char const* id() const + { + return m_id; + } + + bool enabled() const + { + return m_enabled; + } + + void enable(bool e) + { + m_enabled = e; + } + + void flush() { m_stream.flush(); } + + template + log& operator<<(T const& x) + { + m_stream << x; + return *this; + } + +private: + char const* m_id; + bool m_enabled; + std::ostream& m_stream; +}; + +class log_event +{ +public: + log_event(log& log) + : log_(log) + { + if (log_.enabled()) + log_ << '[' << log.id() << "] "; + } + + ~log_event() + { + if (log_.enabled()) + { + log_ << "\n"; + log_.flush(); + } + } + + template + log_event& operator<<(T const& x) + { + log_ << x; + return *this; + } + + operator bool() const + { + return log_.enabled(); + } + +private: + log& log_; +}; + +class inverted_log_event : public log_event +{ +public: + inverted_log_event(log& log) : log_event(log) {} + + operator bool() const + { + return !log_event::operator bool(); + } +}; + +} } // namespace libtorrent::dht + +#define TORRENT_DECLARE_LOG(name) \ + libtorrent::dht::log& name ## _log() + +#define TORRENT_DEFINE_LOG(name) \ + libtorrent::dht::log& name ## _log() \ + { \ + static std::ofstream log_file("dht.log", std::ios::app); \ + static libtorrent::dht::log instance(#name, log_file); \ + return instance; \ + } + +#define TORRENT_LOG(name) \ + if (libtorrent::dht::inverted_log_event event_object__ = name ## _log()); \ + else static_cast(event_object__) + +#endif + diff --git a/encryption/libtorrent/include/libtorrent/kademlia/msg.hpp b/encryption/libtorrent/include/libtorrent/kademlia/msg.hpp new file mode 100644 index 000000000..a205ce463 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/kademlia/msg.hpp @@ -0,0 +1,102 @@ +/* + +Copyright (c) 2007, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef MSG_HPP +#define MSG_HPP + +#include +#include +#include "libtorrent/entry.hpp" +#include + +namespace libtorrent { +namespace dht { + +typedef std::vector packet_t; + +using asio::ip::udp; + +namespace messages +{ + enum { ping = 0, find_node = 1, get_peers = 2, announce_peer = 3, error = 4 }; + char const* const ids[] = { "ping", "find_node", "get_peers", "announce_peer", "error" }; +} // namespace messages + +struct msg +{ + msg() : reply(false), piggy_backed_ping(false) + , message_id(-1), port(0) {} + + // true if this message is a reply + bool reply; + // true if this is a reply with a piggy backed ping + bool piggy_backed_ping; + // the kind if message + int message_id; + // if this is a reply, a copy of the transaction id + // from the request. If it's a request, a transaction + // id that should be sent back in the reply + std::string transaction_id; + // if this packet has a piggy backed ping, this + // is the transaction id of that ping + std::string ping_transaction_id; + // the node id of the process sending the message + node_id id; + // the address of the process sending or receiving + // the message. + udp::endpoint addr; + // if this is a nodes response, these are the nodes + typedef std::vector nodes_t; + nodes_t nodes; + + typedef std::vector peers_t; + peers_t peers; + + // similar to transaction_id but for write operations. + entry write_token; + + // the info has for peer_requests, announce_peer + // and responses + node_id info_hash; + + // port for announce_peer messages + int port; + + // ERROR MESSAGES + int error_code; + std::string error_msg; +}; + + +} } + +#endif diff --git a/encryption/libtorrent/include/libtorrent/kademlia/node.hpp b/encryption/libtorrent/include/libtorrent/kademlia/node.hpp new file mode 100644 index 000000000..850333043 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/kademlia/node.hpp @@ -0,0 +1,259 @@ +/* + +Copyright (c) 2006, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef NODE_HPP +#define NODE_HPP + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include "libtorrent/socket.hpp" + +namespace libtorrent { namespace dht +{ + +using asio::ip::udp; + +#ifdef TORRENT_DHT_VERBOSE_LOGGING +TORRENT_DECLARE_LOG(node); +#endif + +// this is the entry for every peer +// the timestamp is there to make it possible +// to remove stale peers +struct peer_entry +{ + tcp::endpoint addr; + ptime added; +}; + +// this is a group. It contains a set of group members +struct torrent_entry +{ + std::set peers; +}; + +inline bool operator<(peer_entry const& lhs, peer_entry const& rhs) +{ + return lhs.addr.address() == rhs.addr.address() + ? lhs.addr.port() < rhs.addr.port() + : lhs.addr.address() < rhs.addr.address(); +} + +struct null_type {}; + +class announce_observer : public observer +{ +public: + announce_observer(boost::pool<>& allocator + , sha1_hash const& info_hash + , int listen_port + , entry const& write_token) + : observer(allocator) + , m_info_hash(info_hash) + , m_listen_port(listen_port) + , m_token(write_token) + {} + + void send(msg& m) + { + m.port = m_listen_port; + m.info_hash = m_info_hash; + m.write_token = m_token; + } + + void timeout() {} + void reply(msg const&) {} + void abort() {} + +private: + sha1_hash m_info_hash; + int m_listen_port; + entry m_token; +}; + +class get_peers_observer : public observer +{ +public: + get_peers_observer(sha1_hash const& info_hash + , int listen_port + , rpc_manager& rpc + , boost::function const&, sha1_hash const&)> f) + : observer(rpc.allocator()) + , m_info_hash(info_hash) + , m_listen_port(listen_port) + , m_rpc(rpc) + , m_fun(f) + {} + + void send(msg& m) + { + m.port = m_listen_port; + m.info_hash = m_info_hash; + } + + void timeout() {} + void reply(msg const& r) + { + m_rpc.invoke(messages::announce_peer, r.addr + , observer_ptr(new (m_rpc.allocator().malloc()) announce_observer( + m_rpc.allocator(), m_info_hash, m_listen_port, r.write_token))); + m_fun(r.peers, m_info_hash); + } + void abort() {} + +private: + sha1_hash m_info_hash; + int m_listen_port; + rpc_manager& m_rpc; + boost::function const&, sha1_hash const&)> m_fun; +}; + + + +class node_impl : boost::noncopyable +{ +typedef std::map table_t; +public: + node_impl(boost::function const& f + , dht_settings const& settings, boost::optional node_id); + + virtual ~node_impl() {} + + void refresh(node_id const& id, boost::function0 f); + void bootstrap(std::vector const& nodes + , boost::function0 f); + void find_node(node_id const& id, boost::function< + void(std::vector const&)> f); + void add_router_node(udp::endpoint router); + + void incoming(msg const& m); + + void refresh(); + void refresh_bucket(int bucket); + int bucket_size(int bucket); + + typedef routing_table::iterator iterator; + + iterator begin() const { return m_table.begin(); } + iterator end() const { return m_table.end(); } + + typedef table_t::iterator data_iterator; + + node_id const& nid() const { return m_id; } + boost::tuple size() const{ return m_table.size(); } + size_type num_global_nodes() const + { return m_table.num_global_nodes(); } + + data_iterator begin_data() { return m_map.begin(); } + data_iterator end_data() { return m_map.end(); } + int data_size() const { return int(m_map.size()); } + +#ifdef TORRENT_DHT_VERBOSE_LOGGING + void print_state(std::ostream& os) const + { m_table.print_state(os); } +#endif + + void announce(sha1_hash const& info_hash, int listen_port + , boost::function const& + , sha1_hash const&)> f); + + bool verify_token(msg const& m); + entry generate_token(msg const& m); + + // the returned time is the delay until connection_timeout() + // should be called again the next time + time_duration connection_timeout(); + time_duration refresh_timeout(); + + // generates a new secret number used to generate write tokens + void new_write_key(); + + // pings the given node, and adds it to + // the routing table if it respons and if the + // bucket is not full. + void add_node(udp::endpoint node); + + void replacement_cache(bucket_t& nodes) const + { m_table.replacement_cache(nodes); } + +protected: + // is called when a find data request is received. Should + // return false if the data is not stored on this node. If + // the data is stored, it should be serialized into 'data'. + bool on_find(msg const& m, std::vector& peers) const; + + // this is called when a store request is received. The data + // is store-parameters and the data to be stored. + void on_announce(msg const& m, msg& reply); + + dht_settings const& m_settings; + + // the maximum number of peers to send in a get_peers + // reply. Ordinary trackers usually limit this to 50. + // 50 => 6 * 50 = 250 bytes + packet overhead + int m_max_peers_reply; + +private: + void incoming_request(msg const& h); + + node_id m_id; + routing_table m_table; + rpc_manager m_rpc; + table_t m_map; + + ptime m_last_tracker_tick; + + // secret random numbers used to create write tokens + int m_secret[2]; +}; + + +} } // namespace libtorrent::dht + +#endif // NODE_HPP + diff --git a/encryption/libtorrent/include/libtorrent/kademlia/node_entry.hpp b/encryption/libtorrent/include/libtorrent/kademlia/node_entry.hpp new file mode 100644 index 000000000..edc5dff80 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/kademlia/node_entry.hpp @@ -0,0 +1,63 @@ +/* + +Copyright (c) 2006, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef KADEMLIA_NODE_ENTRY_HPP +#define KADEMLIA_NODE_ENTRY_HPP + +#include "libtorrent/kademlia/node_id.hpp" +#include "libtorrent/socket.hpp" + +namespace libtorrent { namespace dht +{ + +struct node_entry +{ + node_entry(node_id const& id_, asio::ip::udp::endpoint addr_) + : id(id_) + , addr(addr_) + , fail_count(0) {} + node_entry(asio::ip::udp::endpoint addr_) + : id(0) + , addr(addr_) + , fail_count(0) {} + + node_id id; + udp::endpoint addr; + // the number of times this node has failed to + // respond in a row + int fail_count; +}; + +} } // namespace libtorrent::dht + +#endif + diff --git a/encryption/libtorrent/include/libtorrent/kademlia/node_id.hpp b/encryption/libtorrent/include/libtorrent/kademlia/node_id.hpp new file mode 100644 index 000000000..eb4d6c539 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/kademlia/node_id.hpp @@ -0,0 +1,60 @@ +/* + +Copyright (c) 2006, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ +#ifndef NODE_ID_HPP +#define NODE_ID_HPP + +#include +#include + +#include +#include "libtorrent/peer_id.hpp" + +namespace libtorrent { namespace dht +{ + +typedef libtorrent::big_number node_id; + +// returns the distance between the two nodes +// using the kademlia XOR-metric +node_id distance(node_id const& n1, node_id const& n2); + +// returns true if: distance(n1, ref) < distance(n2, ref) +bool compare_ref(node_id const& n1, node_id const& n2, node_id const& ref); + +// returns n in: 2^n <= distance(n1, n2) < 2^(n+1) +// usefult for finding out which bucket a node belongs to +int distance_exp(node_id const& n1, node_id const& n2); + +} } // namespace libtorrent::dht + +#endif // NODE_ID_HPP + diff --git a/encryption/libtorrent/include/libtorrent/kademlia/observer.hpp b/encryption/libtorrent/include/libtorrent/kademlia/observer.hpp new file mode 100644 index 000000000..141460dc0 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/kademlia/observer.hpp @@ -0,0 +1,92 @@ +/* + +Copyright (c) 2007, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef OBSERVER_HPP +#define OBSERVER_HPP + +#include +#include +#include + +namespace libtorrent { +namespace dht { + +struct observer; +struct msg; + +// defined in rpc_manager.cpp +TORRENT_EXPORT void intrusive_ptr_add_ref(observer const*); +TORRENT_EXPORT void intrusive_ptr_release(observer const*); + +struct observer : boost::noncopyable +{ + friend TORRENT_EXPORT void intrusive_ptr_add_ref(observer const*); + friend TORRENT_EXPORT void intrusive_ptr_release(observer const*); + + observer(boost::pool<>& p) + : sent(time_now()) + , pool_allocator(p) + , m_refs(0) + {} + + virtual ~observer() {} + + // these two callbacks lets the observer add + // information to the message before it's sent + virtual void send(msg& m) = 0; + + // this is called when a reply is received + virtual void reply(msg const& m) = 0; + + // this is called when no reply has been received within + // some timeout + virtual void timeout() = 0; + + // 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 + virtual void abort() = 0; + + udp::endpoint target_addr; + ptime sent; +private: + boost::pool<>& pool_allocator; + // reference counter for intrusive_ptr + mutable boost::detail::atomic_count m_refs; +}; + +typedef boost::intrusive_ptr observer_ptr; + +} } + +#endif diff --git a/encryption/libtorrent/include/libtorrent/kademlia/packet_iterator.hpp b/encryption/libtorrent/include/libtorrent/kademlia/packet_iterator.hpp new file mode 100644 index 000000000..e906a90bf --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/kademlia/packet_iterator.hpp @@ -0,0 +1,95 @@ +/* + +Copyright (c) 2006, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef PACKET_ITERATOR_HPP +#define PACKET_ITERATOR_HPP + +#include +#include +#include + +namespace libtorrent { namespace dht +{ + +class packet_iterator: public boost::iterator_facade< + packet_iterator, const char, boost::forward_traversal_tag> +{ +public: + typedef std::vector::const_iterator base_iterator; + + packet_iterator() {} + + packet_iterator(std::vector::const_iterator start + , std::vector::const_iterator end + , std::string const& error_msg = "") + : m_base(start) + , m_end(end) + , m_msg(error_msg) + {} + + base_iterator base() const + { return m_base; } + + base_iterator end() const + { return m_end; } + + int left() const { return int(m_end - m_base); } + +private: + friend class boost::iterator_core_access; + + bool equal(packet_iterator const& other) const + { return m_base == other.m_base; } + + void advance(int n) + { + m_base += n; + } + + void increment() + { ++m_base; } + + char const& dereference() const + { + if (m_base == m_end) throw std::runtime_error(m_msg); + return *m_base; + } + + base_iterator m_base; + base_iterator m_end; + std::string m_msg; +}; + +} } // namespace libtorrent::dht + +#endif // PACKET_ITERATOR_HPP + diff --git a/encryption/libtorrent/include/libtorrent/kademlia/refresh.hpp b/encryption/libtorrent/include/libtorrent/kademlia/refresh.hpp new file mode 100644 index 000000000..953c4d871 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/kademlia/refresh.hpp @@ -0,0 +1,214 @@ +/* + +Copyright (c) 2006, Arvid Norberg & Daniel Wallin +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef REFRESH_050324_HPP +#define REFRESH_050324_HPP + +#include + +#include +#include +#include +#include + +#include + +namespace libtorrent { namespace dht +{ + +#ifdef TORRENT_DHT_VERBOSE_LOGGING +TORRENT_DECLARE_LOG(refresh); +#endif + +class routing_table; +class rpc_manager; + +class refresh : public traversal_algorithm +{ +public: + typedef boost::function done_callback; + + template + static void initiate( + node_id target + , int branch_factor + , int max_active_pings + , int max_results + , routing_table& table + , InIt first + , InIt last + , rpc_manager& rpc + , done_callback const& callback + ); + + void ping_reply(node_id id); + void ping_timeout(node_id id, bool prevent_request = false); + +private: + template + refresh( + node_id target + , int branch_factor + , int max_active_pings + , int max_results + , routing_table& table + , InIt first + , InIt last + , rpc_manager& rpc + , done_callback const& callback + ); + + void done(); + void invoke(node_id const& id, udp::endpoint addr); + + void invoke_pings_or_finish(bool prevent_request = false); + + int m_max_active_pings; + int m_active_pings; + + done_callback m_done_callback; + + std::vector::iterator m_leftover_nodes_iterator; +}; + +class refresh_observer : public observer +{ +public: + refresh_observer( + boost::intrusive_ptr const& algorithm + , node_id self + , node_id target) + : observer(algorithm->allocator()) + , m_target(target) + , m_self(self) + , m_algorithm(algorithm) + {} + ~refresh_observer(); + + void send(msg& m) + { + m.info_hash = m_target; + } + + void timeout(); + void reply(msg const& m); + void abort() { m_algorithm = 0; } + + +private: + node_id const m_target; + node_id const m_self; + boost::intrusive_ptr m_algorithm; +}; + +class ping_observer : public observer +{ +public: + ping_observer( + boost::intrusive_ptr const& algorithm + , node_id self) + : observer(algorithm->allocator()) + , m_self(self) + , m_algorithm(algorithm) + {} + ~ping_observer(); + + void send(msg& p) {} + void timeout(); + void reply(msg const& m); + void abort() { m_algorithm = 0; } + + +private: + node_id const m_self; + boost::intrusive_ptr m_algorithm; +}; + +template +inline refresh::refresh( + node_id target + , int branch_factor + , int max_active_pings + , int max_results + , routing_table& table + , InIt first + , InIt last + , rpc_manager& rpc + , done_callback const& callback +) + : traversal_algorithm( + target + , branch_factor + , max_results + , table + , rpc + , first + , last + ) + , m_max_active_pings(max_active_pings) + , m_active_pings(0) + , m_done_callback(callback) +{ + boost::intrusive_ptr self(this); + add_requests(); +} + +template +inline void refresh::initiate( + node_id target + , int branch_factor + , int max_active_pings + , int max_results + , routing_table& table + , InIt first + , InIt last + , rpc_manager& rpc + , done_callback const& callback +) +{ + new refresh( + target + , branch_factor + , max_active_pings + , max_results + , table + , first + , last + , rpc + , callback + ); +} + +} } // namespace libtorrent::dht + +#endif // REFRESH_050324_HPP + diff --git a/encryption/libtorrent/include/libtorrent/kademlia/routing_table.hpp b/encryption/libtorrent/include/libtorrent/kademlia/routing_table.hpp new file mode 100644 index 000000000..45a7dd762 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/kademlia/routing_table.hpp @@ -0,0 +1,248 @@ +/* + +Copyright (c) 2006, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef ROUTING_TABLE_HPP +#define ROUTING_TABLE_HPP + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +namespace libtorrent { namespace dht +{ + +using asio::ip::udp; + +//TORRENT_DECLARE_LOG(table); + +typedef std::vector bucket_t; + +// differences in the implementation from the description in +// the paper: +// +// * The routing table tree is not allocated dynamically, there +// are always 160 buckets. +// * Nodes are not marked as being stale, they keep a counter +// that tells how many times in a row they have failed. When +// a new node is to be inserted, the node that has failed +// the most times is replaced. If none of the nodes in the +// bucket has failed, then it is put in the replacement +// cache (just like in the paper). + +class routing_table; + +namespace aux +{ + + // Iterates over a flattened routing_table structure. + class routing_table_iterator + : public boost::iterator_facade< + routing_table_iterator + , node_entry const + , boost::forward_traversal_tag + > + { + public: + routing_table_iterator() + { + } + + private: + friend class libtorrent::dht::routing_table; + friend class boost::iterator_core_access; + + typedef boost::array, 160>::const_iterator + bucket_iterator_t; + + routing_table_iterator( + bucket_iterator_t begin + , bucket_iterator_t end) + : m_bucket_iterator(begin) + , m_bucket_end(end) + , m_iterator(begin != end ? begin->first.begin() : bucket_t::const_iterator()) + { + if (m_bucket_iterator == m_bucket_end) return; + while (m_iterator == m_bucket_iterator->first.end()) + { + if (++m_bucket_iterator == m_bucket_end) + break; + m_iterator = m_bucket_iterator->first.begin(); + } + } + + bool equal(routing_table_iterator const& other) const + { + return m_bucket_iterator == other.m_bucket_iterator + && (m_bucket_iterator == m_bucket_end + || m_iterator == other.m_iterator); + } + + void increment() + { + assert(m_bucket_iterator != m_bucket_end); + ++m_iterator; + while (m_iterator == m_bucket_iterator->first.end()) + { + if (++m_bucket_iterator == m_bucket_end) + break; + m_iterator = m_bucket_iterator->first.begin(); + } + } + + node_entry const& dereference() const + { + assert(m_bucket_iterator != m_bucket_end); + return *m_iterator; + } + + bucket_iterator_t m_bucket_iterator; + bucket_iterator_t m_bucket_end; + bucket_t::const_iterator m_iterator; + }; + +} // namespace aux + +class routing_table +{ +public: + typedef aux::routing_table_iterator iterator; + typedef iterator const_iterator; + + routing_table(node_id const& id, int bucket_size + , dht_settings const& settings); + + void node_failed(node_id const& id); + + // adds an endpoint that will never be added to + // the routing table + void add_router_node(udp::endpoint router); + + // iterates over the router nodes added + typedef std::set::const_iterator router_iterator; + router_iterator router_begin() const { return m_router_nodes.begin(); } + router_iterator router_end() const { return m_router_nodes.end(); } + + // this function is called every time the node sees + // a sign of a node being alive. This node will either + // be inserted in the k-buckets or be moved to the top + // of its bucket. + bool node_seen(node_id const& id, udp::endpoint addr); + + // returns time when the given bucket needs another refresh. + // if the given bucket is empty but there are nodes + // in a bucket closer to us, or if the bucket is non-empty and + // the time from the last activity is more than 15 minutes + ptime next_refresh(int bucket); + + // fills the vector with the count nodes from our buckets that + // are nearest to the given id. + void find_node(node_id const& id, std::vector& l + , bool include_self, int count = 0); + + // returns true if the given node would be placed in a bucket + // that is not full. If the node already exists in the table + // this function returns false + bool need_node(node_id const& id); + + // this will set the given bucket's latest activity + // to the current time + void touch_bucket(int bucket); + + int bucket_size(int bucket) + { + assert(bucket >= 0 && bucket < 160); + return (int)m_buckets[bucket].first.size(); + } + int bucket_size() const { return m_bucket_size; } + + iterator begin() const; + iterator end() const; + + boost::tuple size() const; + size_type num_global_nodes() const; + + // returns true if there are no working nodes + // in the routing table + bool need_bootstrap() const; + int num_active_buckets() const + { return 160 - m_lowest_active_bucket + 1; } + + void replacement_cache(bucket_t& nodes) const; +#ifdef TORRENT_DHT_VERBOSE_LOGGING + // 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 + +private: + + // constant called k in paper + int m_bucket_size; + + dht_settings const& m_settings; + + // 160 (k-bucket, replacement cache) pairs + typedef boost::array, 160> table_t; + table_t m_buckets; + // timestamps of the last activity in each bucket + typedef boost::array table_activity_t; + table_activity_t m_bucket_activity; + node_id m_id; // our own node id + + // this is a set of all the endpoints that have + // been identified as router nodes. They will + // be used in searches, but they will never + // be added to the routing table. + std::set m_router_nodes; + + // this is the lowest bucket index with nodes in it + int m_lowest_active_bucket; +}; + +} } // namespace libtorrent::dht + +#endif // ROUTING_TABLE_HPP + diff --git a/encryption/libtorrent/include/libtorrent/kademlia/rpc_manager.hpp b/encryption/libtorrent/include/libtorrent/kademlia/rpc_manager.hpp new file mode 100644 index 000000000..a7c47f29a --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/kademlia/rpc_manager.hpp @@ -0,0 +1,140 @@ +/* + +Copyright (c) 2006, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef RPC_MANAGER_HPP +#define RPC_MANAGER_HPP + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "libtorrent/time.hpp" + +namespace libtorrent { namespace dht +{ + +struct observer; + +using asio::ip::udp; +#ifdef TORRENT_DHT_VERBOSE_LOGGING +TORRENT_DECLARE_LOG(rpc); +#endif + +struct null_observer : public observer +{ + null_observer(boost::pool<>& allocator): observer(allocator) {} + virtual void reply(msg const&) {} + virtual void timeout() {} + virtual void send(msg&) {} + void abort() {} +}; + +class routing_table; + +class rpc_manager +{ +public: + typedef boost::function1 fun; + typedef boost::function1 send_fun; + + rpc_manager(fun const& incoming_fun, node_id const& our_id + , routing_table& table, send_fun const& sf); + ~rpc_manager(); + + // returns true if the node needs a refresh + bool incoming(msg const&); + time_duration tick(); + + void invoke(int message_id, udp::endpoint target + , observer_ptr o); + + void reply(msg& m); + void reply_with_ping(msg& m); + +#ifndef NDEBUG + void check_invariant() const; +#endif + + boost::pool<>& allocator() const + { return m_pool_allocator; } + +private: + + enum { max_transactions = 2048 }; + + unsigned int new_transaction_id(observer_ptr o); + void update_oldest_transaction_id(); + + boost::uint32_t calc_connection_id(udp::endpoint addr); + + mutable boost::pool<> m_pool_allocator; + + typedef boost::array + transactions_t; + transactions_t m_transactions; + std::vector m_aborted_transactions; + + // this is the next transaction id to be used + int m_next_transaction_id; + // this is the oldest transaction id still + // (possibly) in use. This is the transaction + // that will time out first, the one we are + // waiting for to time out + int m_oldest_transaction_id; + + fun m_incoming; + send_fun m_send; + node_id m_our_id; + routing_table& m_table; + ptime m_timer; + node_id m_random_number; + bool m_destructing; +}; + +} } // namespace libtorrent::dht + +#endif + + diff --git a/encryption/libtorrent/include/libtorrent/kademlia/traversal_algorithm.hpp b/encryption/libtorrent/include/libtorrent/kademlia/traversal_algorithm.hpp new file mode 100644 index 000000000..d51ed5506 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/kademlia/traversal_algorithm.hpp @@ -0,0 +1,162 @@ +/* + +Copyright (c) 2006, Arvid Norberg & Daniel Wallin +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TRAVERSAL_ALGORITHM_050324_HPP +#define TRAVERSAL_ALGORITHM_050324_HPP + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +namespace libtorrent { namespace dht +{ +#ifdef TORRENT_DHT_VERBOSE_LOGGING +TORRENT_DECLARE_LOG(traversal); +#endif + +class rpc_manager; + +// this class may not be instantiated as a stack object +class traversal_algorithm : boost::noncopyable +{ +public: + void traverse(node_id const& id, udp::endpoint addr); + void finished(node_id const& id); + void failed(node_id const& id, bool prevent_request = false); + virtual ~traversal_algorithm() {} + boost::pool<>& allocator() const; + +protected: + template + traversal_algorithm( + node_id target + , int branch_factor + , int max_results + , routing_table& table + , rpc_manager& rpc + , InIt start + , InIt end + ); + + void add_requests(); + void add_entry(node_id const& id, udp::endpoint addr, unsigned char flags); + + virtual void done() = 0; + virtual void invoke(node_id const& id, udp::endpoint addr) = 0; + + struct result + { + result(node_id const& id, udp::endpoint addr, unsigned char f = 0) + : id(id), addr(addr), flags(f) + {} + + node_id id; + udp::endpoint addr; + enum { queried = 1, initial = 2 }; + unsigned char flags; + }; + + std::vector::iterator last_iterator(); + + friend void intrusive_ptr_add_ref(traversal_algorithm* p) + { + p->m_ref_count++; + } + + friend void intrusive_ptr_release(traversal_algorithm* p) + { + if (--p->m_ref_count == 0) + delete p; + } + + int m_ref_count; + + node_id m_target; + int m_branch_factor; + int m_max_results; + std::vector m_results; + std::set m_failed; + routing_table& m_table; + rpc_manager& m_rpc; + int m_invoke_count; +}; + +template +traversal_algorithm::traversal_algorithm( + node_id target + , int branch_factor + , int max_results + , routing_table& table + , rpc_manager& rpc + , InIt start // <- nodes to initiate traversal with + , InIt end +) + : m_ref_count(0) + , m_target(target) + , m_branch_factor(branch_factor) + , m_max_results(max_results) + , m_table(table) + , m_rpc(rpc) + , m_invoke_count(0) +{ + using boost::bind; + + for (InIt i = start; i != end; ++i) + { + add_entry(i->id, i->addr, result::initial); + } + + // in case the routing table is empty, use the + // router nodes in the table + if (start == end) + { + for (routing_table::router_iterator i = table.router_begin() + , end(table.router_end()); i != end; ++i) + { + add_entry(node_id(0), *i, result::initial); + } + } + +} + +} } // namespace libtorrent::dht + +#endif // TRAVERSAL_ALGORITHM_050324_HPP + diff --git a/encryption/libtorrent/include/libtorrent/lsd.hpp b/encryption/libtorrent/include/libtorrent/lsd.hpp new file mode 100644 index 000000000..9ffbcdfc3 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/lsd.hpp @@ -0,0 +1,105 @@ +/* + +Copyright (c) 2007, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_LSD_HPP +#define TORRENT_LSD_HPP + +#include "libtorrent/socket.hpp" +#include "libtorrent/peer_id.hpp" + +#include +#include +#include +#include +#include + +#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING) +#include +#endif + +namespace libtorrent +{ + +typedef boost::function peer_callback_t; + +class lsd : boost::noncopyable +{ +public: + lsd(io_service& ios, address const& listen_interface + , peer_callback_t const& cb); + ~lsd(); + + void rebind(address const& listen_interface); + + void announce(sha1_hash const& ih, int listen_port); + void close(); + +private: + + static address_v4 lsd_multicast_address; + static udp::endpoint lsd_multicast_endpoint; + + void resend_announce(asio::error_code const& e, std::string msg); + void on_announce(asio::error_code const& e + , std::size_t bytes_transferred); + void setup_receive(); + + peer_callback_t m_callback; + + // current retry count + int m_retry_count; + + // used to receive responses in + char m_receive_buffer[1024]; + + // the endpoint we received the message from + udp::endpoint m_remote; + + // the udp socket used to send and receive + // multicast messages on + datagram_socket m_socket; + + // used to resend udp packets in case + // they time out + deadline_timer m_broadcast_timer; + + bool m_disabled; +#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING) + std::ofstream m_log; +#endif +}; + +} + + +#endif + diff --git a/encryption/libtorrent/include/libtorrent/natpmp.hpp b/encryption/libtorrent/include/libtorrent/natpmp.hpp new file mode 100644 index 000000000..1c0ffd0be --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/natpmp.hpp @@ -0,0 +1,150 @@ +/* + +Copyright (c) 2007, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_NATPMP_HPP +#define TORRENT_NATPMP_HPP + +#include "libtorrent/socket.hpp" + +#include + +#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING) +#include +#endif + +namespace libtorrent +{ + +// int: external tcp port +// int: external udp port +// std::string: error message +typedef boost::function portmap_callback_t; + +class natpmp +{ +public: + natpmp(io_service& ios, address const& listen_interface, portmap_callback_t const& cb); + + void rebind(address const& listen_interface); + + // maps the ports, if a port is set to 0 + // it will not be mapped + void set_mappings(int tcp, int udp); + + void close(); + +private: + + void update_mapping(int i, int port); + void send_map_request(int i); + void resend_request(int i, asio::error_code const& e); + void on_reply(asio::error_code const& e + , std::size_t bytes_transferred); + void try_next_mapping(int i); + void update_expiration_timer(); + void refresh_mapping(int i); + void mapping_expired(asio::error_code const& e, int i); + + struct mapping + { + mapping() + : need_update(false) + , local_port(0) + , external_port(0) + , protocol(1) + {} + + // indicates that the mapping has changed + // and needs an update + bool need_update; + + // the time the port mapping will expire + ptime 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; + + // 1 = udp, 2 = tcp + int protocol; + }; + + portmap_callback_t m_callback; + + // 0 is tcp and 1 is udp + mapping m_mappings[2]; + + // 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]; + + // the endpoint we received the message from + udp::endpoint m_remote; + + // the udp socket used to communicate + // with the NAT router + datagram_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; + + bool m_disabled; + +#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING) + std::ofstream m_log; +#endif +}; + +} + + +#endif + diff --git a/encryption/libtorrent/include/libtorrent/pch.hpp b/encryption/libtorrent/include/libtorrent/pch.hpp new file mode 100644 index 000000000..735999826 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/pch.hpp @@ -0,0 +1,96 @@ +#ifdef BOOST_BUILD_PCH_ENABLED + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __OBJC__ +#define Protocol Protocol_ +#endif + +#include +#include +#include +#include +#include +#include + +#ifdef __OBJC__ +#undef Protocol +#endif + +#endif + diff --git a/encryption/libtorrent/include/libtorrent/pe_crypto.hpp b/encryption/libtorrent/include/libtorrent/pe_crypto.hpp new file mode 100644 index 000000000..91616c42d --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/pe_crypto.hpp @@ -0,0 +1,125 @@ +/* + +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. + +*/ + +#ifndef TORRENT_DISABLE_ENCRYPTION + +#ifndef TORRENT_PE_CRYPTO_HPP_INCLUDED +#define TORRENT_PE_CRYPTO_HPP_INCLUDED + +#include + +#include +#include +#include + +#include "peer_id.hpp" // For sha1_hash + +namespace libtorrent +{ + class DH_key_exchange + { + public: + DH_key_exchange (); + ~DH_key_exchange (); + + // Get local public key, always 96 bytes + char const* get_local_key (void) const; + + // read remote_pubkey, generate and store shared secret in + // m_dh_secret + void compute_secret (const char* remote_pubkey); + + const char* get_secret (void) const; + + private: + int get_local_key_size () const + { + assert (m_DH); + return BN_num_bytes (m_DH->pub_key); + } + + DH* m_DH; + static const unsigned char m_dh_prime[96]; + static const unsigned char m_dh_generator[1]; + + char m_dh_local_key[96]; + char m_dh_secret[96]; + }; + + class RC4_handler // Non copyable + { + public: + // Input longkeys must be 20 bytes + RC4_handler (const sha1_hash& rc4_local_longkey, + const sha1_hash& rc4_remote_longkey) + + { + RC4_set_key (&m_local_key, 20, + reinterpret_cast(rc4_local_longkey.begin())); + RC4_set_key (&m_remote_key, 20, + reinterpret_cast(rc4_remote_longkey.begin())); + + // Discard first 1024 bytes + char buf[1024]; + encrypt (buf, 1024); + decrypt (buf, 1024); + }; + + ~RC4_handler () {}; + + void encrypt (char* pos, int len) + { + assert (len >= 0); + assert (pos); + + RC4 (&m_local_key, len, reinterpret_cast(pos), + reinterpret_cast(pos)); + } + + void decrypt (char* pos, int len) + { + assert (len >= 0); + assert (pos); + + RC4 (&m_remote_key, len, reinterpret_cast(pos), + reinterpret_cast(pos)); + } + + private: + RC4_KEY m_local_key; // Key to encrypt outgoing data + RC4_KEY m_remote_key; // Key to decrypt incoming data + }; + +} // namespace libtorrent + +#endif // TORRENT_PE_CRYPTO_HPP_INCLUDED +#endif // TORRENT_DISABLE_ENCRYPTION diff --git a/encryption/libtorrent/include/libtorrent/peer.hpp b/encryption/libtorrent/include/libtorrent/peer.hpp new file mode 100755 index 000000000..c404a611d --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/peer.hpp @@ -0,0 +1,63 @@ +/* + +Copyright (c) 2003, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_PEER_HPP_INCLUDED +#define TORRENT_PEER_HPP_INCLUDED + +#include + +#include "libtorrent/peer_id.hpp" + +namespace libtorrent +{ + + struct TORRENT_EXPORT peer_entry + { + std::string ip; + int port; + peer_id pid; + + bool operator==(const peer_entry& p) const + { + return pid == p.pid; + } + + bool operator<(const peer_entry& p) const + { + return pid < p.pid; + } + }; + +} + +#endif // TORRENT_PEER_HPP_INCLUDED + diff --git a/encryption/libtorrent/include/libtorrent/peer_connection.hpp b/encryption/libtorrent/include/libtorrent/peer_connection.hpp new file mode 100755 index 000000000..9514874fb --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/peer_connection.hpp @@ -0,0 +1,692 @@ +/* + +Copyright (c) 2003, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_PEER_CONNECTION_HPP_INCLUDED +#define TORRENT_PEER_CONNECTION_HPP_INCLUDED + +#include +#include +#include +#include +#include + +#include "libtorrent/debug.hpp" + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include +#include +#include +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/buffer.hpp" +#include "libtorrent/socket.hpp" +#include "libtorrent/peer_id.hpp" +#include "libtorrent/storage.hpp" +#include "libtorrent/stat.hpp" +#include "libtorrent/alert.hpp" +#include "libtorrent/torrent_handle.hpp" +#include "libtorrent/torrent.hpp" +#include "libtorrent/allocate_resources.hpp" +#include "libtorrent/peer_request.hpp" +#include "libtorrent/piece_block_progress.hpp" +#include "libtorrent/config.hpp" +#include "libtorrent/session.hpp" +#include "libtorrent/bandwidth_manager.hpp" +#include "libtorrent/policy.hpp" +#include "libtorrent/socket_type.hpp" + +// TODO: each time a block is 'taken over' +// from another peer. That peer must be given +// a chance to become not-interested. + +namespace libtorrent +{ + class torrent; + struct peer_plugin; + + namespace detail + { + struct session_impl; + } + + TORRENT_EXPORT void intrusive_ptr_add_ref(peer_connection const*); + TORRENT_EXPORT void intrusive_ptr_release(peer_connection const*); + + struct TORRENT_EXPORT protocol_error: std::runtime_error + { + protocol_error(const std::string& msg): std::runtime_error(msg) {}; + }; + + class TORRENT_EXPORT peer_connection + : public boost::noncopyable + { + friend class invariant_access; + friend TORRENT_EXPORT void intrusive_ptr_add_ref(peer_connection const*); + friend TORRENT_EXPORT void intrusive_ptr_release(peer_connection const*); + public: + + enum channels + { + upload_channel, + download_channel, + num_channels + }; + + // this is the constructor where the we are the active part. + // The peer_conenction should handshake and verify that the + // other end has the correct id + peer_connection( + aux::session_impl& ses + , boost::weak_ptr t + , boost::shared_ptr s + , tcp::endpoint const& remote + , policy::peer* peerinfo); + + // with this constructor we have been contacted and we still don't + // know which torrent the connection belongs to + peer_connection( + aux::session_impl& ses + , boost::shared_ptr s + , policy::peer* peerinfo); + + virtual ~peer_connection(); + + void set_peer_info(policy::peer* pi) + { m_peer_info = pi; } + + policy::peer* peer_info_struct() const + { return m_peer_info; } + + enum peer_speed_t { slow, medium, fast }; + peer_speed_t peer_speed(); + +#ifndef TORRENT_DISABLE_EXTENSIONS + void add_extension(boost::shared_ptr); +#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 set_upload_limit(int limit); + void set_download_limit(int limit); + + int upload_limit() const { return m_upload_limit; } + int download_limit() const { return m_download_limit; } + + bool prefer_whole_pieces() const + { return m_prefer_whole_pieces; } + + void prefer_whole_pieces(bool b) + { m_prefer_whole_pieces = b; } + + bool request_large_blocks() const + { return m_request_large_blocks; } + + void request_large_blocks(bool b) + { m_request_large_blocks = b; } + + void set_non_prioritized(bool b) + { m_non_prioritized = b; } + + // this adds an announcement in the announcement queue + // it will let the peer know that we have the given piece + void announce_piece(int index); + + // tells if this connection has data it want to send + // and has enough upload bandwidth quota left to send it. + bool can_write() const; + bool can_read() const; + + bool is_seed() const; + + bool has_timed_out() const; + + // will send a keep-alive message to the peer + void keep_alive(); + + peer_id const& pid() const { return m_peer_id; } + void set_pid(const peer_id& pid) { m_peer_id = pid; } + bool has_piece(int i) const; + + const std::deque& download_queue() const; + const std::deque& request_queue() const; + const std::deque& upload_queue() const; + + bool is_interesting() const { return m_interesting; } + bool is_choked() const { return m_choked; } + + bool is_peer_interested() const { return m_peer_interested; } + bool has_peer_choked() const { return m_peer_choked; } + + 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; } + + const stat& statistics() const { return m_statistics; } + void add_stat(size_type downloaded, size_type uploaded); + + // is called once every second by the main loop + void second_tick(float tick_interval); + + boost::shared_ptr get_socket() const { return m_socket; } + tcp::endpoint const& remote() const { return m_remote; } + + std::vector const& get_bitfield() const; + + void timed_out(); + // this will cause this peer_connection to be disconnected. + void disconnect(); + bool is_disconnecting() const { return m_disconnecting; } + + // this is called when the connection attempt has succeeded + // and the peer_connection is supposed to set m_connecting + // to false, and stop monitor writability + void on_connection_complete(asio::error_code const& e); + + // returns true if this connection is still waiting to + // finish the connection attempt + bool is_connecting() const { return m_connecting; } + + // returns true if the socket of this peer hasn't been + // attempted to connect yet (i.e. it's queued for + // connection attempt). + bool is_queued() const { return m_queued; } + + // called when it's time for this peer_conncetion to actually + // initiate the tcp connection. This may be postponed until + // the library isn't using up the limitation of half-open + // tcp connections. + void connect(int ticket); + + // This is called for every peer right after the upload + // bandwidth has been distributed among them + // It will reset the used bandwidth to 0. + void reset_upload_quota(); + + // free upload. + size_type total_free_upload() const; + void add_free_upload(size_type free_upload); + + // trust management. + void received_valid_data(int index); + void received_invalid_data(int index); + + size_type share_diff() const; + + // a connection is local if it was initiated by us. + // if it was an incoming connection, it is remote + bool is_local() const { return m_active; } + + bool on_local_network() const; + bool ignore_bandwidth_limits() const + { return m_ignore_bandwidth_limits; } + + void set_failed() { m_failed = true; } + bool failed() const { return m_failed; } + + int desired_queue_size() const { return m_desired_queue_size; } + +#ifdef TORRENT_VERBOSE_LOGGING + boost::shared_ptr m_logger; +#endif + + // the message handlers are called + // each time a recv() returns some new + // data, the last time it will be called + // is when the entire packet has been + // received, then it will no longer + // be called. i.e. most handlers need + // to check how much of the packet they + // have received before any processing + void incoming_keepalive(); + void incoming_choke(); + void incoming_unchoke(); + void incoming_interested(); + void incoming_not_interested(); + void incoming_have(int piece_index); + void incoming_bitfield(std::vector const& bitfield); + void incoming_request(peer_request const& r); + void incoming_piece(peer_request const& p, char const* data); + void incoming_piece_fragment(); + void incoming_cancel(peer_request const& r); + void incoming_dht_port(int listen_port); + + // the following functions appends messages + // to the send buffer + void send_choke(); + void send_unchoke(); + void send_interested(); + void send_not_interested(); + + // adds a block to the request queue + void add_request(piece_block const& b); + // 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) + void cancel_request(piece_block const& b); + void send_block_requests(); + + int max_assignable_bandwidth(int channel) const + { + return m_bandwidth_limit[channel].max_assignable(); + } + + void assign_bandwidth(int channel, int amount); + void expire_bandwidth(int channel, int amount); + +#ifndef NDEBUG + void check_invariant() const; + ptime m_last_choke; +#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 + { + #ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << "downloading_piece_progress() dispatched to the base class!\n"; + #endif + return boost::optional(); + } + + void send_buffer(char const* begin, char const* end); + buffer::interval allocate_send_buffer(int size); + void setup_send(); + +#ifndef TORRENT_DISABLE_RESOLVE_COUNTRIES + void set_country(char const* c) + { + assert(strlen(c) == 2); + m_country[0] = c[0]; + m_country[1] = c[1]; + } + bool has_country() const { return m_country[0] != 0; } +#endif + + protected: + + 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_keepalive() = 0; + virtual void write_piece(peer_request const& r) = 0; + + virtual void on_connected() = 0; + virtual void on_tick() {} + + virtual void on_receive(asio::error_code const& error + , std::size_t bytes_transferred) = 0; + virtual void on_sent(asio::error_code const& error + , std::size_t bytes_transferred) = 0; + + int send_buffer_size() const + { + return (int)m_send_buffer[0].size() + + (int)m_send_buffer[1].size() + - m_write_pos; + } + +#ifndef TORRENT_DISABLE_ENCRYPTION + buffer::interval wr_recv_buffer() + { + return buffer::interval(&m_recv_buffer[0] + , &m_recv_buffer[0] + m_recv_pos); + } +#endif + + buffer::const_interval receive_buffer() const + { + return buffer::const_interval(&m_recv_buffer[0] + , &m_recv_buffer[0] + m_recv_pos); + } + + void cut_receive_buffer(int size, int packet_size); + + void reset_recv_buffer(int packet_size); + int packet_size() const { return m_packet_size; } + + bool packet_finished() const + { + return m_packet_size <= m_recv_pos; + } + + void setup_receive(); + + void attach_to_torrent(sha1_hash const& ih); + + bool verify_piece(peer_request const& p) const; + + // the bandwidth channels, upload and download + // keeps track of the current quotas + bandwidth_limit m_bandwidth_limit[num_channels]; + + // statistics about upload and download speeds + // and total amount of uploads and downloads for + // this peer + stat m_statistics; + + // a back reference to the session + // the peer belongs to. + aux::session_impl& m_ses; + + boost::intrusive_ptr self() + { return boost::intrusive_ptr(this); } + + // called from the main loop when this connection has any + // work to do. + void on_send_data(asio::error_code const& error + , std::size_t bytes_transferred); + void on_receive_data(asio::error_code const& error + , std::size_t bytes_transferred); + + // this is the limit on the number of outstanding requests + // we have to this peer. This is initialized to the settings + // in the session_settings structure. But it may be lowered + // if the peer is known to require a smaller limit (like BitComet). + // or if the extended handshake sets a limit. + // web seeds also has a limit on the queue size. + int m_max_out_request_queue; + + void set_timeout(int s) { m_timeout = s; } + +#ifndef TORRENT_DISABLE_EXTENSIONS + typedef std::list > extension_list_t; + extension_list_t m_extensions; +#endif + +#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 + + private: + + void fill_send_buffer(); + + // the timeout in seconds + int m_timeout; + + // the time when we last got a part of a + // piece packet from this peer + ptime m_last_piece; + // the time we sent a request to + // this peer the last time + ptime m_last_request; + + int m_packet_size; + int m_recv_pos; + std::vector m_recv_buffer; + + // this is the buffer where data that is + // to be sent is stored until it gets + // consumed by send(). Since asio requires + // the memory buffer that is given to async. + // operations to remain valid until the operation + // finishes, there has to be two buffers. While + // waiting for a async_write operation on one + // buffer, the other is used to write data to + // be queued up. + std::vector m_send_buffer[2]; + // the current send buffer is the one to write to. + // (m_current_send_buffer + 1) % 2 is the + // buffer we're currently waiting for. + int m_current_send_buffer; + + // if the sending buffer doesn't finish in one send + // operation, this is the position within that buffer + // where the next operation should continue + int m_write_pos; + + // timeouts + ptime m_last_receive; + ptime m_last_sent; + + boost::shared_ptr m_socket; + // 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; + + // this is the torrent this connection is + // associated with. If the connection is an + // incoming conncetion, this is set to zero + // until the info_hash is received. Then it's + // set to the torrent it belongs to. + boost::weak_ptr m_torrent; + // is true if it was we that connected to the peer + // and false if we got an incomming connection + // could be considered: true = local, false = remote + bool m_active; + + // remote peer's id + peer_id m_peer_id; + + // other side says that it's interested in downloading + // from us. + bool m_peer_interested; + + // the other side has told us that it won't send anymore + // data to us for a while + bool m_peer_choked; + + // the peer has pieces we are interested in + bool m_interesting; + + // we have choked the upload to the peer + bool m_choked; + + // this is set to true if the connection timed + // out or closed the connection. In that + // case we will not try to reconnect to + // this peer + bool m_failed; + + // if this is set to true, the peer will not + // request bandwidth from the limiter, but instead + // just send and receive as much as possible. + bool m_ignore_bandwidth_limits; + + // the pieces the other end have + std::vector m_have_piece; + + // the number of pieces this peer + // has. Must be the same as + // std::count(m_have_piece.begin(), + // m_have_piece.end(), true) + int m_num_pieces; + + // the queue of requests we have got + // from this peer + std::deque m_requests; + + // the blocks we have reserved in the piece + // picker and will send to this peer. + std::deque m_request_queue; + + // the queue of blocks we have requested + // from this peer + std::deque m_download_queue; + + // the number of request we should queue up + // at the remote end. + int m_desired_queue_size; + + // the amount of data this peer has been given + // as free upload. This is distributed from + // peers from which we get free download + // this will be negative on a peer from which + // we get free download, and positive on peers + // that we give the free upload, to keep the balance. + size_type m_free_upload; + + // if this is true, this peer is assumed to handle all piece + // requests in fifo order. All skipped blocks are re-requested + // immediately instead of having a looser requirement + // where blocks can be sent out of order. The default is to + // allow non-fifo order. + bool m_assume_fifo; + + // the number of invalid piece-requests + // we have got from this peer. If the request + // queue gets empty, and there have been + // invalid requests, we can assume the + // peer is waiting for those pieces. + // we can then clear its download queue + // by sending choke, unchoke. + int m_num_invalid_requests; + + // this is true if this connection has been added + // to the list of connections that will be closed. + bool m_disconnecting; + + // the time when this peer sent us a not_interested message + // the last time. + ptime m_became_uninterested; + + // the time when we sent a not_interested message to + // this peer the last time. + ptime m_became_uninteresting; + + // this is true until this socket has become + // writable for the first time (i.e. the + // connection completed). While connecting + // the timeout will not be triggered. This is + // because windows XP SP2 may delay connection + // attempts, which means that the connection + // may not even have been attempted when the + // time out is reached. + bool m_connecting; + + // This is true until connect is called on the + // peer_connection's socket. It is false on incoming + // connections. + bool m_queued; + + // these are true when there's a asynchronous write + // or read operation running. + bool m_writing; + bool m_reading; + + // if set to true, this peer will always prefer + // to request entire pieces, rather than blocks. + // if it is false, the download rate limit setting + // will be used to determine if whole pieces + // are preferred. + bool m_prefer_whole_pieces; + + // 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; + + // if this is true, other (prioritized) peers will + // skip ahead of it in the queue for bandwidth. The + // effect is that non prioritized peers will only use + // the left-over bandwidth (suitable for web seeds). + bool m_non_prioritized; + + // reference counter for intrusive_ptr + mutable boost::detail::atomic_count m_refs; + + int m_upload_limit; + int m_download_limit; + + // 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. + policy::peer* m_peer_info; + + // this is a measurement of how fast the peer + // it allows some variance without changing + // back and forth between states + peer_speed_t m_speed; + + // the ticket id from the connection queue. + // This is used to identify the connection + // so that it can be removed from the queue + // once the connection completes + int m_connection_ticket; +#ifndef NDEBUG + public: + bool m_in_constructor; +#endif + }; +} + +#endif // TORRENT_PEER_CONNECTION_HPP_INCLUDED + diff --git a/encryption/libtorrent/include/libtorrent/peer_id.hpp b/encryption/libtorrent/include/libtorrent/peer_id.hpp new file mode 100755 index 000000000..23a5eb463 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/peer_id.hpp @@ -0,0 +1,191 @@ +/* + +Copyright (c) 2003, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_PEER_ID_HPP_INCLUDED +#define TORRENT_PEER_ID_HPP_INCLUDED + +#include +#include +#include +#include +#include +#include + +#include "libtorrent/config.hpp" + +namespace libtorrent +{ + + class TORRENT_EXPORT big_number + { + // private type + struct private_pointer {}; + // the number of bytes of the number + enum { number_size = 20 }; + public: + enum { size = number_size }; + + big_number() {} + + big_number(std::string const& s) + { + int sl = int(s.size()) < size ? int(s.size()) : size; + std::memcpy(m_number, &s[0], sl); + } + + // when initialized with 0 + big_number(private_pointer*) { clear(); } + + void clear() + { + std::fill(m_number,m_number+number_size,0); + } + + bool is_all_zeros() const + { + return std::count(m_number,m_number+number_size,0) == number_size; + } + + bool operator==(big_number const& n) const + { + return std::equal(n.m_number, n.m_number+number_size, m_number); + } + + bool operator!=(big_number const& n) const + { + return !std::equal(n.m_number, n.m_number+number_size, m_number); + } + + bool operator<(big_number const& n) const + { + for (int i = 0; i < number_size; ++i) + { + if (m_number[i] < n.m_number[i]) return true; + if (m_number[i] > n.m_number[i]) return false; + } + return false; + } + + big_number operator~() + { + big_number ret; + for (int i = 0; i< number_size; ++i) + ret.m_number[i] = ~m_number[i]; + return ret; + } + + big_number& operator &= (big_number const& n) + { + for (int i = 0; i< number_size; ++i) + m_number[i] &= n.m_number[i]; + return *this; + } + + big_number& operator |= (big_number const& n) + { + for (int i = 0; i< number_size; ++i) + m_number[i] |= n.m_number[i]; + return *this; + } + + big_number& operator ^= (big_number const& n) + { + for (int i = 0; i< number_size; ++i) + m_number[i] ^= n.m_number[i]; + return *this; + } + + unsigned char& operator[](int i) + { assert(i >= 0 && i < number_size); return m_number[i]; } + + unsigned char const& operator[](int i) const + { assert(i >= 0 && i < number_size); return m_number[i]; } + + typedef const unsigned char* const_iterator; + typedef unsigned char* iterator; + + const_iterator begin() const { return m_number; } + const_iterator end() const { return m_number+number_size; } + + iterator begin() { return m_number; } + iterator end() { return m_number+number_size; } + + private: + + unsigned char m_number[number_size]; + + }; + + typedef big_number peer_id; + typedef big_number sha1_hash; + + inline std::ostream& operator<<(std::ostream& os, big_number const& peer) + { + for (big_number::const_iterator i = peer.begin(); + i != peer.end(); ++i) + { + os << std::hex << std::setw(2) << std::setfill('0') + << static_cast(*i); + } + os << std::dec << std::setfill(' '); + return os; + } + + inline std::istream& operator>>(std::istream& is, big_number& peer) + { + using namespace std; + + for (big_number::iterator i = peer.begin(); + i != peer.end(); ++i) + { + char c[2]; + is >> c[0] >> c[1]; + c[0] = tolower(c[0]); + c[1] = tolower(c[1]); + if ( + ((c[0] < '0' || c[0] > '9') && (c[0] < 'a' || c[0] > 'f')) + || ((c[1] < '0' || c[1] > '9') && (c[1] < 'a' || c[1] > 'f')) + || is.fail()) + { + is.setstate(ios_base::failbit); + return is; + } + *i = ((isdigit(c[0])?c[0]-'0':c[0]-'a'+10) << 4) + + (isdigit(c[1])?c[1]-'0':c[1]-'a'+10); + } + return is; + } + +} + +#endif // TORRENT_PEER_ID_HPP_INCLUDED + diff --git a/encryption/libtorrent/include/libtorrent/peer_info.hpp b/encryption/libtorrent/include/libtorrent/peer_info.hpp new file mode 100755 index 000000000..0f398284b --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/peer_info.hpp @@ -0,0 +1,148 @@ +/* + +Copyright (c) 2003, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_PEER_INFO_HPP_INCLUDED +#define TORRENT_PEER_INFO_HPP_INCLUDED + +#include + +#include "libtorrent/socket.hpp" +#include "libtorrent/peer_id.hpp" +#include "libtorrent/size_type.hpp" +#include "libtorrent/config.hpp" + +namespace libtorrent +{ + struct TORRENT_EXPORT peer_info + { + enum + { + interesting = 0x1, + choked = 0x2, + remote_interested = 0x4, + remote_choked = 0x8, + supports_extensions = 0x10, + local_connection = 0x20, + handshake = 0x40, + connecting = 0x80, + queued = 0x100, + on_parole = 0x200, + seed = 0x400 +#ifndef TORRENT_DISABLE_ENCRYPTION + , rc4_encrypted = 0x200, + plaintext_encrypted = 0x400 +#endif + }; + + unsigned int flags; + + enum peer_source_flags + { + tracker = 0x1, + dht = 0x2, + pex = 0x4, + lsd = 0x8, + resume_data = 0x10 + }; + + int source; + + tcp::endpoint ip; + float up_speed; + float down_speed; + float payload_up_speed; + float payload_down_speed; + size_type total_download; + size_type total_upload; + peer_id pid; + std::vector pieces; + int upload_limit; + int download_limit; + + // time since last request + time_duration last_request; + + // time since last download or upload + time_duration last_active; + + // the size of the send buffer for this peer + int send_buffer_size; + + // the number of failed hashes for this peer + int num_hashfails; + +#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 country[2]; +#endif + + size_type load_balancing; + + // this is the number of requests + // we have sent to this peer + // that we haven't got a response + // for yet + int download_queue_length; + + // this is the number of requests + // the peer has sent to us + // that we haven't sent yet + int upload_queue_length; + + // the number of times this IP + // has failed to connect + int failcount; + + // the currently downloading piece + // if piece index is -1 all associated + // members are just set to 0 + int downloading_piece_index; + int downloading_block_index; + int downloading_progress; + int downloading_total; + + std::string client; + + enum + { + standard_bittorrent = 0, + web_seed = 1 + }; + int connection_type; + }; + +} + +#endif // TORRENT_PEER_INFO_HPP_INCLUDED diff --git a/encryption/libtorrent/include/libtorrent/peer_request.hpp b/encryption/libtorrent/include/libtorrent/peer_request.hpp new file mode 100644 index 000000000..445ff4d7e --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/peer_request.hpp @@ -0,0 +1,49 @@ +/* + +Copyright (c) 2006, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_PEER_REQUEST_HPP_INCLUDED +#define TORRENT_PEER_REQUEST_HPP_INCLUDED + +namespace libtorrent +{ + struct TORRENT_EXPORT peer_request + { + int piece; + int start; + int length; + bool operator==(peer_request const& r) const + { return piece == r.piece && start == r.start && length == r.length; } + }; +} + +#endif // TORRENT_PEER_REQUEST_HPP_INCLUDED + diff --git a/encryption/libtorrent/include/libtorrent/piece_block_progress.hpp b/encryption/libtorrent/include/libtorrent/piece_block_progress.hpp new file mode 100644 index 000000000..481ffc971 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/piece_block_progress.hpp @@ -0,0 +1,57 @@ +/* + +Copyright (c) 2003, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_PIECE_BLOCK_PROGRESS_HPP_INCLUDED +#define TORRENT_PIECE_BLOCK_PROGRESS_HPP_INCLUDED + +#include "libtorrent/config.hpp" + +namespace libtorrent +{ + struct TORRENT_EXPORT piece_block_progress + { + // the piece and block index + // determines exactly which + // part of the torrent that + // is currently being downloaded + int piece_index; + int block_index; + // the number of bytes we have received + // of this block + int bytes_downloaded; + // the number of bytes in the block + int full_block_bytes; + }; +} + +#endif // TORRENT_PIECE_BLOCK_PROGRESS_HPP_INCLUDED + diff --git a/encryption/libtorrent/include/libtorrent/piece_picker.hpp b/encryption/libtorrent/include/libtorrent/piece_picker.hpp new file mode 100755 index 000000000..7b8612909 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/piece_picker.hpp @@ -0,0 +1,414 @@ +/* + +Copyright (c) 2003, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ +#ifndef TORRENT_PIECE_PICKER_HPP_INCLUDED +#define TORRENT_PIECE_PICKER_HPP_INCLUDED + +#include +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/peer_id.hpp" +#include "libtorrent/socket.hpp" +#include "libtorrent/session_settings.hpp" +#include "libtorrent/config.hpp" + +namespace libtorrent +{ + + class torrent; + class peer_connection; + + struct TORRENT_EXPORT piece_block + { + piece_block(int p_index, int b_index) + : piece_index(p_index) + , block_index(b_index) + {} + int piece_index; + int block_index; + + bool operator<(piece_block const& b) const + { + if (piece_index < b.piece_index) return true; + if (piece_index == b.piece_index) return block_index < b.block_index; + return false; + } + + bool operator==(piece_block const& b) const + { return piece_index == b.piece_index && block_index == b.block_index; } + + bool operator!=(piece_block const& b) const + { return piece_index != b.piece_index || block_index != b.block_index; } + + }; + + class TORRENT_EXPORT piece_picker + { + public: + + struct block_info + { + block_info(): num_downloads(0), requested(0), finished(0) {} + // the peer this block was requested or + // downloaded from + tcp::endpoint peer; + // the number of times this block has been downloaded + unsigned num_downloads:14; + unsigned requested:1; + unsigned finished:1; + }; + + // the peers that are downloading this piece + // are considered fast peers or slow peers. + // none is set if the blocks were downloaded + // in a previous session + enum piece_state_t + { none, slow, medium, fast }; + + struct downloading_piece + { + downloading_piece(): finished(0), requested(0) {} + piece_state_t state; + + // the index of the piece + int index; + // info about each block + // this is a pointer into the m_block_info + // vector owned by the piece_picker + block_info* info; + boost::uint16_t finished; + boost::uint16_t requested; + }; + + piece_picker(int blocks_per_piece + , int total_num_blocks); + + void set_sequenced_download_threshold(int sequenced_download_threshold); + + // the vector tells which pieces we already have + // and which we don't have. + void files_checked( + const std::vector& pieces + , const std::vector& unfinished); + + // increases the peer count for the given piece + // (is used when a HAVE or BITFIELD message is received) + void inc_refcount(int index); + + // decreases the peer count for the given piece + // (used when a peer disconnects) + void dec_refcount(int index); + + // 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(); + void dec_refcount_all(); + + // 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); + + // sets the priority of a piece. + void 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 tcp::endpoint of the peer that we'll download + // from. + void pick_pieces(const std::vector& pieces + , std::vector& interesting_blocks + , int num_pieces, bool prefer_whole_pieces + , tcp::endpoint peer, piece_state_t speed) const; + + // returns true if any client is currently downloading this + // piece-block, or if it's queued for downloading by some client + // or if it already has been successfully downloaded + bool is_downloading(piece_block block) const; + bool is_finished(piece_block block) const; + + // marks this piece-block as queued for downloading + void mark_as_downloading(piece_block block, tcp::endpoint const& peer + , piece_state_t s); + void mark_as_finished(piece_block block, tcp::endpoint const& peer); + + // if a piece had a hash-failure, it must be restored and + // made available for redownloading + void restore_piece(int index); + + // clears the given piece's download flag + // this means that this piece-block can be picked again + void abort_download(piece_block block); + + bool is_piece_finished(int index) const; + + // returns the number of blocks there is in the given piece + int blocks_in_piece(int index) const; + + // the number of downloaded blocks that hasn't passed + // the hash-check yet + int unverified_blocks() const; + + void get_downloaders(std::vector& d, int index) const; + + std::vector const& get_download_queue() const + { return m_downloads; } + + boost::optional get_downloader(piece_block block) const; + + // the number of filtered pieces we don't have + int num_filtered() const { return m_num_filtered; } + + // the number of filtered pieces we already have + int num_have_filtered() const { return m_num_have_filtered; } + +#ifndef NDEBUG + // used in debug mode + void check_invariant(const torrent* t = 0) const; +#endif + + // functor that compares indices on downloading_pieces + struct has_index + { + has_index(int i): index(i) { assert(i >= 0); } + bool operator()(const downloading_piece& p) const + { return p.index == index; } + int index; + }; + + int blocks_in_last_piece() const + { return m_blocks_in_last_piece; } + + float distributed_copies() const; + + private: + + struct piece_pos + { + piece_pos() {} + piece_pos(int peer_count_, int index_) + : peer_count(peer_count_) + , downloading(0) + , piece_priority(1) + , index(index_) + { + assert(peer_count_ >= 0); + assert(index_ >= 0); + } + + // selects which vector to look in + unsigned peer_count : 10; + // is 1 if the piece is marked as being downloaded + unsigned downloading : 1; + // is 0 if the piece is filtered (not to be downloaded) + // 1 is normal priority (default) + // 2 is higher priority than pieces at the same availability level + // 3 is same priority as partial pieces + // 4 is higher priority than partial pieces + // 5 and 6 same priority as availability 1 (ignores availability) + // 7 is maximum priority (ignores availability) + unsigned piece_priority : 3; + // index in to the piece_info vector + unsigned index : 18; + + 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. + we_have_index = 0x3ffff, + // the priority value that means the piece is filtered + filter_priority = 0, + // the max number the peer count can hold + max_peer_count = 0x3ff + }; + + bool have() const { return index == we_have_index; } + void set_have() { index = we_have_index; assert(have()); } + + bool filtered() const { return piece_priority == filter_priority; } + void filtered(bool f) { piece_priority = f ? filter_priority : 0; } + + int priority(int limit) const + { + if (filtered() || have()) return 0; + // pieces we are currently downloading have high priority + int prio = downloading ? (std::min)(1, int(peer_count)) : peer_count * 2; + // if the peer_count is 0 or 1, the priority cannot be higher + if (prio <= 1) return prio; + if (prio >= limit * 2) prio = limit * 2; + // the different priority levels + switch (piece_priority) + { + case 2: return prio - 1; + case 3: return (std::max)(prio / 2, 1); + case 4: return (std::max)(prio / 2 - 1, 1); + case 5: + case 6: return (std::min)(prio / 2 - 1, 2); + case 7: return 1; + } + return prio; + } + + 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; } + + }; + + BOOST_STATIC_ASSERT(sizeof(piece_pos) == sizeof(char) * 4); + + bool is_ordered(int priority) const + { + return priority >= m_sequenced_download_threshold * 2; + } + + void add(int index); + void move(int vec_index, int elem_index); + + int add_interesting_blocks(const std::vector& piece_list + , const std::vector& pieces + , std::vector& interesting_blocks + , std::vector& backup_blocks + , int num_blocks, bool prefer_whole_pieces + , tcp::endpoint peer, piece_state_t speed) const; + + downloading_piece& add_download_piece(); + void erase_download_piece(std::vector::iterator i); + + // this vector contains all pieces we don't have. + // in the first entry (index 0) is a vector of all pieces + // that no peer have, the vector at index 1 contains + // all pieces that exactly one peer have, index 2 contains + // all pieces exactly two peers have and so on. + // this is not entirely true. The availibility of a piece + // is adjusted depending on its priority. But the principle + // is that the higher index, the lower priority a piece has. + std::vector > m_piece_info; + + // this maps indices to number of peers that has this piece and + // index into the m_piece_info vectors. + // piece_pos::we_have_index means that we have the piece, so it + // doesn't exist in the piece_info buckets + // pieces with the filtered flag set doesn't have entries in + // the m_piece_info buckets either + std::vector m_piece_map; + + // each piece that's currently being downloaded + // has an entry in this list with block allocations. + // i.e. it says wich parts of the piece that + // is being downloaded + std::vector m_downloads; + + // this holds the information of the + // blocks in partially downloaded pieces. + // the first m_blocks_per_piece entries + // in the vector belongs to the first + // entry in m_downloads, the second + // m_blocks_per_piece entries to the + // second entry in m_downloads and so on. + std::vector m_block_info; + + int m_blocks_per_piece; + int m_blocks_in_last_piece; + + // the number of filtered pieces that we don't already + // have. total_number_of_pieces - number_of_pieces_we_have + // - num_filtered is supposed to the number of pieces + // we still want to download + int m_num_filtered; + + // the number of pieces we have that also are filtered + int m_num_have_filtered; + + // the number of pieces we have + int m_num_have; + + // the required popularity of a piece in order to download + // it in sequence instead of random order. + int m_sequenced_download_threshold; +#ifndef NDEBUG + bool m_files_checked_called; +#endif + }; + + inline int piece_picker::blocks_in_piece(int index) const + { + assert(index >= 0); + assert(index < (int)m_piece_map.size()); + if (index+1 == (int)m_piece_map.size()) + return m_blocks_in_last_piece; + else + return m_blocks_per_piece; + } + +} + +#endif // TORRENT_PIECE_PICKER_HPP_INCLUDED + diff --git a/encryption/libtorrent/include/libtorrent/policy.hpp b/encryption/libtorrent/include/libtorrent/policy.hpp new file mode 100755 index 000000000..9404aa095 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/policy.hpp @@ -0,0 +1,263 @@ +/* + +Copyright (c) 2003, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_POLICY_HPP_INCLUDED +#define TORRENT_POLICY_HPP_INCLUDED + +#include +#include + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/peer.hpp" +#include "libtorrent/piece_picker.hpp" +#include "libtorrent/socket.hpp" +#include "libtorrent/size_type.hpp" +#include "libtorrent/invariant_check.hpp" +#include "libtorrent/config.hpp" +#include "libtorrent/time.hpp" + +namespace libtorrent +{ + + class torrent; + class peer_connection; + + enum + { + // the limits of the download queue size + min_request_queue = 2, + + // the amount of free upload allowed before + // the peer is choked + free_upload_amount = 4 * 16 * 1024 + }; + + void request_a_block( + torrent& t + , peer_connection& c + , std::vector ignore = std::vector()); + + class TORRENT_EXPORT policy + { + public: + + policy(torrent* t); + + // this is called every 10 seconds to allow + // for peer choking management + void pulse(); + + // this is called once for every peer we get from + // the tracker, pex, lsd or dht. + void peer_from_tracker(const tcp::endpoint& remote, const peer_id& pid + , int source, char flags); + + // called when an incoming connection is accepted + void new_connection(peer_connection& c); + + // the given connection was just closed + void connection_closed(const peer_connection& c); + + // the peer has got at least one interesting piece + void peer_is_interesting(peer_connection& c); + + void piece_finished(int index, bool successfully_verified); + + void block_finished(peer_connection& c, piece_block b); + + // the peer choked us + void choked(peer_connection& c); + + // the peer unchoked us + void unchoked(peer_connection& c); + + // the peer is interested in our pieces + void interested(peer_connection& c); + + // the peer is not interested in our pieces + void not_interested(peer_connection& c); + +#ifndef NDEBUG + bool has_connection(const peer_connection* p); + + void check_invariant() const; +#endif + + struct peer + { + enum connection_type { not_connectable,connectable }; + + peer(const tcp::endpoint& ip, connection_type t, int src); + + size_type total_download() const; + size_type total_upload() const; + + // the ip/port pair this peer is or was connected on + // if it was a remote (incoming) connection, type is + // set thereafter. If it was a peer we got from the + // tracker, type is set to local_connection. + tcp::endpoint ip; + connection_type type; + +#ifndef TORRENT_DISABLE_ENCRYPTION + // Hints encryption support of 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; +#endif + // the number of failed connection attempts this peer has + int failcount; + + // the number of times this peer has been + // part of a piece that failed the hash check + int hashfails; + + // this is true if the peer is a seed + bool seed; + + // the time when this peer was optimistically unchoked + // the last time. + libtorrent::ptime last_optimistically_unchoked; + + // the time when the peer connected to us + // or disconnected if it isn't connected right now + libtorrent::ptime connected; + + // for every valid piece we receive where this + // peer was one of the participants, we increase + // this value. For every invalid piece we receive + // where this peer was a participant, we decrease + // this value. If it sinks below a threshold, its + // considered a bad peer and will be banned. + int trust_points; + + // if this is true, the peer has previously participated + // in a piece that failed the piece hash check. This will + // put the peer on parole and only request entire pieces. + // if a piece pass that was partially requested from this + // peer it will leave parole mode and continue download + // pieces as normal peers. + bool on_parole; + + // this is the accumulated amount of + // uploaded and downloaded data to this + // peer. It only accounts for what was + // shared during the last connection to + // this peer. i.e. These are only updated + // when the connection is closed. For the + // total amount of upload and download + // we'll have to add thes figures with the + // statistics from the peer_connection. + size_type prev_amount_upload; + size_type prev_amount_download; + + // is set to true if this peer has been banned + bool banned; + + // a bitmap combining the peer_source flags + // from peer_info. + int source; + + // if the peer is connected now, this + // will refer to a valid peer_connection + peer_connection* connection; + }; + + int num_peers() const + { + return m_peers.size(); + } + + int num_uploads() const + { + return m_num_unchoked; + } + + typedef std::list::iterator iterator; + typedef std::list::const_iterator const_iterator; + iterator begin_peer() { return m_peers.begin(); } + iterator end_peer() { return m_peers.end(); } + + bool connect_one_peer(); + + private: + + bool unchoke_one_peer(); + void choke_one_peer(); + iterator find_choke_candidate(); + iterator find_unchoke_candidate(); + + // the seed prefix means that the + // function is used while seeding. + bool seed_unchoke_one_peer(); + void seed_choke_one_peer(); + iterator find_seed_choke_candidate(); + iterator find_seed_unchoke_candidate(); + + bool disconnect_one_peer(); + iterator find_disconnect_candidate(); + iterator find_connect_candidate(); + + std::list m_peers; + + torrent* m_torrent; + + // the number of unchoked peers + // at any given time + int m_num_unchoked; + + // free download we have got that hasn't + // been distributed yet. + size_type m_available_free_upload; + + // if there is a connection limit, + // we disconnect one peer every minute in hope of + // establishing a connection with a better peer + ptime m_last_optimistic_disconnect; + }; + +} + +#endif // TORRENT_POLICY_HPP_INCLUDED + diff --git a/encryption/libtorrent/include/libtorrent/random_sample.hpp b/encryption/libtorrent/include/libtorrent/random_sample.hpp new file mode 100644 index 000000000..8d85080df --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/random_sample.hpp @@ -0,0 +1,74 @@ +/* + +Copyright (c) 2006, Arvid Norberg & Daniel Wallin +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_RANDOM_SAMPLE_HPP +#define TORRENT_RANDOM_SAMPLE_HPP + +#include +#include + +#include "libtorrent/config.hpp" + +namespace libtorrent +{ + + template + inline void random_sample_n(InIter start, InIter end + , OutIter out, Distance n) + { + Distance t = 0; + Distance m = 0; + Distance N = std::distance(start, end); + + assert(N >= n); + + while (m < n) + { + if ((std::rand() / (RAND_MAX + 1.f)) * (N - t) >= n - m) + { + ++start; + ++t; + } + else + { + *out = *start; + ++out; + ++start; + ++t; + ++m; + } + } + } + +} + +#endif diff --git a/encryption/libtorrent/include/libtorrent/resource_request.hpp b/encryption/libtorrent/include/libtorrent/resource_request.hpp new file mode 100755 index 000000000..1e41b9cbb --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/resource_request.hpp @@ -0,0 +1,99 @@ +/* + +Copyright (c) 2003, Magnus Jonsson, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_RESOURCE_REQUEST_HPP_INCLUDED +#define TORRENT_RESOURCE_REQUEST_HPP_INCLUDED + +#include + +#ifdef min +#undef min +#endif + +#ifdef max +#undef max +#endif + +#include "libtorrent/config.hpp" + +namespace libtorrent +{ + struct TORRENT_EXPORT resource_request + { + resource_request() + : used(0) + , min(0) + , max(0) + , given(0) + , leftovers(0) + {} + + resource_request(int used_, int min_, int max_, int given_) + : used(used_) + , min(min_) + , max(max_) + , given(given_) + , leftovers(0) + {} + + int left() const + { + assert(given <= max); + assert(given >= min); + assert(used >= 0); + return (std::max)(given - used, 0); + } + + void reset() { used = leftovers; leftovers = 0; } + + static const int inf = boost::integer_traits::const_max; + + // right now I'm actively using this amount + int used; + + // given cannot be smaller than min + // and not greater than max. + int min; + int max; + + // Reply: Okay, you're allowed to use this amount (a compromise): + int given; + + // this is the amount of resources that exceeded the + // given limit. When the used field is reset (after resources + // have been distributed), it is reset to this number. + int leftovers; + }; +} + + +#endif diff --git a/encryption/libtorrent/include/libtorrent/session.hpp b/encryption/libtorrent/include/libtorrent/session.hpp new file mode 100755 index 000000000..f0127e22a --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/session.hpp @@ -0,0 +1,271 @@ +/* + +Copyright (c) 2006, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_SESSION_HPP_INCLUDED +#define TORRENT_SESSION_HPP_INCLUDED + +#include +#include +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/torrent_handle.hpp" +#include "libtorrent/entry.hpp" +#include "libtorrent/alert.hpp" +#include "libtorrent/session_status.hpp" +#include "libtorrent/version.hpp" +#include "libtorrent/fingerprint.hpp" + +#include "libtorrent/resource_request.hpp" +#include "libtorrent/storage.hpp" + +#ifdef _MSC_VER +# include +#endif + +namespace libtorrent +{ + struct torrent_plugin; + class torrent; + class ip_filter; + class connection_queue; + + namespace aux + { + // workaround for microsofts + // hardware exceptions that makes + // it hard to debug stuff +#ifdef _MSC_VER + struct eh_initializer + { + eh_initializer() + { + ::_set_se_translator(straight_to_debugger); + } + + static void straight_to_debugger(unsigned int, _EXCEPTION_POINTERS*) + { throw; } + }; +#else + struct eh_initializer {}; +#endif + struct session_impl; + + struct filesystem_init + { + filesystem_init(); + }; + + } + + class TORRENT_EXPORT session_proxy + { + friend class session; + public: + session_proxy() {} + private: + session_proxy(boost::shared_ptr impl) + : m_impl(impl) {} + boost::shared_ptr m_impl; + }; + + class TORRENT_EXPORT session: public boost::noncopyable, aux::eh_initializer + { + public: + + session(fingerprint const& print = fingerprint("LT" + , LIBTORRENT_VERSION_MAJOR, LIBTORRENT_VERSION_MINOR, 0, 0)); + session( + fingerprint const& print + , std::pair listen_port_range + , char const* listen_interface = "0.0.0.0"); + + ~session(); + + // returns a list of all torrents in this session + std::vector get_torrents() const; + + // returns an invalid handle in case the torrent doesn't exist + torrent_handle find_torrent(sha1_hash const& info_hash) const; + + // all torrent_handles must be destructed before the session is destructed! + torrent_handle add_torrent( + torrent_info const& ti + , boost::filesystem::path const& save_path + , entry const& resume_data = entry() + , bool compact_mode = true + , int block_size = 16 * 1024 + , storage_constructor_type sc = default_storage_constructor); + + // TODO: deprecated, this is for backwards compatibility only + torrent_handle add_torrent( + entry const& e + , boost::filesystem::path const& save_path + , entry const& resume_data = entry() + , bool compact_mode = true + , int block_size = 16 * 1024 + , storage_constructor_type sc = default_storage_constructor) + { + return add_torrent(torrent_info(e), save_path, resume_data + , compact_mode, block_size, sc); + } + + torrent_handle add_torrent( + char const* tracker_url + , sha1_hash const& info_hash + , char const* name + , boost::filesystem::path const& save_path + , entry const& resume_data = entry() + , bool compact_mode = true + , int block_size = 16 * 1024 + , storage_constructor_type sc = default_storage_constructor); + + session_proxy abort() { return session_proxy(m_impl); } + + session_status status() const; + +#ifndef TORRENT_DISABLE_DHT + void start_dht(entry const& startup_state = entry()); + void stop_dht(); + void set_dht_settings(dht_settings const& settings); + entry dht_state() const; + void add_dht_node(std::pair const& node); + void add_dht_router(std::pair const& node); +#endif + +#ifndef TORRENT_DISABLE_ENCRYPTION + void set_pe_settings(pe_settings const& settings); +#endif + +#ifndef TORRENT_DISABLE_EXTENSIONS + void add_extension(boost::function(torrent*)> ext); +#endif + + void set_ip_filter(ip_filter const& f); + void set_peer_id(peer_id const& pid); + void set_key(int key); + + bool is_listening() const; + + // if the listen port failed in some way + // you can retry to listen on another port- + // range with this function. If the listener + // succeeded and is currently listening, + // a call to this function will shut down the + // listen port and reopen it using these new + // properties (the given interface and port range). + // As usual, if the interface is left as 0 + // this function will return false on failure. + // If it fails, it will also generate alerts describing + // the error. It will return true on success. + bool listen_on( + std::pair const& port_range + , const char* net_interface = 0); + + // returns the port we ended up listening on + unsigned short listen_port() const; + + // Get the number of uploads. + int num_uploads() const; + + // Get the number of connections. This number also contains the + // number of half open connections. + int num_connections() const; + + void remove_torrent(const torrent_handle& h); + + void set_settings(session_settings const& s); + session_settings const& settings(); + + void set_peer_proxy(proxy_settings const& s); + void set_web_seed_proxy(proxy_settings const& s); + void set_tracker_proxy(proxy_settings const& s); + + proxy_settings const& peer_proxy() const; + proxy_settings const& web_seed_proxy() const; + proxy_settings const& tracker_proxy() const; + +#ifndef TORRENT_DISABLE_DHT + void set_dht_proxy(proxy_settings const& s); + proxy_settings const& dht_proxy() const; +#endif + + int upload_rate_limit() const; + int download_rate_limit() const; + + void set_upload_rate_limit(int bytes_per_second); + void set_download_rate_limit(int bytes_per_second); + void set_max_uploads(int limit); + void set_max_connections(int limit); + void set_max_half_open_connections(int limit); + + std::auto_ptr pop_alert(); + void set_severity_level(alert::severity_t s); + + connection_queue& get_connection_queue(); + + // Resource management used for global limits. + resource_request m_ul_bandwidth_quota; + resource_request m_dl_bandwidth_quota; + resource_request m_uploads_quota; + resource_request m_connections_quota; + + private: + + // just a way to initialize boost.filesystem + // before the session_impl is created + aux::filesystem_init m_dummy; + + // data shared between the main thread + // and the working thread + boost::shared_ptr m_impl; + }; + +} + +#endif // TORRENT_SESSION_HPP_INCLUDED + diff --git a/encryption/libtorrent/include/libtorrent/session_settings.hpp b/encryption/libtorrent/include/libtorrent/session_settings.hpp new file mode 100644 index 000000000..c41fca1b7 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/session_settings.hpp @@ -0,0 +1,302 @@ +/* + +Copyright (c) 2003, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_SESSION_SETTINGS_HPP_INCLUDED +#define TORRENT_SESSION_SETTINGS_HPP_INCLUDED + +#include "libtorrent/version.hpp" +#include "libtorrent/config.hpp" + +namespace libtorrent +{ + + struct TORRENT_EXPORT proxy_settings + { + proxy_settings() : port(0), type(none) {} + + std::string hostname; + int port; + + std::string username; + std::string password; + + enum proxy_type + { + // a plain tcp socket is used, and + // the other settings are ignored. + none, + // the hostname and port settings are + // used to connect to the proxy. No + // username or password is sent. + socks5, + // the hostname and port are used to + // connect to the proxy. the username + // and password are used to authenticate + // with the proxy server. + socks5_pw, + // the http proxy is only available for + // tracker and web seed traffic + // assumes anonymous access to proxy + http, + // http proxy with basic authentication + // uses username and password + http_pw + }; + + proxy_type type; + + }; + + struct TORRENT_EXPORT session_settings + { + session_settings(std::string const& user_agent_ = "libtorrent/" + LIBTORRENT_VERSION) + : user_agent(user_agent_) + , tracker_completion_timeout(60) + , tracker_receive_timeout(20) + , stop_tracker_timeout(10) + , tracker_maximum_response_length(1024*1024) + , piece_timeout(120) + , request_queue_time(3.f) + , max_allowed_in_request_queue(250) + , max_out_request_queue(200) + , whole_pieces_threshold(20) + , peer_timeout(120) + , urlseed_timeout(20) + , urlseed_pipeline_size(5) + , file_pool_size(40) + , allow_multiple_connections_per_ip(false) + , max_failcount(3) + , min_reconnect_time(60) + , peer_connect_timeout(10) + , ignore_limits_on_local_network(true) + , connection_speed(20) + , send_redundant_have(false) + , lazy_bitfields(true) +#ifndef TORRENT_DISABLE_DHT + , use_dht_as_fallback(true) +#endif + {} + + // this is the user agent that will be sent to the tracker + // when doing requests. It is used to identify the client. + // It cannot contain \r or \n + std::string user_agent; + + // the number of seconds to wait until giving up on a + // tracker request if it hasn't finished + int tracker_completion_timeout; + + // the number of seconds where no data is received + // from the tracker until it should be considered + // as timed out + int tracker_receive_timeout; + + // the time to wait when sending a stopped message + // before considering a tracker to have timed out. + // this is usually shorter, to make the client quit + // faster + int stop_tracker_timeout; + + // if the content-length is greater than this value + // the tracker connection will be aborted + int tracker_maximum_response_length; + + // the number of seconds from a request is sent until + // it times out if no piece response is returned. + int piece_timeout; + + // the length of the request queue given in the number + // of seconds it should take for the other end to send + // all the pieces. i.e. the actual number of requests + // depends on the download rate and this number. + float request_queue_time; + + // the number of outstanding block requests a peer is + // allowed to queue up in the client. If a peer sends + // more requests than this (before the first one has + // been sent) the last request will be dropped. + // the higher this is, the faster upload speeds the + // client can get to a single peer. + int max_allowed_in_request_queue; + + // the maximum number of outstanding requests to + // send to a peer. This limit takes precedence over + // request_queue_time. + int max_out_request_queue; + + // if a whole piece can be downloaded in this number + // of seconds, or less, the peer_connection will prefer + // to request whole pieces at a time from this peer. + // The benefit of this is to better utilize disk caches by + // doing localized accesses and also to make it easier + // to identify bad peers if a piece fails the hash check. + int whole_pieces_threshold; + + // the number of seconds to wait for any activity on + // the peer wire before closing the connectiong due + // to time out. + int peer_timeout; + + // same as peer_timeout, but only applies to url-seeds. + // this is usually set lower, because web servers are + // expected to be more reliable. + int urlseed_timeout; + + // controls the pipelining size of url-seeds + int urlseed_pipeline_size; + + // 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; + + // false to not allow multiple connections from the same + // IP address. true will allow it. + bool allow_multiple_connections_per_ip; + + // the number of times we can fail to connect to a peer + // before we stop retrying it. + 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; + + // this is the timeout for a connection attempt. If + // the connect does not succeed within this time, the + // connection is dropped. The time is specified in seconds. + int peer_connect_timeout; + + // if set to true, upload, download and unchoke limits + // are ignored for peers on the local network. + bool ignore_limits_on_local_network; + + // the number of connection attempts that + // are made per second. + 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; + + // 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. + bool lazy_bitfields; + +#ifndef TORRENT_DISABLE_DHT + // while this is true, the dht will note be used unless the + // tracker is online + bool use_dht_as_fallback; +#endif + }; + +#ifndef TORRENT_DISABLE_DHT + struct dht_settings + { + dht_settings() + : max_peers_reply(50) + , search_branching(5) + , service_port(0) + , max_fail_count(20) + {} + + // the maximum number of peers to send in a + // reply to get_peers + int max_peers_reply; + + // the number of simultanous "connections" when + // searching the DHT. + int search_branching; + + // the listen port for the dht. This is a UDP port. + // zero means use the same as the tcp interface + int service_port; + + // the maximum number of times a node can fail + // in a row before it is removed from the table. + int max_fail_count; + }; +#endif + +#ifndef TORRENT_DISABLE_ENCRYPTION + + struct pe_settings + { + pe_settings() + : out_enc_policy(enabled) + , in_enc_policy(enabled) + , allowed_enc_level(both) + , prefer_rc4(false) + {} + + enum enc_policy + { + forced, // disallow non encrypted connections + enabled, // allow encrypted and non encrypted connections + disabled // disallow encrypted connections + }; + + enum enc_level + { + plaintext, // use only plaintext encryption + rc4, // use only rc4 encryption + both // allow both + }; + + enc_policy out_enc_policy; + enc_policy in_enc_policy; + + enc_level 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 + +} + +#endif diff --git a/encryption/libtorrent/include/libtorrent/session_status.hpp b/encryption/libtorrent/include/libtorrent/session_status.hpp new file mode 100644 index 000000000..adbb1b57d --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/session_status.hpp @@ -0,0 +1,69 @@ +/* + +Copyright (c) 2006, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_SESSION_STATUS_HPP_INCLUDED +#define TORRENT_SESSION_STATUS_HPP_INCLUDED + + +namespace libtorrent +{ + + struct TORRENT_EXPORT session_status + { + bool has_incoming_connections; + + float upload_rate; + float download_rate; + + float payload_upload_rate; + float payload_download_rate; + + size_type total_download; + size_type total_upload; + + size_type total_payload_download; + size_type total_payload_upload; + + int num_peers; + +#ifndef TORRENT_DISABLE_DHT + int dht_nodes; + int dht_node_cache; + int dht_torrents; + size_type dht_global_nodes; +#endif + }; + +} + +#endif // TORRENT_SESSION_STATUS_HPP_INCLUDED + diff --git a/encryption/libtorrent/include/libtorrent/size_type.hpp b/encryption/libtorrent/include/libtorrent/size_type.hpp new file mode 100755 index 000000000..6020a5ac3 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/size_type.hpp @@ -0,0 +1,52 @@ +/* + +Copyright (c) 2003, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_SIZE_TYPE_HPP_INCLUDED +#define TORRENT_SIZE_TYPE_HPP_INCLUDED + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +namespace libtorrent +{ + typedef boost::int64_t size_type; +} + + +#endif diff --git a/encryption/libtorrent/include/libtorrent/socket.hpp b/encryption/libtorrent/include/libtorrent/socket.hpp new file mode 100755 index 000000000..c478a92ef --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/socket.hpp @@ -0,0 +1,162 @@ +/* + +Copyright (c) 2003, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_SOCKET_HPP_INCLUDED +#define TORRENT_SOCKET_HPP_INCLUDED + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +// if building as Objective C++, asio's template +// parameters Protocol has to be renamed to avoid +// colliding with keywords + +#ifdef __OBJC__ +#define Protocol Protocol_ +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __OBJC__ +#undef Protocol +#endif + +#include "libtorrent/io.hpp" +#include "libtorrent/time.hpp" + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +namespace libtorrent +{ + +/* + namespace asio = boost::asio; + + using boost::asio::ipv4::tcp; + using boost::asio::ipv4::address; + using boost::asio::stream_socket; + using boost::asio::datagram_socket; + using boost::asio::socket_acceptor; + using boost::asio::io_service; + using boost::asio::ipv4::host_resolver; + using boost::asio::async_write; + using boost::asio::ipv4::host; + using boost::asio::deadline_timer; +*/ +// namespace asio = ::asio; + + using asio::ip::tcp; + using asio::ip::udp; + typedef asio::ip::tcp::socket stream_socket; + typedef asio::ip::address address; + typedef asio::ip::address_v4 address_v4; + typedef asio::ip::address_v6 address_v6; + typedef asio::ip::udp::socket datagram_socket; + typedef asio::ip::tcp::acceptor socket_acceptor; + typedef asio::io_service io_service; + + using asio::async_write; + + typedef asio::basic_deadline_timer deadline_timer; + + namespace detail + { + template + void write_address(address const& a, OutIt& out) + { + if (a.is_v4()) + { + write_uint32(a.to_v4().to_ulong(), out); + } + else if (a.is_v6()) + { + asio::ip::address_v6::bytes_type bytes + = a.to_v6().to_bytes(); + std::copy(bytes.begin(), bytes.end(), out); + } + } + + template + address read_v4_address(InIt& in) + { + unsigned long ip = read_uint32(in); + return asio::ip::address_v4(ip); + } + + template + address read_v6_address(InIt& in) + { + typedef asio::ip::address_v6::bytes_type bytes_t; + bytes_t bytes; + for (bytes_t::iterator i = bytes.begin() + , end(bytes.end()); i != end; ++i) + *i = read_uint8(in); + return asio::ip::address_v6(bytes); + } + + template + 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); + } + + template + Endpoint read_v6_endpoint(InIt& in) + { + address addr = read_v6_address(in); + int port = read_uint16(in); + return Endpoint(addr, port); + } + } +} + +#endif // TORRENT_SOCKET_HPP_INCLUDED + diff --git a/encryption/libtorrent/include/libtorrent/socket_type.hpp b/encryption/libtorrent/include/libtorrent/socket_type.hpp new file mode 100644 index 000000000..f81d12342 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/socket_type.hpp @@ -0,0 +1,46 @@ +/* + +Copyright (c) 2007, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_SOCKET_TYPE +#define TORRENT_SOCKET_TYPE + +#include "libtorrent/socks5_stream.hpp" +#include "libtorrent/http_stream.hpp" +#include "libtorrent/variant_stream.hpp" + +namespace libtorrent +{ + typedef variant_stream socket_type; +} + +#endif + diff --git a/encryption/libtorrent/include/libtorrent/socks5_stream.hpp b/encryption/libtorrent/include/libtorrent/socks5_stream.hpp new file mode 100644 index 000000000..9e8a0d04b --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/socks5_stream.hpp @@ -0,0 +1,195 @@ +#include "libtorrent/io.hpp" +#include "libtorrent/socket.hpp" +#include +#include +#include +#include +#include + + +namespace libtorrent { + +class socks5_stream : boost::noncopyable +{ +public: + + typedef stream_socket::lowest_layer_type lowest_layer_type; + typedef stream_socket::endpoint_type endpoint_type; + typedef stream_socket::protocol_type protocol_type; + + explicit socks5_stream(asio::io_service& io_service) + : m_sock(io_service) + , m_resolver(io_service) + {} + + void set_proxy(std::string hostname, int port) + { + m_hostname = hostname; + m_port = port; + } + + void set_username(std::string const& user + , std::string const& password) + { + m_user = user; + m_password = password; + } + + 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, asio::error_code& ec) + { + return m_sock.read_some(buffers, ec); + } + + 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.io_control(ioc); + } + + template + void io_control(IO_Control_Command& ioc, asio::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); + } + + void bind(endpoint_type const& endpoint) + { + m_sock.bind(endpoint); + } + + template + void bind(endpoint_type const& endpoint, Error_Handler const& error_handler) + { + m_sock.bind(endpoint, error_handler); + } + + void open(protocol_type const& p) + { + m_sock.open(p); + } + + template + void open(protocol_type const& p, Error_Handler const& error_handler) + { + m_sock.open(p, error_handler); + } + + void close() + { + m_remote_endpoint = endpoint_type(); + m_sock.close(); + } + + template + void close(Error_Handler const& error_handler) + { + m_sock.close(error_handler); + } + + endpoint_type remote_endpoint() + { + return m_remote_endpoint; + } + + template + endpoint_type remote_endpoint(Error_Handler const& error_handler) + { + return m_remote_endpoint; + } + + endpoint_type local_endpoint() + { + return m_sock.local_endpoint(); + } + + template + endpoint_type local_endpoint(Error_Handler const& error_handler) + { + return m_sock.local_endpoint(error_handler); + } + + asio::io_service& io_service() + { + return m_sock.io_service(); + } + + lowest_layer_type& lowest_layer() + { + return m_sock.lowest_layer(); + } + + typedef boost::function handler_type; + + 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 SOCKS5 authentication method message + // 4. read SOCKS5 authentication response + // 5. send username+password + // 6. send SOCKS5 CONNECT message + + // 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 + , boost::lexical_cast(m_port)); + m_resolver.async_resolve(q, boost::bind( + &socks5_stream::name_lookup, this, _1, _2, h)); + } + +private: + + void name_lookup(asio::error_code const& e, tcp::resolver::iterator i + , boost::shared_ptr h); + void connected(asio::error_code const& e, boost::shared_ptr h); + void handshake1(asio::error_code const& e, boost::shared_ptr h); + void handshake2(asio::error_code const& e, boost::shared_ptr h); + void handshake3(asio::error_code const& e, boost::shared_ptr h); + void handshake4(asio::error_code const& e, boost::shared_ptr h); + void socks_connect(boost::shared_ptr h); + void connect1(asio::error_code const& e, boost::shared_ptr h); + void connect2(asio::error_code const& e, boost::shared_ptr h); + void connect3(asio::error_code const& e, boost::shared_ptr h); + + stream_socket m_sock; + // the socks5 proxy + std::string m_hostname; + int m_port; + // send and receive buffer + std::vector m_buffer; + // proxy authentication + std::string m_user; + std::string m_password; + + endpoint_type m_remote_endpoint; + + tcp::resolver m_resolver; +}; + +} + diff --git a/encryption/libtorrent/include/libtorrent/stat.hpp b/encryption/libtorrent/include/libtorrent/stat.hpp new file mode 100755 index 000000000..8e92c12a1 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/stat.hpp @@ -0,0 +1,193 @@ +/* + +Copyright (c) 2003, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_STAT_HPP_INCLUDED +#define TORRENT_STAT_HPP_INCLUDED + +#include +#include +#include + +#include "libtorrent/size_type.hpp" +#include "libtorrent/invariant_check.hpp" +#include "libtorrent/config.hpp" + +namespace libtorrent +{ + + class TORRENT_EXPORT stat + { + friend class invariant_access; + enum { history = 10 }; + public: + + stat() + : m_downloaded_payload(0) + , m_uploaded_payload(0) + , m_downloaded_protocol(0) + , m_uploaded_protocol(0) + , m_total_download_payload(0) + , m_total_upload_payload(0) + , m_total_download_protocol(0) + , m_total_upload_protocol(0) + , m_mean_download_rate(0) + , m_mean_upload_rate(0) + , m_mean_download_payload_rate(0) + , m_mean_upload_payload_rate(0) + { + std::fill(m_download_rate_history, m_download_rate_history+history, 0.f); + std::fill(m_upload_rate_history, m_upload_rate_history+history, 0.f); + std::fill(m_download_payload_rate_history, m_download_payload_rate_history+history, 0.f); + std::fill(m_upload_payload_rate_history, m_upload_payload_rate_history+history, 0.f); + } + + void operator+=(const stat& s) + { + INVARIANT_CHECK; + + m_downloaded_payload += s.m_downloaded_payload; + m_total_download_payload += s.m_downloaded_payload; + m_downloaded_protocol += s.m_downloaded_protocol; + m_total_download_protocol += s.m_downloaded_protocol; + + m_uploaded_payload += s.m_uploaded_payload; + m_total_upload_payload += s.m_uploaded_payload; + m_uploaded_protocol += s.m_uploaded_protocol; + m_total_upload_protocol += s.m_uploaded_protocol; + } + + void received_bytes(int bytes_payload, int bytes_protocol) + { + INVARIANT_CHECK; + + assert(bytes_payload >= 0); + assert(bytes_protocol >= 0); + + m_downloaded_payload += bytes_payload; + m_total_download_payload += bytes_payload; + m_downloaded_protocol += bytes_protocol; + m_total_download_protocol += bytes_protocol; + } + + void sent_bytes(int bytes_payload, int bytes_protocol) + { + INVARIANT_CHECK; + + assert(bytes_payload >= 0); + assert(bytes_protocol >= 0); + + m_uploaded_payload += bytes_payload; + m_total_upload_payload += bytes_payload; + m_uploaded_protocol += bytes_protocol; + m_total_upload_protocol += bytes_protocol; + } + + // should be called once every second + void second_tick(float tick_interval); + + float upload_rate() const { return m_mean_upload_rate; } + float download_rate() const { return m_mean_download_rate; } + + float upload_payload_rate() const { return m_mean_upload_payload_rate; } + float download_payload_rate() const { return m_mean_download_payload_rate; } + + size_type total_payload_upload() const { return m_total_upload_payload; } + size_type total_payload_download() const { return m_total_download_payload; } + + size_type total_protocol_upload() const { return m_total_upload_protocol; } + size_type total_protocol_download() const { return m_total_download_protocol; } + + // this is used to offset the statistics when a + // peer_connection is opened and have some previous + // transfers from earlier connections. + void add_stat(size_type downloaded, size_type uploaded) + { + m_total_download_payload += downloaded; + m_total_upload_payload += uploaded; + } + + private: + +#ifndef NDEBUG + void check_invariant() const + { + assert(m_mean_upload_rate >= 0); + assert(m_mean_download_rate >= 0); + assert(m_mean_upload_payload_rate >= 0); + assert(m_mean_download_payload_rate >= 0); + assert(m_total_upload_payload >= 0); + assert(m_total_download_payload >= 0); + assert(m_total_upload_protocol >= 0); + assert(m_total_download_protocol >= 0); + } +#endif + + // history of download/upload speeds a few seconds back + float m_download_rate_history[history]; + float m_upload_rate_history[history]; + + float m_download_payload_rate_history[history]; + float m_upload_payload_rate_history[history]; + + // the accumulators we are adding the downloads/uploads + // to this second. This only counts the actual payload + // and ignores the bytes sent as protocol chatter. + int m_downloaded_payload; + int m_uploaded_payload; + + // the accumulators we are adding the downloads/uploads + // to this second. This only counts the protocol + // chatter and ignores the actual payload + int m_downloaded_protocol; + int m_uploaded_protocol; + + // total download/upload counters + // only counting payload data + size_type m_total_download_payload; + size_type m_total_upload_payload; + + // total download/upload counters + // only counting protocol chatter + size_type m_total_download_protocol; + size_type m_total_upload_protocol; + + // current mean download/upload rates + float m_mean_download_rate; + float m_mean_upload_rate; + + float m_mean_download_payload_rate; + float m_mean_upload_payload_rate; + }; + +} + +#endif // TORRENT_STAT_HPP_INCLUDED diff --git a/encryption/libtorrent/include/libtorrent/storage.hpp b/encryption/libtorrent/include/libtorrent/storage.hpp new file mode 100755 index 000000000..ccb17cb71 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/storage.hpp @@ -0,0 +1,205 @@ +/* + +Copyright (c) 2003, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_STORAGE_HPP_INCLUDE +#define TORRENT_STORAGE_HPP_INCLUDE + +#include +#include + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + + +#include "libtorrent/torrent_info.hpp" +#include "libtorrent/piece_picker.hpp" +#include "libtorrent/config.hpp" + +namespace libtorrent +{ + namespace aux + { + struct piece_checker_data; + } + + class session; + struct file_pool; + +#if defined(_WIN32) && defined(UNICODE) + + TORRENT_EXPORT std::wstring safe_convert(std::string const& s); + +#endif + + TORRENT_EXPORT std::vector > get_filesizes( + torrent_info const& t + , boost::filesystem::path p); + + TORRENT_EXPORT bool match_filesizes( + torrent_info const& t + , boost::filesystem::path p + , std::vector > const& sizes + , bool compact_mode + , std::string* error = 0); + + struct TORRENT_EXPORT file_allocation_failed: std::exception + { + file_allocation_failed(const char* error_msg): m_msg(error_msg) {} + virtual const char* what() const throw() { return m_msg.c_str(); } + virtual ~file_allocation_failed() throw() {} + std::string m_msg; + }; + + struct TORRENT_EXPORT storage_interface + { + // create directories and set file sizes + // if allocate_files is true. + // allocate_files is true if allocation mode + // is set to full and sparse files are supported + virtual void initialize(bool allocate_files) = 0; + + // may throw file_error if storage for slot does not exist + virtual size_type read(char* buf, int slot, int offset, int size) = 0; + + // may throw file_error if storage for slot hasn't been allocated + virtual void write(const char* buf, int slot, int offset, int size) = 0; + + virtual bool move_storage(boost::filesystem::path save_path) = 0; + + // verify storage dependent fast resume entries + virtual bool verify_resume_data(entry& rd, std::string& error) = 0; + + // write storage dependent fast resume entries + virtual void write_resume_data(entry& rd) const = 0; + + // moves (or copies) the content in src_slot to dst_slot + virtual void move_slot(int src_slot, int dst_slot) = 0; + + // swaps the data in slot1 and slot2 + virtual void swap_slots(int slot1, int slot2) = 0; + + // swaps the puts the data in slot1 in slot2, the data in slot2 + // in slot3 and the data in slot3 in slot1 + virtual void swap_slots3(int slot1, int slot2, int slot3) = 0; + + // this will close all open files that are opened for + // writing. This is called when a torrent has finished + // downloading. + virtual void release_files() = 0; + virtual ~storage_interface() {} + }; + + typedef storage_interface* (&storage_constructor_type)( + torrent_info const&, boost::filesystem::path const& + , file_pool&); + + TORRENT_EXPORT storage_interface* default_storage_constructor(torrent_info const& ti + , boost::filesystem::path const& path, file_pool& fp); + + // returns true if the filesystem the path relies on supports + // sparse files or automatic zero filling of files. + TORRENT_EXPORT bool supports_sparse_files(boost::filesystem::path const& p); + + class TORRENT_EXPORT piece_manager : boost::noncopyable + { + public: + + piece_manager( + const torrent_info& info + , const boost::filesystem::path& path + , file_pool& fp + , storage_constructor_type sc); + + ~piece_manager(); + + bool check_fastresume(aux::piece_checker_data& d + , std::vector& pieces, int& num_pieces, bool compact_mode); + std::pair check_files(std::vector& pieces + , int& num_pieces, boost::recursive_mutex& mutex); + + void release_files(); + + void write_resume_data(entry& rd) const; + bool verify_resume_data(entry& rd, std::string& error); + + bool is_allocating() const; + bool allocate_slots(int num_slots, bool abort_on_disk = false); + void mark_failed(int index); + + unsigned long piece_crc( + int slot_index + , int block_size + , piece_picker::block_info const* bi); + int slot_for_piece(int piece_index) const; + + size_type read( + char* buf + , int piece_index + , int offset + , int size); + + void write( + const char* buf + , int piece_index + , int offset + , int size); + + boost::filesystem::path const& save_path() const; + bool move_storage(boost::filesystem::path const&); + + // fills the vector that maps all allocated + // slots to the piece that is stored (or + // partially stored) there. -2 is the index + // of unassigned pieces and -1 is unallocated + void export_piece_map(std::vector& pieces) const; + + bool compact_allocation() const; + + private: + class impl; + std::auto_ptr m_pimpl; + }; + +} + +#endif // TORRENT_STORAGE_HPP_INCLUDED + diff --git a/encryption/libtorrent/include/libtorrent/time.hpp b/encryption/libtorrent/include/libtorrent/time.hpp new file mode 100644 index 000000000..2470522c0 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/time.hpp @@ -0,0 +1,383 @@ +/* + +Copyright (c) 2007, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_TIME_HPP_INCLUDED +#define TORRENT_TIME_HPP_INCLUDED + +#include + +#ifndef _WIN32 +#include +#endif + +namespace libtorrent +{ + inline char const* time_now_string() + { + 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; + } +} + +#if (!defined (__MACH__) && !defined (_WIN32) && !defined(_POSIX_MONOTONIC_CLOCK) || _POSIX_MONOTONIC_CLOCK < 0) || defined (TORRENT_USE_BOOST_DATE_TIME) + +#include + +namespace libtorrent +{ + typedef boost::posix_time::ptime ptime; + typedef boost::posix_time::time_duration time_duration; + inline ptime time_now() + { return boost::posix_time::microsec_clock::universal_time(); } + inline ptime min_time() + { return boost::posix_time::ptime(boost::posix_time::min_date_time); } + inline ptime max_time() + { return boost::posix_time::ptime(boost::posix_time::max_date_time); } + inline time_duration seconds(int s) { return boost::posix_time::seconds(s); } + inline time_duration milliseconds(int s) { return boost::posix_time::milliseconds(s); } + inline time_duration microsec(int s) { return boost::posix_time::microsec(s); } + inline time_duration minutes(int s) { return boost::posix_time::minutes(s); } + inline time_duration hours(int s) { return boost::posix_time::hours(s); } + + inline int total_seconds(time_duration td) + { return td.total_seconds(); } + inline int total_milliseconds(time_duration td) + { return td.total_milliseconds(); } + inline boost::int64_t total_microseconds(time_duration td) + { return td.total_microseconds(); } + +} + +#else + +#include +#include + +namespace libtorrent +{ + // libtorrent time_duration type + struct time_duration + { + time_duration() {} + time_duration operator/(int rhs) const { return time_duration(diff / rhs); } + explicit time_duration(boost::int64_t d) : diff(d) {} + boost::int64_t diff; + }; + + inline bool is_negative(time_duration dt) { return dt.diff < 0; } + inline bool operator<(time_duration lhs, time_duration rhs) + { return lhs.diff < rhs.diff; } + inline bool operator>(time_duration lhs, time_duration rhs) + { return lhs.diff > rhs.diff; } + + // libtorrent time type + struct ptime + { + ptime() {} + explicit ptime(boost::int64_t t): time(t) {} + boost::int64_t time; + }; + + inline bool operator>(ptime lhs, ptime rhs) + { return lhs.time > rhs.time; } + inline bool operator>=(ptime lhs, ptime rhs) + { return lhs.time >= rhs.time; } + inline bool operator<=(ptime lhs, ptime rhs) + { return lhs.time <= rhs.time; } + inline bool operator<(ptime lhs, ptime rhs) + { return lhs.time < rhs.time; } + inline bool operator!=(ptime lhs, ptime rhs) + { return lhs.time != rhs.time;} + inline bool operator==(ptime lhs, ptime rhs) + { return lhs.time == rhs.time;} + inline time_duration operator-(ptime lhs, ptime rhs) + { return time_duration(lhs.time - rhs.time); } + inline ptime operator+(ptime lhs, time_duration rhs) + { return ptime(lhs.time + rhs.diff); } + inline ptime operator+(time_duration lhs, ptime rhs) + { return ptime(rhs.time + lhs.diff); } + inline ptime operator-(ptime lhs, time_duration rhs) + { return ptime(lhs.time - rhs.diff); } + + ptime time_now(); + inline ptime min_time() { return ptime(0); } + inline ptime max_time() { return ptime((std::numeric_limits::max)()); } + int total_seconds(time_duration td); + int total_milliseconds(time_duration td); + boost::int64_t total_microseconds(time_duration td); +} + +// asio time_traits +namespace asio +{ + template<> + struct time_traits + { + typedef libtorrent::ptime time_type; + typedef libtorrent::time_duration duration_type; + static time_type now() + { return time_type(libtorrent::time_now()); } + static time_type add(time_type t, duration_type d) + { return time_type(t.time + d.diff);} + static duration_type subtract(time_type t1, time_type t2) + { return duration_type(t1 - t2); } + static bool less_than(time_type t1, time_type t2) + { return t1 < t2; } + static boost::posix_time::time_duration to_posix_duration( + duration_type d) + { return boost::posix_time::microseconds(libtorrent::total_microseconds(d)); } + }; +} + +#if defined(__MACH__) + +#include +#include + +// high precision timer for darwin intel and ppc + +namespace libtorrent +{ + namespace aux + { + inline boost::int64_t absolutetime_to_microseconds(boost::int64_t at) + { + static mach_timebase_info_data_t timebase_info = {0,0}; + if (timebase_info.denom == 0) + mach_timebase_info(&timebase_info); + // make sure we don't overflow + assert((at >= 0 && at >= at / 1000 * timebase_info.numer / timebase_info.denom) + || (at < 0 && at < at / 1000 * timebase_info.numer / timebase_info.denom)); + return at / 1000 * timebase_info.numer / timebase_info.denom; + } + + inline boost::int64_t microseconds_to_absolutetime(boost::int64_t ms) + { + static mach_timebase_info_data_t timebase_info = {0,0}; + if (timebase_info.denom == 0) + { + mach_timebase_info(&timebase_info); + assert(timebase_info.numer > 0); + assert(timebase_info.denom > 0); + } + // make sure we don't overflow + assert((ms >= 0 && ms <= ms * timebase_info.denom / timebase_info.numer * 1000) + || (ms < 0 && ms > ms * timebase_info.denom / timebase_info.numer * 1000)); + return ms * timebase_info.denom / timebase_info.numer * 1000; + } + } + + inline int total_seconds(time_duration td) + { + return aux::absolutetime_to_microseconds(td.diff) + / 1000000; + } + inline int total_milliseconds(time_duration td) + { + return aux::absolutetime_to_microseconds(td.diff) + / 1000; + } + inline boost::int64_t total_microseconds(time_duration td) + { + return aux::absolutetime_to_microseconds(td.diff); + } + + inline ptime time_now() { return ptime(mach_absolute_time()); } + + inline time_duration microsec(boost::int64_t s) + { + return time_duration(aux::microseconds_to_absolutetime(s)); + } + inline time_duration milliseconds(boost::int64_t s) + { + return time_duration(aux::microseconds_to_absolutetime(s * 1000)); + } + inline time_duration seconds(boost::int64_t s) + { + return time_duration(aux::microseconds_to_absolutetime(s * 1000000)); + } + inline time_duration minutes(boost::int64_t s) + { + return time_duration(aux::microseconds_to_absolutetime(s * 1000000 * 60)); + } + inline time_duration hours(boost::int64_t s) + { + return time_duration(aux::microseconds_to_absolutetime(s * 1000000 * 60 * 60)); + } + +} +#elif defined(_WIN32) + +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include + +namespace libtorrent +{ + namespace aux + { + inline boost::int64_t performance_counter_to_microseconds(boost::int64_t pc) + { + static LARGE_INTEGER performace_counter_frequency = {0,0}; + if (performace_counter_frequency.QuadPart == 0) + QueryPerformanceFrequency(&performace_counter_frequency); + +#ifndef NDEBUG + // make sure we don't overflow + boost::int64_t ret = (pc * 1000 / performace_counter_frequency.QuadPart) * 1000; + assert((pc >= 0 && pc >= ret) || (pc < 0 && pc < ret)); +#endif + return (pc * 1000 / performace_counter_frequency.QuadPart) * 1000; + } + + inline boost::int64_t microseconds_to_performance_counter(boost::int64_t ms) + { + static LARGE_INTEGER performace_counter_frequency = {0,0}; + if (performace_counter_frequency.QuadPart == 0) + QueryPerformanceFrequency(&performace_counter_frequency); +#ifndef NDEBUG + // make sure we don't overflow + boost::int64_t ret = (ms / 1000) * performace_counter_frequency.QuadPart / 1000; + assert((ms >= 0 && ms <= ret) + || (ms < 0 && ms > ret)); +#endif + return (ms / 1000) * performace_counter_frequency.QuadPart / 1000; + } + } + + inline int total_seconds(time_duration td) + { + return aux::performance_counter_to_microseconds(td.diff) + / 1000000; + } + inline int total_milliseconds(time_duration td) + { + return aux::performance_counter_to_microseconds(td.diff) + / 1000; + } + inline boost::int64_t total_microseconds(time_duration td) + { + return aux::performance_counter_to_microseconds(td.diff); + } + + inline ptime time_now() + { + LARGE_INTEGER now; + QueryPerformanceCounter(&now); + return ptime(now.QuadPart); + } + + inline time_duration microsec(boost::int64_t s) + { + return time_duration(aux::microseconds_to_performance_counter(s)); + } + inline time_duration milliseconds(boost::int64_t s) + { + return time_duration(aux::microseconds_to_performance_counter( + s * 1000)); + } + inline time_duration seconds(boost::int64_t s) + { + return time_duration(aux::microseconds_to_performance_counter( + s * 1000000)); + } + inline time_duration minutes(boost::int64_t s) + { + return time_duration(aux::microseconds_to_performance_counter( + s * 1000000 * 60)); + } + inline time_duration hours(boost::int64_t s) + { + return time_duration(aux::microseconds_to_performance_counter( + s * 1000000 * 60 * 60)); + } + +} + +#elif defined(_POSIX_MONOTONIC_CLOCK) && _POSIX_MONOTONIC_CLOCK >= 0 + +#include + +namespace libtorrent +{ + inline int total_seconds(time_duration td) + { + return td.diff / 1000000; + } + inline int total_milliseconds(time_duration td) + { + return td.diff / 1000; + } + inline boost::int64_t total_microseconds(time_duration td) + { + return td.diff; + } + + inline ptime time_now() + { + timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + return ptime(boost::int64_t(ts.tv_sec) * 1000000 + ts.tv_nsec / 1000); + } + + inline time_duration microsec(boost::int64_t s) + { + return time_duration(s); + } + inline time_duration milliseconds(boost::int64_t s) + { + return time_duration(s * 1000); + } + inline time_duration seconds(boost::int64_t s) + { + return time_duration(s * 1000000); + } + inline time_duration minutes(boost::int64_t s) + { + return time_duration(s * 1000000 * 60); + } + inline time_duration hours(boost::int64_t s) + { + return time_duration(s * 1000000 * 60 * 60); + } + +} + +#endif + +#endif +#endif + diff --git a/encryption/libtorrent/include/libtorrent/torrent.hpp b/encryption/libtorrent/include/libtorrent/torrent.hpp new file mode 100755 index 000000000..8943b0db9 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/torrent.hpp @@ -0,0 +1,761 @@ +/* + +Copyright (c) 2003, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_TORRENT_HPP_INCLUDE +#define TORRENT_TORRENT_HPP_INCLUDE + +#include +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include +#include +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/torrent_handle.hpp" +#include "libtorrent/entry.hpp" +#include "libtorrent/torrent_info.hpp" +#include "libtorrent/socket.hpp" +#include "libtorrent/policy.hpp" +#include "libtorrent/tracker_manager.hpp" +#include "libtorrent/stat.hpp" +#include "libtorrent/alert.hpp" +#include "libtorrent/resource_request.hpp" +#include "libtorrent/piece_picker.hpp" +#include "libtorrent/config.hpp" +#include "libtorrent/escape_string.hpp" +#include "libtorrent/bandwidth_manager.hpp" +#include "libtorrent/storage.hpp" + +namespace libtorrent +{ +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + struct logger; +#endif + + class piece_manager; + struct torrent_plugin; + + namespace aux + { + struct session_impl; + struct piece_checker_data; + } + + // a torrent is a class that holds information + // for a specific download. It updates itself against + // the tracker + class TORRENT_EXPORT torrent: public request_callback + , public boost::enable_shared_from_this + { + public: + + torrent( + aux::session_impl& ses + , aux::checker_impl& checker + , torrent_info const& tf + , boost::filesystem::path const& save_path + , tcp::endpoint const& net_interface + , bool compact_mode + , int block_size + , session_settings const& s + , storage_constructor_type sc); + + // used with metadata-less torrents + // (the metadata is downloaded from the peers) + torrent( + aux::session_impl& ses + , aux::checker_impl& checker + , char const* tracker_url + , sha1_hash const& info_hash + , char const* name + , boost::filesystem::path const& save_path + , tcp::endpoint const& net_interface + , bool compact_mode + , int block_size + , session_settings const& s + , storage_constructor_type sc); + + ~torrent(); + + // starts the announce timer + void start(); + +#ifndef TORRENT_DISABLE_EXTENSIONS + void add_extension(boost::shared_ptr); +#endif + + // this is called when the torrent has metadata. + // it will initialize the storage and the piece-picker + void init(); + + // this will flag the torrent as aborted. The main + // loop in session_impl will check for this state + // on all torrents once every second, and take + // the necessary actions then. + void abort(); + bool is_aborted() const { return m_abort; } + + // returns true if this torrent is being allocated + // by the checker thread. + bool is_allocating() const; + + session_settings const& settings() const; + + aux::session_impl& session() { return m_ses; } + + void set_sequenced_download_threshold(int threshold); + + bool verify_resume_data(entry& rd, std::string& error) + { assert(m_storage); return m_storage->verify_resume_data(rd, error); } + + // is called every second by session. This will + // caclulate the upload/download and number + // of connections this torrent needs. And prepare + // it for being used by allocate_resources. + void second_tick(stat& accumulator, float tick_interval); + + // debug purpose only + void print(std::ostream& os) const; + + std::string name() const; + + bool check_fastresume(aux::piece_checker_data&); + std::pair check_files(); + void files_checked(std::vector const& + unfinished_pieces); + + stat statistics() const { return m_stat; } + size_type bytes_left() const; + boost::tuples::tuple bytes_done() const; + size_type quantized_bytes_done() const; + + void pause(); + void resume(); + bool is_paused() const { return m_paused; } + + // ============ 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); + // ============ end deprecation ============= + + void set_piece_priority(int index, int priority); + int piece_priority(int index) const; + + void prioritize_pieces(std::vector const& pieces); + void piece_priorities(std::vector&) const; + + void prioritize_files(std::vector const& files); + + torrent_status status() const; + void file_progress(std::vector& fp) const; + + void use_interface(const char* net_interface); + tcp::endpoint const& get_interface() const { return m_net_interface; } + + void connect_to_url_seed(std::string const& url); + peer_connection* connect_to_peer(policy::peer* peerinfo); + + void set_ratio(float ratio) + { assert(ratio >= 0.0f); m_ratio = ratio; } + + float ratio() const + { return m_ratio; } + +#ifndef TORRENT_DISABLE_RESOLVE_COUNTRIES + void resolve_countries(bool r) + { m_resolve_countries = r; } + + bool resolving_countries() const { return m_resolve_countries; } +#endif + +// -------------------------------------------- + // BANDWIDTH MANAGEMENT + + bandwidth_limit m_bandwidth_limit[2]; + + void request_bandwidth(int channel + , boost::intrusive_ptr p + , bool non_prioritized); + + void expire_bandwidth(int channel, int amount); + void assign_bandwidth(int channel, int amount); + + int bandwidth_throttle(int channel) const; + +// -------------------------------------------- + // PEER MANAGEMENT + + // add or remove a url that will be attempted for + // finding the file(s) in this torrent. + void add_url_seed(std::string const& url) + { m_web_seeds.insert(url); } + + void remove_url_seed(std::string const& url) + { m_web_seeds.erase(url); } + + // used by peer_connection to attach itself to a torrent + // since incoming connections don't know what torrent + // they're a part of until they have received an info_hash. + void attach_peer(peer_connection* p); + + // this will remove the peer and make sure all + // the pieces it had have their reference counter + // decreased in the piece_picker + void remove_peer(peer_connection* p); + + bool want_more_peers() const; + bool try_connect_peer(); + + peer_connection* connection_for(tcp::endpoint const& a) + { + peer_iterator i = m_connections.find(a); + if (i == m_connections.end()) return 0; + return i->second; + } + + // the number of peers that belong to this torrent + int num_peers() const { return (int)m_connections.size(); } + int num_seeds() const; + + typedef std::map::iterator peer_iterator; + typedef std::map::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 resolve_peer_country(boost::intrusive_ptr const& p) const; + +// -------------------------------------------- + // TRACKER MANAGEMENT + + // these are callbacks called by the tracker_connection instance + // (either http_tracker_connection or udp_tracker_connection) + // when this torrent got a response from its tracker request + // or when a failure occured + virtual void tracker_response( + tracker_request const& r + , std::vector& e, int interval + , int complete, int incomplete); + virtual void tracker_request_timed_out( + tracker_request const& r); + virtual void tracker_request_error(tracker_request const& r + , int response_code, const std::string& str); + virtual void tracker_warning(std::string const& msg); + + // generates a request string for sending + // to the tracker + tracker_request generate_tracker_request(); + + // if no password and username is set + // this will return an empty string, otherwise + // it will concatenate the login and password + // ready to be sent over http (but without + // base64 encoding). + std::string tracker_login() const; + + // returns the absolute time when the next tracker + // announce will take place. + ptime next_announce() const; + + // returns true if it is time for this torrent to make another + // tracker request + bool should_request(); + + // forcefully sets next_announce to the current time + void force_tracker_request(); + void force_tracker_request(ptime); + + // sets the username and password that will be sent to + // the tracker + void set_tracker_login(std::string const& name, std::string const& pw); + + // the tcp::endpoint of the tracker that we managed to + // announce ourself at the last time we tried to announce + const tcp::endpoint& current_tracker() const; + +// -------------------------------------------- + // PIECE MANAGEMENT + + // returns true if we have downloaded the given piece + bool have_piece(int index) const + { + assert(index >= 0 && index < (signed)m_have_pieces.size()); + return m_have_pieces[index]; + } + + const std::vector& pieces() const + { return m_have_pieces; } + + int num_pieces() const { return m_num_pieces; } + + // when we get a have- or bitfield- messages, this is called for every + // piece a peer has gained. + void peer_has(int index) + { + if (m_picker.get()) + { + assert(!is_seed()); + assert(index >= 0 && index < (signed)m_have_pieces.size()); + m_picker->inc_refcount(index); + } +#ifndef NDEBUG + else + { + assert(is_seed()); + } +#endif + } + + void peer_has_all() + { + if (m_picker.get()) + { + assert(!is_seed()); + m_picker->inc_refcount_all(); + } +#ifndef NDEBUG + else + { + assert(is_seed()); + } +#endif + } + + // when peer disconnects, this is called for every piece it had + void peer_lost(int index) + { + if (m_picker.get()) + { + assert(!is_seed()); + assert(index >= 0 && index < (signed)m_have_pieces.size()); + m_picker->dec_refcount(index); + } +#ifndef NDEBUG + else + { + assert(is_seed()); + } +#endif + } + + int block_size() const { assert(m_block_size > 0); return m_block_size; } + + // this will tell all peers that we just got his piece + // and also let the piece picker know that we have this piece + // so it wont pick it for download + void announce_piece(int index); + + void disconnect_all(); + + // this is called wheh the torrent has completed + // the download. It will post an event, disconnect + // all seeds and let the tracker know we're finished. + void completed(); + + // this is the asio callback that is called when a name + // lookup for a PEER is completed. + void on_peer_name_lookup(asio::error_code const& e, tcp::resolver::iterator i + , peer_id pid); + + // this is the asio callback that is called when a name + // lookup for a WEB SEED is completed. + void on_name_lookup(asio::error_code const& e, tcp::resolver::iterator i + , std::string url, tcp::endpoint proxy); + + // 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(asio::error_code const& e, tcp::resolver::iterator i + , std::string url); + + // this is called when the torrent has finished. i.e. + // all the pieces we have not filtered have been downloaded. + // If no pieces are filtered, this is called first and then + // completed() is called immediately after it. + void finished(); + + bool verify_piece(int piece_index); + + // this is called from the peer_connection + // each time a piece has failed the hash + // test + void piece_failed(int index); + void received_redundant_data(int num_bytes) + { assert(num_bytes > 0); m_total_redundant_bytes += num_bytes; } + + // this is true if we have all the pieces + bool is_seed() const + { + return valid_metadata() + && m_num_pieces == m_torrent_file.num_pieces(); + } + + // this is true if we have all the pieces that we want + bool is_finished() const + { + if (is_seed()) return true; + return valid_metadata() && m_torrent_file.num_pieces() + - m_num_pieces - m_picker->num_filtered() == 0; + } + + boost::filesystem::path save_path() const; + alert_manager& alerts() const; + piece_picker& picker() + { + assert(!is_seed()); + assert(m_picker.get()); + return *m_picker; + } + bool has_picker() const + { + assert((valid_metadata() && !is_seed()) == bool(m_picker.get() != 0)); + return m_picker.get() != 0; + } + policy& get_policy() + { + assert(m_policy); + return *m_policy; + } + piece_manager& filesystem(); + torrent_info const& torrent_file() const + { return m_torrent_file; } + + std::vector const& trackers() const + { return m_trackers; } + + void replace_trackers(std::vector const& urls); + + torrent_handle get_handle() const; + + // LOGGING +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + virtual void debug_log(const std::string& line); +#endif + + // DEBUG +#ifndef NDEBUG + void check_invariant() const; +#endif + +// -------------------------------------------- + // RESOURCE MANAGEMENT + + void distribute_resources(float tick_interval); + + resource_request m_uploads_quota; + resource_request m_connections_quota; + + void set_peer_upload_limit(tcp::endpoint ip, int limit); + void set_peer_download_limit(tcp::endpoint ip, int limit); + + void set_upload_limit(int limit); + int upload_limit() const; + void set_download_limit(int limit); + int download_limit() const; + + void set_max_uploads(int limit); + void set_max_connections(int limit); + bool move_storage(boost::filesystem::path const& save_path); + + // unless this returns true, new connections must wait + // with their initialization. + bool ready_for_connections() const + { return m_connections_initialized; } + bool valid_metadata() const + { return m_storage.get() != 0; } + + // parses the info section from the given + // bencoded tree and moves the torrent + // to the checker thread for initial checking + // of the storage. + void set_metadata(entry const&); + + private: + + void try_next_tracker(); + int prioritize_tracker(int tracker_index); + void on_country_lookup(asio::error_code const& error, tcp::resolver::iterator i + , boost::intrusive_ptr p) const; + bool request_bandwidth_from_session(int channel) const; + + void update_peer_interest(); + + torrent_info m_torrent_file; + + // is set to true when the torrent has + // been aborted. + bool m_abort; + + // is true if this torrent has been paused + bool m_paused; + // this is true from the time when the torrent was + // paused to the time should_request() is called + bool m_just_paused; + + tracker_request::event_t m_event; + + void parse_response(const entry& e, std::vector& peer_list); + + // the size of a request block + // each piece is divided into these + // blocks when requested + int m_block_size; + + // if this pointer is 0, the torrent is in + // a state where the metadata hasn't been + // received yet. + boost::scoped_ptr m_storage; + + // the time of next tracker request + ptime m_next_request; + + // ----------------------------- + // DATA FROM TRACKER RESPONSE + + // the number number of seconds between requests + // from the tracker + int m_duration; + + // the scrape data from the tracker response, this + // is optional and may be -1. + int m_complete; + int m_incomplete; + +#ifndef NDEBUG + public: +#endif + std::map m_connections; +#ifndef NDEBUG + private: +#endif + + // The list of web seeds in this torrent. Seeds + // with fatal errors are removed from the set + std::set m_web_seeds; + + // urls of the web seeds that we are currently + // resolving the address for + std::set m_resolving_web_seeds; + + // used to resolve the names of web seeds + mutable tcp::resolver m_host_resolver; + +#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; + + // this is true if the user has enabled + // country resolution in this torrent + bool m_resolve_countries; +#endif + + // this announce timer is used both + // by Local service discovery and + // by the DHT. + deadline_timer m_announce_timer; + + static void on_announce_disp(boost::weak_ptr p + , asio::error_code const& e); + + // this is called once per announce interval + void on_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; + + // the time when the DHT was last announced of our + // presence on this torrent + ptime m_last_dht_announce; +#endif + + // this is the upload and download statistics for the whole torrent. + // it's updated from all its peers once every second. + libtorrent::stat m_stat; + + // ----------------------------- + + boost::shared_ptr m_policy; + + // a back reference to the session + // this torrent belongs to. + aux::session_impl& m_ses; + aux::checker_impl& m_checker; + + boost::scoped_ptr m_picker; + + // the queue of peer_connections that want more bandwidth + std::deque m_bandwidth_queue[2]; + + std::vector m_trackers; + // this is an index into m_torrent_file.trackers() + int m_last_working_tracker; + int m_currently_trying_tracker; + // the number of connection attempts that has + // failed in a row, this is currently used to + // determine the timeout until next try. + int m_failed_trackers; + + // this is a counter that is increased every + // second, and when it reaches 10, the policy::pulse() + // is called and the time scaler is reset to 0. + int m_time_scaler; + + // the bitmask that says which pieces we have + std::vector m_have_pieces; + + // the number of pieces we have. The same as + // std::accumulate(m_have_pieces.begin(), + // m_have_pieces.end(), 0) + int m_num_pieces; + + // 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 + int m_sequenced_download_threshold; + + // is false by default and set to + // true when the first tracker reponse + // is received + bool m_got_tracker_response; + + // the upload/download ratio that each peer + // tries to maintain. + // 0 is infinite + float m_ratio; + + // the number of bytes that has been + // downloaded that failed the hash-test + size_type m_total_failed_bytes; + size_type m_total_redundant_bytes; + + std::string m_username; + std::string m_password; + + // the network interface all outgoing connections + // are opened through + tcp::endpoint m_net_interface; + + boost::filesystem::path m_save_path; + + // determines the storage state for this torrent. + const bool m_compact_mode; + + // defaults to 16 kiB, but can be set by the user + // when creating the torrent + const int m_default_block_size; + + // this is set to false as long as the connections + // of this torrent hasn't been initialized. If we + // have metadata from the start, connections are + // initialized immediately, if we didn't have metadata, + // they are initialized right after files_checked(). + // valid_resume_data() will return false as long as + // the connections aren't initialized, to avoid + // them from altering the piece-picker before it + // has been initialized with files_checked(). + bool m_connections_initialized; + + // 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; + + session_settings const& m_settings; + + storage_constructor_type m_storage_constructor; + +#ifndef TORRENT_DISABLE_EXTENSIONS + typedef std::list > extension_list_t; + extension_list_t m_extensions; +#endif + +#ifndef NDEBUG + // this is the amount downloaded when this torrent + // is started. i.e. + // total_done - m_initial_done <= total_payload_download + size_type m_initial_done; +#endif + }; + + inline ptime torrent::next_announce() const + { + return m_next_request; + } + + inline void torrent::force_tracker_request() + { + m_next_request = time_now(); + } + + inline void torrent::force_tracker_request(ptime t) + { + m_next_request = t; + } + + inline void torrent::set_tracker_login( + std::string const& name + , std::string const& pw) + { + m_username = name; + m_password = pw; + } + +} + +#endif // TORRENT_TORRENT_HPP_INCLUDED + diff --git a/encryption/libtorrent/include/libtorrent/torrent_handle.hpp b/encryption/libtorrent/include/libtorrent/torrent_handle.hpp new file mode 100755 index 000000000..cb4b892d2 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/torrent_handle.hpp @@ -0,0 +1,376 @@ +/* + +Copyright (c) 2003, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_TORRENT_HANDLE_HPP_INCLUDED +#define TORRENT_TORRENT_HANDLE_HPP_INCLUDED + +#include + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/peer_id.hpp" +#include "libtorrent/peer_info.hpp" +#include "libtorrent/piece_picker.hpp" +#include "libtorrent/torrent_info.hpp" +#include "libtorrent/time.hpp" +#include "libtorrent/config.hpp" + +namespace libtorrent +{ + namespace aux + { + struct session_impl; + struct checker_impl; + } + + struct TORRENT_EXPORT duplicate_torrent: std::exception + { + virtual const char* what() const throw() + { return "torrent already exists in session"; } + }; + + struct TORRENT_EXPORT invalid_handle: std::exception + { + virtual const char* what() const throw() + { return "invalid torrent handle used"; } + }; + + struct TORRENT_EXPORT torrent_status + { + torrent_status() + : state(queued_for_checking) + , paused(false) + , progress(0.f) + , total_download(0) + , total_upload(0) + , total_payload_download(0) + , total_payload_upload(0) + , total_failed_bytes(0) + , total_redundant_bytes(0) + , download_rate(0) + , upload_rate(0) + , download_payload_rate(0) + , upload_payload_rate(0) + , num_peers(0) + , num_complete(-1) + , num_incomplete(-1) + , pieces(0) + , num_pieces(0) + , total_done(0) + , total_wanted_done(0) + , total_wanted(0) + , num_seeds(0) + , distributed_copies(0.f) + , block_size(0) + {} + + enum state_t + { + queued_for_checking, + checking_files, + connecting_to_tracker, + downloading_metadata, + downloading, + finished, + seeding, + allocating + }; + + state_t state; + bool paused; + float progress; + boost::posix_time::time_duration next_announce; + boost::posix_time::time_duration announce_interval; + + std::string current_tracker; + + // transferred this session! + // total, payload plus protocol + size_type total_download; + size_type total_upload; + + // payload only + size_type total_payload_download; + size_type total_payload_upload; + + // the amount of payload bytes that + // has failed their hash test + size_type total_failed_bytes; + + // the number of payload bytes that + // has been received redundantly. + size_type total_redundant_bytes; + + // current transfer rate + // payload plus protocol + float download_rate; + float upload_rate; + + // the rate of payload that is + // sent and received + float download_payload_rate; + float upload_payload_rate; + + // the number of peers this torrent + // is connected to. + int num_peers; + + // if the tracker sends scrape info in its + // announce reply, these fields will be + // set to the total number of peers that + // have the whole file and the total number + // of peers that are still downloading + int num_complete; + int num_incomplete; + + const std::vector* pieces; + + // this is the number of pieces the client has + // downloaded. it is equal to: + // std::accumulate(pieces->begin(), pieces->end()); + int num_pieces; + + // the number of bytes of the file we have + // including pieces that may have been filtered + // after we downloaded them + size_type total_done; + + // the number of bytes we have of those that we + // want. i.e. not counting bytes from pieces that + // are filtered as not wanted. + size_type total_wanted_done; + + // the total number of bytes we want to download + // this may be smaller than the total torrent size + // in case any pieces are filtered as not wanted + size_type total_wanted; + + // the number of peers this torrent is connected to + // that are seeding. + int num_seeds; + + // the number of distributed copies of the file. + // note that one copy may be spread out among many peers. + // + // the 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 block size that is used in this torrent. i.e. + // the number of bytes each piece request asks for + // and each bit in the download queue bitfield represents + int block_size; + }; + + struct TORRENT_EXPORT partial_piece_info + { + enum { max_blocks_per_piece = 256 }; + int piece_index; + int blocks_in_piece; + std::bitset requested_blocks; + std::bitset finished_blocks; + tcp::endpoint peer[max_blocks_per_piece]; + int num_downloads[max_blocks_per_piece]; + enum state_t { none, slow, medium, fast }; + state_t piece_state; + }; + + struct TORRENT_EXPORT torrent_handle + { + friend class invariant_access; + friend struct aux::session_impl; + friend class torrent; + + torrent_handle(): m_ses(0), m_chk(0), m_info_hash(0) {} + + void get_peer_info(std::vector& v) const; + bool send_chat_message(tcp::endpoint ip, std::string message) const; + torrent_status status() const; + void get_download_queue(std::vector& queue) const; + + // fills the specified vector with the download progress [0, 1] + // of each file in the torrent. The files are ordered as in + // the torrent_info. + void file_progress(std::vector& progress); + + std::vector const& trackers() const; + void replace_trackers(std::vector const&) const; + + void add_url_seed(std::string const& url); + + bool has_metadata() const; + const torrent_info& get_torrent_info() const; + bool is_valid() const; + + bool is_seed() const; + bool is_paused() const; + void pause() const; + void resume() const; + +#ifndef TORRENT_DISABLE_RESOLVE_COUNTRIES + void resolve_countries(bool r); + bool resolve_countries() const; +#endif + + // all these are deprecated, use piece + // priority functions instead + + // ================ start deprecation ============ + + // marks the piece with the given index as filtered + // it will not be downloaded + void filter_piece(int index, bool filter) const; + void filter_pieces(std::vector const& pieces) const; + bool is_piece_filtered(int index) const; + std::vector filtered_pieces() const; + // marks the file with the given index as filtered + // it will not be downloaded + void filter_files(std::vector const& files) const; + + // ================ end deprecation ============ + + // priority must be within the range [0, 7] + void piece_priority(int index, int priority) const; + int piece_priority(int index) const; + + void prioritize_pieces(std::vector const& pieces) const; + std::vector piece_priorities() const; + + void prioritize_files(std::vector const& files) const; + + + // set the interface to bind outgoing connections + // to. + void use_interface(const char* net_interface) const; + + entry write_resume_data() const; + + // forces this torrent to reannounce + // (make a rerequest from the tracker) + void force_reannounce() const; + + // forces a reannounce in the specified amount of time. + // This overrides the default announce interval, and no + // announce will take place until the given time has + // timed out. + void force_reannounce(boost::posix_time::time_duration) const; + + // returns the name of this torrent, in case it doesn't + // have metadata it returns the name assigned to it + // when it was added. + std::string name() const; + + // TODO: add a feature where the user can tell the torrent + // to finish all pieces currently in the pipeline, and then + // abort the torrent. + + void set_upload_limit(int limit) const; + int upload_limit() const; + void set_download_limit(int limit) const; + int download_limit() const; + + void set_sequenced_download_threshold(int threshold) const; + + void set_peer_upload_limit(tcp::endpoint ip, int limit) const; + void set_peer_download_limit(tcp::endpoint ip, int limit) const; + + // manually connect a peer + void connect_peer(tcp::endpoint const& adr, int source = 0) const; + + // valid ratios are 0 (infinite ratio) or [ 1.0 , inf ) + // the ratio is uploaded / downloaded. less than 1 is not allowed + void set_ratio(float up_down_ratio) const; + + boost::filesystem::path save_path() const; + + // -1 means unlimited unchokes + void set_max_uploads(int max_uploads) const; + + // -1 means unlimited connections + void set_max_connections(int max_connections) const; + + void set_tracker_login(std::string const& name + , std::string const& password) const; + + // post condition: save_path() == save_path if true is returned + bool move_storage(boost::filesystem::path const& save_path) const; + + const sha1_hash& info_hash() const + { return m_info_hash; } + + bool operator==(const torrent_handle& h) const + { return m_info_hash == h.m_info_hash; } + + bool operator!=(const torrent_handle& h) const + { return m_info_hash != h.m_info_hash; } + + bool operator<(const torrent_handle& h) const + { return m_info_hash < h.m_info_hash; } + + private: + + torrent_handle(aux::session_impl* s, + aux::checker_impl* c, + const sha1_hash& h) + : m_ses(s) + , m_chk(c) + , m_info_hash(h) + { + assert(m_ses != 0); + } + +#ifndef NDEBUG + void check_invariant() const; +#endif + + aux::session_impl* m_ses; + aux::checker_impl* m_chk; + sha1_hash m_info_hash; + + }; + + +} + +#endif // TORRENT_TORRENT_HANDLE_HPP_INCLUDED + diff --git a/encryption/libtorrent/include/libtorrent/torrent_info.hpp b/encryption/libtorrent/include/libtorrent/torrent_info.hpp new file mode 100755 index 000000000..d26f9f1f2 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/torrent_info.hpp @@ -0,0 +1,269 @@ +/* + +Copyright (c) 2003, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_TORRENT_INFO_HPP_INCLUDE +#define TORRENT_TORRENT_INFO_HPP_INCLUDE + + +#include +#include + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/entry.hpp" +#include "libtorrent/socket.hpp" +#include "libtorrent/peer_id.hpp" +#include "libtorrent/size_type.hpp" +#include "libtorrent/peer_request.hpp" +#include "libtorrent/config.hpp" +#include "libtorrent/time.hpp" + +namespace libtorrent +{ + namespace pt = boost::posix_time; + namespace gr = boost::gregorian; + + struct TORRENT_EXPORT file_entry + { + boost::filesystem::path path; + size_type offset; // the offset of this file inside the torrent + size_type size; // the size of this file + // if the path was incorrectly encoded, this is + // the origianal corrupt encoded string. It is + // preserved in order to be able to reproduce + // the correct info-hash + boost::shared_ptr orig_path; + }; + + struct TORRENT_EXPORT file_slice + { + int file_index; + size_type offset; + size_type size; + }; + + struct TORRENT_EXPORT announce_entry + { + announce_entry(std::string const& u): url(u), tier(0) {} + std::string url; + int tier; + }; + + struct TORRENT_EXPORT invalid_torrent_file: std::exception + { + virtual const char* what() const throw() { return "invalid torrent file"; } + }; + + class TORRENT_EXPORT torrent_info + { + public: + + torrent_info(); + torrent_info(sha1_hash const& info_hash); + torrent_info(entry const& torrent_file); + ~torrent_info(); + + entry create_torrent() const; + entry create_info_metadata() const; + void set_comment(char const* str); + void set_creator(char const* str); + void set_piece_size(int size); + void set_hash(int index, sha1_hash const& h); + void add_tracker(std::string const& url, int tier = 0); + void add_file(boost::filesystem::path file, size_type size); + void add_url_seed(std::string const& url); + + std::vector map_block(int piece, size_type offset, int size) const; + peer_request map_file(int file, size_type offset, int size) const; + + std::vector const& url_seeds() const + { + assert(!m_half_metadata); + return m_url_seeds; + } + + typedef std::vector::const_iterator file_iterator; + typedef std::vector::const_reverse_iterator reverse_file_iterator; + + // list the files in the torrent file + file_iterator begin_files() const { return m_files.begin(); } + file_iterator end_files() const { return m_files.end(); } + reverse_file_iterator rbegin_files() const { return m_files.rbegin(); } + reverse_file_iterator rend_files() const { return m_files.rend(); } + + int num_files() const + { assert(m_piece_length > 0); return (int)m_files.size(); } + const file_entry& file_at(int index) const + { assert(index >= 0 && index < (int)m_files.size()); return m_files[index]; } + + const std::vector& trackers() const { return m_urls; } + + size_type total_size() const { assert(m_piece_length > 0); return m_total_size; } + size_type piece_length() const { assert(m_piece_length > 0); return m_piece_length; } + int num_pieces() const { assert(m_piece_length > 0); return m_num_pieces; } + const sha1_hash& info_hash() const { return m_info_hash; } + const std::string& name() const { assert(m_piece_length > 0); return m_name; } + +// ------- start deprecation ------- + void print(std::ostream& os) const; +// ------- end deprecation ------- + + bool is_valid() const { return m_piece_length > 0; } + + bool priv() const { return m_private; } + void set_priv(bool v) { m_private = v; } + + void convert_file_names(); + + size_type piece_size(int index) const; + + const sha1_hash& hash_for_piece(int index) const + { + assert(index >= 0); + assert(index < (int)m_piece_hash.size()); + assert(!m_half_metadata); + return m_piece_hash[index]; + } + + 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; + + nodes_t const& nodes() const + { + assert(!m_half_metadata); + return m_nodes; + } + + void add_node(std::pair const& node); + + void parse_info_section(entry const& e); + + entry extra(char const* key) const + { return m_extra_info[key]; } + + // frees parts of the metadata that isn't + // used by seeds + void seed_free(); + + private: + + void read_torrent_info(const entry& libtorrent); + + // the urls to the trackers + std::vector m_urls; + + std::vector m_url_seeds; + + // the length of one piece + // if this is 0, the torrent_info is + // in an uninitialized state + size_type m_piece_length; + + // the sha-1 hashes of each piece + std::vector m_piece_hash; + + // the list of files that this torrent consists of + std::vector m_files; + + nodes_t m_nodes; + + // the sum of all filesizes + size_type m_total_size; + + // the number of pieces in the torrent + int m_num_pieces; + + // the hash that identifies this torrent + // is mutable because it's calculated + // lazily + mutable sha1_hash m_info_hash; + + std::string m_name; + + // if a creation date is found in the torrent file + // this will be set to that, otherwise it'll be + // 1970, Jan 1 + pt::ptime m_creation_date; + + // if a comment is found in the torrent file + // this will be set to that comment + std::string m_comment; + + // an optional string naming the software used + // to create the torrent file + std::string m_created_by; + + // this is used when creating a torrent. If there's + // only one file there are cases where it's impossible + // to know if it should be written as a multifile torrent + // or not. e.g. test/test there's one file and one directory + // and they have the same name. + bool m_multifile; + + // this is true if the torrent is private. i.e., is should not + // be announced on the dht + bool m_private; + + // contains any non-parsed entries from the info-section + // these are kept in order to be able to accurately + // reproduce the info-section when sending the metadata + // to peers. + entry m_extra_info; + +#ifndef NDEBUG + // this is set to true when seed_free() is called + bool m_half_metadata; +#endif + }; + +} + +#endif // TORRENT_TORRENT_INFO_HPP_INCLUDED + diff --git a/encryption/libtorrent/include/libtorrent/tracker_manager.hpp b/encryption/libtorrent/include/libtorrent/tracker_manager.hpp new file mode 100755 index 000000000..a4d24f751 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/tracker_manager.hpp @@ -0,0 +1,258 @@ +/* + +Copyright (c) 2003, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_TRACKER_MANAGER_HPP_INCLUDED +#define TORRENT_TRACKER_MANAGER_HPP_INCLUDED + +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include +#include +#include +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/socket.hpp" +#include "libtorrent/entry.hpp" +#include "libtorrent/session_settings.hpp" +#include "libtorrent/peer_id.hpp" +#include "libtorrent/peer.hpp" +#include "libtorrent/config.hpp" +#include "libtorrent/time.hpp" +#include "libtorrent/connection_queue.hpp" + +namespace libtorrent +{ + struct request_callback; + class tracker_manager; + struct timeout_handler; + struct tracker_connection; + + // encodes a string using the base64 scheme + TORRENT_EXPORT std::string base64encode(const std::string& s); + + // returns -1 if gzip header is invalid or the header size in bytes + TORRENT_EXPORT int gzip_header(const char* buf, int size); + + TORRENT_EXPORT boost::tuple + parse_url_components(std::string url); + + struct TORRENT_EXPORT tracker_request + { + tracker_request() + : kind(announce_request) + , event(none) + , key(0) + , num_want(0) + {} + + enum + { + announce_request, + scrape_request + } kind; + + enum event_t + { + none, + completed, + started, + stopped + }; + + sha1_hash info_hash; + peer_id pid; + size_type downloaded; + size_type uploaded; + size_type left; + unsigned short listen_port; + event_t event; + std::string url; + int key; + int num_want; + }; + + struct TORRENT_EXPORT request_callback + { + friend class tracker_manager; + request_callback(): m_manager(0) {} + virtual ~request_callback() {} + virtual void tracker_warning(std::string const& msg) = 0; + virtual void tracker_response( + tracker_request const& + , std::vector& peers + , int interval + , int complete + , int incomplete) = 0; + virtual void tracker_request_timed_out( + tracker_request const&) = 0; + virtual void tracker_request_error( + tracker_request const& + , int response_code + , const std::string& description) = 0; + + tcp::endpoint m_tracker_address; + +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + virtual void debug_log(const std::string& line) = 0; +#endif + private: + tracker_manager* m_manager; + }; + + TORRENT_EXPORT bool inflate_gzip( + std::vector& buffer + , tracker_request const& req + , request_callback* requester + , int maximum_tracker_response_length); + + TORRENT_EXPORT void intrusive_ptr_add_ref(timeout_handler const*); + TORRENT_EXPORT void intrusive_ptr_release(timeout_handler const*); + + struct TORRENT_EXPORT timeout_handler + : boost::noncopyable + { + friend TORRENT_EXPORT void intrusive_ptr_add_ref(timeout_handler const*); + friend TORRENT_EXPORT void intrusive_ptr_release(timeout_handler const*); + + timeout_handler(asio::strand& str); + + void set_timeout(int completion_timeout, int read_timeout); + void restart_read_timeout(); + void cancel(); + + virtual void on_timeout() = 0; + virtual ~timeout_handler() {} + + private: + + void timeout_callback(asio::error_code const&); + + boost::intrusive_ptr self() + { return boost::intrusive_ptr(this); } + + asio::strand& m_strand; + // used for timeouts + // this is set when the request has been sent + ptime m_start_time; + // this is set every time something is received + ptime m_read_time; + // the asio async operation + deadline_timer m_timeout; + + int m_completion_timeout; + int m_read_timeout; + + typedef boost::mutex mutex_t; + mutable mutex_t m_mutex; + mutable int m_refs; + }; + + struct TORRENT_EXPORT tracker_connection + : timeout_handler + { + tracker_connection(tracker_manager& man + , tracker_request req + , asio::strand& str + , address bind_interface + , boost::weak_ptr r); + + request_callback& requester(); + virtual ~tracker_connection() {} + + tracker_request const& tracker_req() const { return m_req; } + bool has_requester() const { return !m_requester.expired(); } + + void fail(int code, char const* msg); + void fail_timeout(); + void close(); + address const& bind_interface() const { return m_bind_interface; } + + protected: + boost::weak_ptr m_requester; + private: + address m_bind_interface; + tracker_manager& m_man; + const tracker_request m_req; + }; + + class TORRENT_EXPORT tracker_manager: boost::noncopyable + { + public: + + tracker_manager(session_settings const& s, proxy_settings const& ps) + : m_settings(s) + , m_proxy(ps) + , m_abort(false) {} + + void queue_request( + asio::strand& str + , connection_queue& cc + , tracker_request r + , std::string const& auth + , address bind_infc + , boost::weak_ptr c + = boost::weak_ptr()); + void abort_all_requests(); + + void remove_request(tracker_connection const*); + bool empty() const; + + private: + + typedef boost::recursive_mutex mutex_t; + mutable mutex_t m_mutex; + + typedef std::list > + tracker_connections_t; + tracker_connections_t m_connections; + session_settings const& m_settings; + proxy_settings const& m_proxy; + bool m_abort; + }; +} + +#endif // TORRENT_TRACKER_MANAGER_HPP_INCLUDED + diff --git a/encryption/libtorrent/include/libtorrent/udp_tracker_connection.hpp b/encryption/libtorrent/include/libtorrent/udp_tracker_connection.hpp new file mode 100755 index 000000000..c0e6853b9 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/udp_tracker_connection.hpp @@ -0,0 +1,122 @@ +/* + +Copyright (c) 2003, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_UDP_TRACKER_CONNECTION_HPP_INCLUDED +#define TORRENT_UDP_TRACKER_CONNECTION_HPP_INCLUDED + +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/socket.hpp" +#include "libtorrent/entry.hpp" +#include "libtorrent/session_settings.hpp" +#include "libtorrent/peer_id.hpp" +#include "libtorrent/peer.hpp" +#include "libtorrent/tracker_manager.hpp" +#include "libtorrent/config.hpp" + +namespace libtorrent +{ + class TORRENT_EXPORT udp_tracker_connection: public tracker_connection + { + friend class tracker_manager; + public: + + udp_tracker_connection( + asio::strand& str + , tracker_manager& man + , tracker_request const& req + , std::string const& hostname + , unsigned short port + , address bind_infc + , boost::weak_ptr c + , session_settings const& stn); + + private: + + enum action_t + { + action_connect, + action_announce, + action_scrape, + action_error + }; + + boost::intrusive_ptr self() + { return boost::intrusive_ptr(this); } + + void name_lookup(asio::error_code const& error, udp::resolver::iterator i); + void timeout(asio::error_code const& error); + + void send_udp_connect(); + void connect_response(asio::error_code const& error, std::size_t bytes_transferred); + + void send_udp_announce(); + void announce_response(asio::error_code const& error, std::size_t bytes_transferred); + + void send_udp_scrape(); + void scrape_response(asio::error_code const& error, std::size_t bytes_transferred); + + virtual void on_timeout(); + + tracker_manager& m_man; + + asio::strand& m_strand; + udp::resolver m_name_lookup; + boost::shared_ptr m_socket; + udp::endpoint m_target; + udp::endpoint m_sender; + + int m_transaction_id; + boost::int64_t m_connection_id; + session_settings const& m_settings; + int m_attempts; + std::vector m_buffer; + }; + +} + +#endif // TORRENT_UDP_TRACKER_CONNECTION_HPP_INCLUDED + diff --git a/encryption/libtorrent/include/libtorrent/upnp.hpp b/encryption/libtorrent/include/libtorrent/upnp.hpp new file mode 100644 index 000000000..d4b701aad --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/upnp.hpp @@ -0,0 +1,237 @@ +/* + +Copyright (c) 2007, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_UPNP_HPP +#define TORRENT_UPNP_HPP + +#include "libtorrent/socket.hpp" +#include "libtorrent/http_connection.hpp" +#include "libtorrent/connection_queue.hpp" + +#include +#include +#include +#include +#include +#include + + +#if (defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)) && !defined (TORRENT_UPNP_LOGGING) +#define TORRENT_UPNP_LOGGING +#endif + +#if defined(TORRENT_UPNP_LOGGING) +#include +#endif + +namespace libtorrent +{ + +bool is_local(address const& a); +address_v4 guess_local_address(asio::io_service&); + +// int: external tcp port +// int: external udp port +// std::string: error message +typedef boost::function portmap_callback_t; + +class upnp : boost::noncopyable +{ +public: + upnp(io_service& ios, connection_queue& cc + , address const& listen_interface, std::string const& user_agent + , portmap_callback_t const& cb); + ~upnp(); + + void rebind(address const& listen_interface); + + // maps the ports, if a port is set to 0 + // it will not be mapped + void set_mappings(int tcp, int udp); + + void close(); + +private: + + static address_v4 upnp_multicast_address; + static udp::endpoint upnp_multicast_endpoint; + + enum { num_mappings = 2 }; + enum { default_lease_time = 3600 }; + + void update_mapping(int i, int port); + void resend_request(asio::error_code const& e); + void on_reply(asio::error_code const& e + , std::size_t bytes_transferred); + void discover_device(); + + struct rootdevice; + + void on_upnp_xml(asio::error_code const& e + , libtorrent::http_parser const& p, rootdevice& d); + void on_upnp_map_response(asio::error_code const& e + , libtorrent::http_parser const& p, rootdevice& d + , int mapping); + void on_upnp_unmap_response(asio::error_code const& e + , libtorrent::http_parser const& p, rootdevice& d + , int mapping); + void on_expire(asio::error_code const& e); + + void post(rootdevice& d, std::stringstream const& s + , std::string const& soap_action); + void map_port(rootdevice& d, int i); + void unmap_port(rootdevice& d, int i); + void disable(); + + struct mapping_t + { + mapping_t() + : need_update(false) + , local_port(0) + , external_port(0) + , protocol(1) + {} + + // the time the port mapping will expire + ptime expires; + + bool need_update; + + // 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; + + // 1 = udp, 0 = tcp + int protocol; + }; + + struct rootdevice + { + rootdevice(): service_namespace(0) + , lease_duration(default_lease_time) + , supports_specific_external(true) + , disabled(false) + { + mapping[0].protocol = 0; + mapping[1].protocol = 1; + } + + // 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 + char const* service_namespace; + + mapping_t mapping[num_mappings]; + + std::string hostname; + int port; + std::string path; + + int lease_duration; + // true if the device supports specifying a + // specific external port, false if it doesn't + bool supports_specific_external; + + bool disabled; + + mutable boost::shared_ptr upnp_connection; + + void close() const + { + if (!upnp_connection) return; + upnp_connection->close(); + upnp_connection.reset(); + } + + bool operator<(rootdevice const& rhs) const + { return url < rhs.url; } + }; + + int m_udp_local_port; + int m_tcp_local_port; + + std::string const& m_user_agent; + + // the set of devices we've found + std::set m_devices; + + portmap_callback_t m_callback; + + // current retry count + int m_retry_count; + + // used to receive responses in + char m_receive_buffer[1024]; + + // the endpoint we received the message from + udp::endpoint m_remote; + + // the local address we're listening on + address_v4 m_local_ip; + + // the udp socket used to send and receive + // multicast messages on the network + datagram_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; + + asio::strand m_strand; + + bool m_disabled; + bool m_closing; + + connection_queue& m_cc; + +#ifdef TORRENT_UPNP_LOGGING + std::ofstream m_log; +#endif +}; + +} + + +#endif + diff --git a/encryption/libtorrent/include/libtorrent/utf8.hpp b/encryption/libtorrent/include/libtorrent/utf8.hpp new file mode 100644 index 000000000..157a7fdba --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/utf8.hpp @@ -0,0 +1,161 @@ +/* + Copyright (C) 2004-2005 Cory Nelson + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +// namespaces added by Arvid Norberg + +#ifndef __UTF8_H__ +#define __UTF8_H__ + +#include +#include +#include +#include + +namespace libtorrent { +namespace detail { + +template +wchar_t decode_utf8_mb(InputIterator &iter, InputIterator last) +{ + if (iter == last) throw std::runtime_error("incomplete UTF-8 sequence"); + if (((*iter) & 0xc0) != 0x80) throw std::runtime_error("invalid UTF-8 sequence"); + + return (wchar_t)((*iter++) & 0x3f); +} + +template +wchar_t decode_utf8(InputIterator &iter, InputIterator last) +{ + wchar_t ret; + + if (((*iter) & 0x80) == 0) // one byte + { + ret = *iter++; + } + else if (((*iter) & 0xe0) == 0xc0) // two bytes + { + wchar_t byte1 = (*iter++) & 0x1f; + wchar_t byte2 = decode_utf8_mb(iter, last); + ret = (byte1 << 6) | byte2; + } + else if (((*iter) & 0xf0) == 0xe0) // three bytes + { + wchar_t byte1 = (*iter++) & 0x0f; + wchar_t byte2 = decode_utf8_mb(iter, last); + wchar_t byte3 = decode_utf8_mb(iter, last); + ret = (byte1 << 12) | (byte2 << 6) | byte3; + } + // TODO: support surrogate pairs + else throw std::runtime_error("UTF-8 not convertable to UTF-16"); + + return ret; +} + +template +OutputIterator utf8_wchar(InputIterator first, InputIterator last, OutputIterator dest) +{ + for(; first!=last; ++dest) + *dest = decode_utf8(first, last); + return dest; +} + +template +void encode_wchar(InputIterator iter, OutputIterator &dest) +{ + if(*iter <= 0x007F) + { + *dest=(char)*iter; + ++dest; + } + else if(*iter <= 0x07FF) + { + *dest = (char)( + 0xC0 | + ((*iter & 0x07C0) >> 6) + ); + ++dest; + + *dest = (char)( + 0x80 | + (*iter & 0x003F) + ); + ++dest; + } + else if(*iter <= 0xFFFF) + { + *dest = (char)( + 0xE0 | + ((*iter & 0xF000) >> 12) + ); + ++dest; + + *dest = (char)( + 0x80 | + ((*iter & 0x0FC0) >> 6) + ); + ++dest; + + *dest = (char)( + 0x80 | + (*iter & 0x003F) + ); + ++dest; + } +} + +template +OutputIterator wchar_utf8(InputIterator first, InputIterator last, OutputIterator dest) +{ + for(; first!=last; ++first) + encode_wchar(first, dest); + return dest; +} + +} + +inline void utf8_wchar(const std::string &utf8, std::wstring &wide) +{ + wide.clear(); + detail::utf8_wchar(utf8.begin(), utf8.end(), std::back_inserter(wide)); +} + +inline std::wstring utf8_wchar(const std::string &str) +{ + std::wstring ret; + utf8_wchar(str, ret); + return ret; +} + +inline void wchar_utf8(const std::wstring &wide, std::string &utf8) +{ + utf8.clear(); + detail::wchar_utf8(wide.begin(), wide.end(), std::back_inserter(utf8)); +} + +inline std::string wchar_utf8(const std::wstring &str) +{ + std::string ret; + wchar_utf8(str, ret); + return ret; +} + +} + +#endif diff --git a/encryption/libtorrent/include/libtorrent/variant_stream.hpp b/encryption/libtorrent/include/libtorrent/variant_stream.hpp new file mode 100644 index 000000000..8f9888519 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/variant_stream.hpp @@ -0,0 +1,716 @@ +// Copyright Daniel Wallin and Arvid Norberg 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 VARIANT_STREAM_070211_HPP +# define VARIANT_STREAM_070211_HPP + +# include + +# include +# include +# include +# include +# include + +# include +# include +# include + +# include +# include + +#include + +# define NETWORK_VARIANT_STREAM_LIMIT 5 + +namespace libtorrent { + +namespace aux +{ + + struct delete_visitor + : boost::static_visitor<> + { + template + void operator()(T* p) const + { + delete p; + } + + void operator()(boost::blank) const + {} + }; + +// -------------- io_control ----------- + + template + struct io_control_visitor_ec: boost::static_visitor<> + { + io_control_visitor_ec(IO_Control_Command& ioc, asio::error_code& ec) + : ioc(ioc), ec(ec) {} + + template + void operator()(T* p) const + { + p->io_control(ioc, ec); + } + + void operator()(boost::blank) const + {} + + IO_Control_Command& ioc; + asio::error_code& ec; + }; + + template + struct io_control_visitor + : boost::static_visitor<> + { + io_control_visitor(IO_Control_Command& ioc) + : ioc(ioc) {} + + template + void operator()(T* p) const + { + p->io_control(ioc); + } + + void operator()(boost::blank) const + {} + + IO_Control_Command& ioc; + }; +// -------------- async_connect ----------- + + template + struct async_connect_visitor + : boost::static_visitor<> + { + async_connect_visitor(EndpointType const& endpoint, Handler const& handler) + : endpoint(endpoint) + , handler(handler) + {} + + template + void operator()(T* p) const + { + p->async_connect(endpoint, handler); + } + + void operator()(boost::blank) const + {} + + EndpointType const& endpoint; + Handler const& handler; + }; + +// -------------- bind ----------- + + template + struct bind_visitor + : boost::static_visitor<> + { + bind_visitor(EndpointType const& ep, Error_Handler const& error_handler) + : endpoint(ep) + , error_handler(error_handler) + {} + + template + void operator()(T* p) const + { + p->bind(endpoint, error_handler); + } + + void operator()(boost::blank) const + {} + + EndpointType const& endpoint; + Error_Handler const& error_handler; + }; + + template + struct bind_visitor + : boost::static_visitor<> + { + bind_visitor(EndpointType const& ep) + : endpoint(ep) + {} + + template + void operator()(T* p) const + { + p->bind(endpoint); + } + + void operator()(boost::blank) const + {} + + EndpointType const& endpoint; + }; + +// -------------- open ----------- + + template + struct open_visitor + : boost::static_visitor<> + { + open_visitor(Protocol const& p, Error_Handler const& error_handler) + : proto(p) + , error_handler(error_handler) + {} + + template + void operator()(T* p) const + { + p->open(proto, error_handler); + } + + void operator()(boost::blank) const + {} + + Protocol const& proto; + Error_Handler const& error_handler; + }; + + template + struct open_visitor + : boost::static_visitor<> + { + open_visitor(Protocol const& p) + : proto(p) + {} + + template + void operator()(T* p) const + { + p->open(proto); + } + + void operator()(boost::blank) const + {} + + Protocol const& proto; + }; + +// -------------- close ----------- + + template + struct close_visitor + : boost::static_visitor<> + { + close_visitor(Error_Handler const& error_handler) + : error_handler(error_handler) + {} + + template + void operator()(T* p) const + { + p->close(error_handler); + } + + void operator()(boost::blank) const + {} + + Error_Handler const& error_handler; + }; + + template <> + struct close_visitor + : boost::static_visitor<> + { + template + void operator()(T* p) const + { + p->close(); + } + + void operator()(boost::blank) const + {} + }; + +// -------------- remote_endpoint ----------- + + template + struct remote_endpoint_visitor + : boost::static_visitor + { + remote_endpoint_visitor(Error_Handler const& error_handler) + : error_handler(error_handler) + {} + + template + EndpointType operator()(T* p) const + { + return p->remote_endpoint(error_handler); + } + + EndpointType operator()(boost::blank) const + { + return EndpointType(); + } + + Error_Handler const& error_handler; + }; + + template + struct remote_endpoint_visitor + : boost::static_visitor + { + template + EndpointType operator()(T* p) const + { + return p->remote_endpoint(); + } + + EndpointType operator()(boost::blank) const + { + return EndpointType(); + } + }; + +// -------------- local_endpoint ----------- + + template + struct local_endpoint_visitor + : boost::static_visitor + { + local_endpoint_visitor(Error_Handler const& error_handler) + : error_handler(error_handler) + {} + + template + EndpointType operator()(T* p) const + { + return p->local_endpoint(error_handler); + } + + EndpointType operator()(boost::blank) const + { + return EndpointType(); + } + + Error_Handler const& error_handler; + }; + + template + struct local_endpoint_visitor + : boost::static_visitor + { + template + EndpointType operator()(T* p) const + { + return p->local_endpoint(); + } + + EndpointType operator()(boost::blank) const + { + return EndpointType(); + } + }; + +// -------------- async_read_some ----------- + + template + struct async_read_some_visitor + : boost::static_visitor<> + { + async_read_some_visitor(Mutable_Buffers const& buffers, Handler const& handler) + : buffers(buffers) + , handler(handler) + {} + + template + void operator()(T* p) const + { + p->async_read_some(buffers, handler); + } + void operator()(boost::blank) const + {} + + Mutable_Buffers const& buffers; + Handler const& handler; + }; + +// -------------- read_some ----------- + + template + struct read_some_visitor + : boost::static_visitor + { + read_some_visitor(Mutable_Buffers const& buffers) + : buffers(buffers) + {} + + template + std::size_t operator()(T* p) const + { return p->read_some(buffers); } + + std::size_t operator()(boost::blank) const + { return 0; } + + Mutable_Buffers const& buffers; + }; + + template + struct read_some_visitor_ec + : boost::static_visitor + { + read_some_visitor_ec(Mutable_Buffers const& buffers, asio::error_code& ec) + : buffers(buffers) + , ec(ec) + {} + + template + std::size_t operator()(T* p) const + { return p->read_some(buffers, ec); } + + std::size_t operator()(boost::blank) const + { return 0; } + + Mutable_Buffers const& buffers; + asio::error_code& ec; + }; + +// -------------- async_write_some ----------- + + template + struct async_write_some_visitor + : boost::static_visitor<> + { + async_write_some_visitor(Const_Buffers const& buffers, Handler const& handler) + : buffers(buffers) + , handler(handler) + {} + + template + void operator()(T* p) const + { + p->async_write_some(buffers, handler); + } + + void operator()(boost::blank) const + {} + + Const_Buffers const& buffers; + Handler const& handler; + }; + +// -------------- in_avail ----------- + + template + struct in_avail_visitor + : boost::static_visitor + { + in_avail_visitor(Error_Handler const& error_handler) + : error_handler(error_handler) + {} + + template + std::size_t operator()(T* p) const + { + return p->in_avail(error_handler); + } + + std::size_t operator()(boost::blank) const + { + return 0; + } + + Error_Handler const& error_handler; + }; + + template <> + struct in_avail_visitor + : boost::static_visitor + { + template + std::size_t operator()(T* p) const + { + return p->in_avail(); + } + + void operator()(boost::blank) const + {} + }; + +// -------------- io_service ----------- + + template + struct io_service_visitor + : boost::static_visitor + { + template + IOService& operator()(T* p) const + { + return p->io_service(); + } + + IOService& operator()(boost::blank) const + { + return *(IOService*)0; + } + }; + +// -------------- lowest_layer ----------- + + template + struct lowest_layer_visitor + : boost::static_visitor + { + template + LowestLayer& operator()(T* p) const + { + return p->lowest_layer(); + } + + LowestLayer& operator()(boost::blank) const + { + return *(LowestLayer*)0; + } + }; + +} // namespace aux + +template < + BOOST_PP_ENUM_BINARY_PARAMS( + NETWORK_VARIANT_STREAM_LIMIT, class S, = boost::mpl::void_ BOOST_PP_INTERCEPT + ) +> +class variant_stream : boost::noncopyable +{ +public: + typedef BOOST_PP_CAT(boost::mpl::vector, NETWORK_VARIANT_STREAM_LIMIT)< + BOOST_PP_ENUM_PARAMS(NETWORK_VARIANT_STREAM_LIMIT, S) + > types0; + + typedef typename boost::mpl::remove::type types; + + typedef typename boost::make_variant_over< + typename boost::mpl::push_back< + typename boost::mpl::transform< + types + , boost::add_pointer + >::type + , boost::blank + >::type + >::type variant_type; + + typedef typename S0::lowest_layer_type lowest_layer_type; + typedef typename S0::endpoint_type endpoint_type; + typedef typename S0::protocol_type protocol_type; + + explicit variant_stream(asio::io_service& io_service) + : m_io_service(io_service) + , m_variant(boost::blank()) + {} + + template + void instantiate() + { + std::auto_ptr owned(new S(m_io_service)); + boost::apply_visitor(aux::delete_visitor(), m_variant); + m_variant = owned.get(); + owned.release(); + } + + template + S& get() + { + return *boost::get(m_variant); + } + + bool instantiated() const + { + return m_variant.which() != boost::mpl::size::value; + } + + ~variant_stream() + { + boost::apply_visitor(aux::delete_visitor(), m_variant); + } + + template + std::size_t read_some(Mutable_Buffers const& buffers, asio::error_code& ec) + { + assert(instantiated()); + return boost::apply_visitor( + aux::read_some_visitor_ec(buffers, ec) + , m_variant + ); + } + + template + std::size_t read_some(Mutable_Buffers const& buffers) + { + assert(instantiated()); + return boost::apply_visitor( + aux::read_some_visitor(buffers) + , m_variant + ); + } + + template + void async_read_some(Mutable_Buffers const& buffers, Handler const& handler) + { + assert(instantiated()); + boost::apply_visitor( + aux::async_read_some_visitor(buffers, handler) + , m_variant + ); + } + + template + void async_write_some(Const_Buffers const& buffers, Handler const& handler) + { + assert(instantiated()); + boost::apply_visitor( + aux::async_write_some_visitor(buffers, handler) + , m_variant + ); + } + + template + void async_connect(endpoint_type const& endpoint, Handler const& handler) + { + assert(instantiated()); + boost::apply_visitor( + aux::async_connect_visitor(endpoint, handler), m_variant + ); + } + + template + void io_control(IO_Control_Command& ioc) + { + assert(instantiated()); + boost::apply_visitor( + aux::io_control_visitor(ioc), m_variant + ); + } + + template + void io_control(IO_Control_Command& ioc, asio::error_code& ec) + { + assert(instantiated()); + boost::apply_visitor( + aux::io_control_visitor_ec(ioc, ec) + , m_variant + ); + } + + void bind(endpoint_type const& endpoint) + { + assert(instantiated()); + boost::apply_visitor(aux::bind_visitor(endpoint), m_variant); + } + + template + void bind(endpoint_type const& endpoint, Error_Handler const& error_handler) + { + assert(instantiated()); + boost::apply_visitor( + aux::bind_visitor(endpoint, error_handler), m_variant + ); + } + + void open(protocol_type const& p) + { + assert(instantiated()); + boost::apply_visitor(aux::open_visitor(p), m_variant); + } + + template + void open(protocol_type const& p, Error_Handler const& error_handler) + { + assert(instantiated()); + boost::apply_visitor( + aux::open_visitor(p, error_handler), m_variant + ); + } + + void close() + { + assert(instantiated()); + boost::apply_visitor(aux::close_visitor<>(), m_variant); + } + + template + void close(Error_Handler const& error_handler) + { + assert(instantiated()); + boost::apply_visitor( + aux::close_visitor(error_handler), m_variant + ); + } + + std::size_t in_avail() + { + assert(instantiated()); + return boost::apply_visitor(aux::in_avail_visitor<>(), m_variant); + } + + template + std::size_t in_avail(Error_Handler const& error_handler) + { + assert(instantiated()); + return boost::apply_visitor( + aux::in_avail_visitor(error_handler), m_variant + ); + } + + endpoint_type remote_endpoint() + { + assert(instantiated()); + return boost::apply_visitor(aux::remote_endpoint_visitor(), m_variant); + } + + template + endpoint_type remote_endpoint(Error_Handler const& error_handler) + { + assert(instantiated()); + return boost::apply_visitor( + aux::remote_endpoint_visitor(error_handler), m_variant + ); + } + + endpoint_type local_endpoint() + { + assert(instantiated()); + return boost::apply_visitor(aux::local_endpoint_visitor(), m_variant); + } + + template + endpoint_type local_endpoint(Error_Handler const& error_handler) + { + assert(instantiated()); + return boost::apply_visitor( + aux::local_endpoint_visitor(error_handler), m_variant + ); + } + + asio::io_service& io_service() + { + assert(instantiated()); + return boost::apply_visitor( + aux::io_service_visitor(), m_variant + ); + } + + lowest_layer_type& lowest_layer() + { + assert(instantiated()); + return boost::apply_visitor( + aux::lowest_layer_visitor(), m_variant + ); + } + +private: + asio::io_service& m_io_service; + variant_type m_variant; +}; + +} // namespace libtorrent + +#endif // VARIANT_STREAM_070211_HPP + diff --git a/encryption/libtorrent/include/libtorrent/version.hpp b/encryption/libtorrent/include/libtorrent/version.hpp new file mode 100755 index 000000000..de1b8bcc8 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/version.hpp @@ -0,0 +1,41 @@ +/* + +Copyright (c) 2003, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_VERSION_HPP_INCLUDED +#define TORRENT_VERSION_HPP_INCLUDED + +#define LIBTORRENT_VERSION_MAJOR 0 +#define LIBTORRENT_VERSION_MINOR 13 + +#define LIBTORRENT_VERSION "0.13.0.0" + +#endif diff --git a/encryption/libtorrent/include/libtorrent/web_peer_connection.hpp b/encryption/libtorrent/include/libtorrent/web_peer_connection.hpp new file mode 100755 index 000000000..a84f2e6ff --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/web_peer_connection.hpp @@ -0,0 +1,179 @@ +/* + +Copyright (c) 2003, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_WEB_PEER_CONNECTION_HPP_INCLUDED +#define TORRENT_WEB_PEER_CONNECTION_HPP_INCLUDED + +#include +#include +#include +#include +#include + +#include "libtorrent/debug.hpp" + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include +#include +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/buffer.hpp" +#include "libtorrent/peer_connection.hpp" +#include "libtorrent/socket.hpp" +#include "libtorrent/peer_id.hpp" +#include "libtorrent/storage.hpp" +#include "libtorrent/stat.hpp" +#include "libtorrent/alert.hpp" +#include "libtorrent/torrent_handle.hpp" +#include "libtorrent/torrent.hpp" +#include "libtorrent/allocate_resources.hpp" +#include "libtorrent/peer_request.hpp" +#include "libtorrent/piece_block_progress.hpp" +#include "libtorrent/config.hpp" +// parse_url +#include "libtorrent/tracker_manager.hpp" +// http_parser +#include "libtorrent/http_tracker_connection.hpp" + +namespace libtorrent +{ + class torrent; + + namespace detail + { + struct session_impl; + } + + class TORRENT_EXPORT web_peer_connection + : public peer_connection + { + friend class invariant_access; + public: + + // this is the constructor where the we are the active part. + // The peer_conenction should handshake and verify that the + // other end has the correct id + web_peer_connection( + aux::session_impl& ses + , boost::weak_ptr t + , boost::shared_ptr s + , tcp::endpoint const& remote + , std::string const& url + , policy::peer* peerinfo); + + ~web_peer_connection(); + + // called from the main loop when this connection has any + // work to do. + void on_sent(asio::error_code const& error + , std::size_t bytes_transferred); + void on_receive(asio::error_code const& error + , std::size_t bytes_transferred); + + std::string const& url() const { return m_url; } + + virtual void get_specific_peer_info(peer_info& p) const; + virtual 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() {} + void write_request(peer_request const& r); + void write_cancel(peer_request const& r) {} + void write_have(int index) {} + void write_piece(peer_request const& r) {} + void write_keepalive() {} + void on_connected(); + +#ifndef NDEBUG + void check_invariant() const; +#endif + + private: + + // returns the block currently being + // downloaded. And the progress of that + // block. If the peer isn't downloading + // a piece for the moment, the boost::optional + // will be invalid. + boost::optional downloading_piece_progress() const; + + // this has one entry per bittorrent request + std::deque m_requests; + // this has one entry per http-request + // (might be more than the bt requests) + std::deque m_file_requests; + + std::string m_server_string; + http_parser m_parser; + std::string m_auth; + std::string m_host; + int m_port; + std::string m_path; + std::string m_url; + + // the first request will contain a little bit more data + // than subsequent ones, things that aren't critical are left + // out to save bandwidth. + bool m_first_request; + + // this is used for intermediate storage of pieces + // that is received in more than on HTTP responses + std::vector m_piece; + // the mapping of the data in the m_piece buffer + peer_request m_intermediate_piece; + + // the number of bytes into the receive buffer where + // current read cursor is. + int m_body_start; + // the number of bytes received in the current HTTP + // response. used to know where in the buffer the + // next response starts + int m_received_body; + }; +} + +#endif // TORRENT_WEB_PEER_CONNECTION_HPP_INCLUDED + diff --git a/encryption/libtorrent/include/libtorrent/xml_parse.hpp b/encryption/libtorrent/include/libtorrent/xml_parse.hpp new file mode 100644 index 000000000..c3aeddad7 --- /dev/null +++ b/encryption/libtorrent/include/libtorrent/xml_parse.hpp @@ -0,0 +1,99 @@ +/* + +Copyright (c) 2007, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_XML_PARSE_HPP +#define TORRENT_XML_PARSE_HPP + +namespace libtorrent +{ + const int xml_start_tag = 0; + const int xml_end_tag = 1; + const int xml_string = 2; + + template + void xml_parse(char* p, char* end, CallbackType callback) + { + for(;p != end; ++p) + { + char const* start = p; + // look for tag start + for(; *p != '<' && p != end; ++p); + + if (p != start) + { + if (p != end) + { + assert(*p == '<'); + *p = 0; + } + callback(xml_string, start); + if (p != end) *p = '<'; + } + + if (p == end) break; + + // skip '<' + ++p; + + // parse the name of the tag. Ignore attributes + for (start = p; p != end && *p != '>'; ++p) + { + // terminate the string at the first space + // to ignore tag attributes + if (*p == ' ') *p = 0; + } + + // parse error + if (p == end) break; + + assert(*p == '>'); + *p = 0; + + if (*start == '/') + { + ++start; + callback(xml_end_tag, start); + } + else + { + callback(xml_start_tag, start); + } + *p = '>'; + } + + } + +} + + +#endif + diff --git a/encryption/libtorrent/src/Makefile.am b/encryption/libtorrent/src/Makefile.am new file mode 100644 index 000000000..4c04b2863 --- /dev/null +++ b/encryption/libtorrent/src/Makefile.am @@ -0,0 +1,95 @@ +lib_LTLIBRARIES = libtorrent.la + +if USE_DHT +kademlia_sources = kademlia/closest_nodes.cpp \ +kademlia/dht_tracker.cpp \ +kademlia/find_data.cpp \ +kademlia/node.cpp \ +kademlia/node_id.cpp \ +kademlia/refresh.cpp \ +kademlia/routing_table.cpp \ +kademlia/rpc_manager.cpp \ +kademlia/traversal_algorithm.cpp +endif + +libtorrent_la_SOURCES = allocate_resources.cpp \ +bandwidth_manager.cpp entry.cpp escape_string.cpp \ +peer_connection.cpp bt_peer_connection.cpp web_peer_connection.cpp \ +natpmp.cpp piece_picker.cpp policy.cpp session.cpp session_impl.cpp sha1.cpp \ +stat.cpp storage.cpp torrent.cpp torrent_handle.cpp pe_crypto.cpp \ +torrent_info.cpp tracker_manager.cpp http_connection.cpp \ +http_tracker_connection.cpp udp_tracker_connection.cpp \ +alert.cpp identify_client.cpp ip_filter.cpp file.cpp metadata_transfer.cpp \ +logger.cpp file_pool.cpp ut_pex.cpp lsd.cpp upnp.cpp instantiate_connection.cpp \ +socks5_stream.cpp http_stream.cpp connection_queue.cpp $(kademlia_sources) + +noinst_HEADERS = \ +$(top_srcdir)/include/libtorrent/alert.hpp \ +$(top_srcdir)/include/libtorrent/alert_types.hpp \ +$(top_srcdir)/include/libtorrent/allocate_resources.hpp \ +$(top_srcdir)/include/libtorrent/aux_/allocate_resources_impl.hpp \ +$(top_srcdir)/include/libtorrent/aux_/session_impl.hpp \ +$(top_srcdir)/include/libtorrent/bandwidth_manager.hpp \ +$(top_srcdir)/include/libtorrent/bencode.hpp \ +$(top_srcdir)/include/libtorrent/buffer.hpp \ +$(top_srcdir)/include/libtorrent/connection_queue.hpp \ +$(top_srcdir)/include/libtorrent/debug.hpp \ +$(top_srcdir)/include/libtorrent/entry.hpp \ +$(top_srcdir)/include/libtorrent/escape_string.hpp \ +$(top_srcdir)/include/libtorrent/extensions.hpp \ +$(top_srcdir)/include/libtorrent/extensions/metadata_transfer.hpp \ +$(top_srcdir)/include/libtorrent/extensions/logger.hpp \ +$(top_srcdir)/include/libtorrent/extensions/ut_pex.hpp \ +$(top_srcdir)/include/libtorrent/file.hpp \ +$(top_srcdir)/include/libtorrent/file_pool.hpp \ +$(top_srcdir)/include/libtorrent/fingerprint.hpp \ +$(top_srcdir)/include/libtorrent/hasher.hpp \ +$(top_srcdir)/include/libtorrent/http_connection.hpp \ +$(top_srcdir)/include/libtorrent/http_stream.hpp \ +$(top_srcdir)/include/libtorrent/session_settings.hpp \ +$(top_srcdir)/include/libtorrent/http_tracker_connection.hpp \ +$(top_srcdir)/include/libtorrent/identify_client.hpp \ +$(top_srcdir)/include/libtorrent/instantiate_connection.hpp \ +$(top_srcdir)/include/libtorrent/invariant_check.hpp \ +$(top_srcdir)/include/libtorrent/io.hpp \ +$(top_srcdir)/include/libtorrent/ip_filter.hpp \ +$(top_srcdir)/include/libtorrent/lsd.hpp \ +$(top_srcdir)/include/libtorrent/peer.hpp \ +$(top_srcdir)/include/libtorrent/peer_connection.hpp \ +$(top_srcdir)/include/libtorrent/bt_peer_connection.hpp \ +$(top_srcdir)/include/libtorrent/web_peer_connection.hpp \ +$(top_srcdir)/include/libtorrent/pe_crypto.hpp \ +$(top_srcdir)/include/libtorrent/natpmp.hpp \ +$(top_srcdir)/include/libtorrent/pch.hpp \ +$(top_srcdir)/include/libtorrent/peer_id.hpp \ +$(top_srcdir)/include/libtorrent/peer_info.hpp \ +$(top_srcdir)/include/libtorrent/peer_request.hpp \ +$(top_srcdir)/include/libtorrent/piece_block_progress.hpp \ +$(top_srcdir)/include/libtorrent/piece_picker.hpp \ +$(top_srcdir)/include/libtorrent/policy.hpp \ +$(top_srcdir)/include/libtorrent/resource_request.hpp \ +$(top_srcdir)/include/libtorrent/session.hpp \ +$(top_srcdir)/include/libtorrent/size_type.hpp \ +$(top_srcdir)/include/libtorrent/socket.hpp \ +$(top_srcdir)/include/libtorrent/socket_type.hpp \ +$(top_srcdir)/include/libtorrent/socks5_stream.hpp \ +$(top_srcdir)/include/libtorrent/stat.hpp \ +$(top_srcdir)/include/libtorrent/storage.hpp \ +$(top_srcdir)/include/libtorrent/time.hpp \ +$(top_srcdir)/include/libtorrent/torrent.hpp \ +$(top_srcdir)/include/libtorrent/torrent_handle.hpp \ +$(top_srcdir)/include/libtorrent/torrent_info.hpp \ +$(top_srcdir)/include/libtorrent/tracker_manager.hpp \ +$(top_srcdir)/include/libtorrent/udp_tracker_connection.hpp \ +$(top_srcdir)/include/libtorrent/utf8.hpp \ +$(top_srcdir)/include/libtorrent/xml_parse.hpp \ +$(top_srcdir)/include/libtorrent/variant_stream.hpp \ +$(top_srcdir)/include/libtorrent/version.hpp + + +libtorrent_la_LDFLAGS = $(LDFLAGS) -release @VERSION@ +libtorrent_la_LIBADD = @ZLIB@ -l@BOOST_DATE_TIME_LIB@ -l@BOOST_FILESYSTEM_LIB@ -l@BOOST_THREAD_LIB@ @PTHREAD_LIBS@ + +AM_CXXFLAGS= -ftemplate-depth-50 -I$(top_srcdir)/include -I$(top_srcdir)/include/libtorrent @ZLIBINCL@ @DEBUGFLAGS@ @PTHREAD_CFLAGS@ +AM_LDFLAGS= $(LDFLAGS) -l@BOOST_DATE_TIME_LIB@ -l@BOOST_FILESYSTEM_LIB@ -l@BOOST_THREAD_LIB@ @PTHREAD_LIBS@ + diff --git a/encryption/libtorrent/src/Makefile.in b/encryption/libtorrent/src/Makefile.in new file mode 100644 index 000000000..aa318eddc --- /dev/null +++ b/encryption/libtorrent/src/Makefile.in @@ -0,0 +1,672 @@ +# Makefile.in generated by automake 1.9.6 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = .. +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +INSTALL = @INSTALL@ +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +subdir = src +DIST_COMMON = $(noinst_HEADERS) $(srcdir)/Makefile.am \ + $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/ac_cxx_namespaces.m4 \ + $(top_srcdir)/m4/acx_pthread.m4 \ + $(top_srcdir)/m4/ax_boost_date-time.m4 \ + $(top_srcdir)/m4/ax_boost_filesystem.m4 \ + $(top_srcdir)/m4/ax_boost_program_options.m4 \ + $(top_srcdir)/m4/ax_boost_regex.m4 \ + $(top_srcdir)/m4/ax_boost_thread.m4 $(top_srcdir)/configure.in +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_CLEAN_FILES = +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = `echo $$p | sed -e 's|^.*/||'`; +am__installdirs = "$(DESTDIR)$(libdir)" +libLTLIBRARIES_INSTALL = $(INSTALL) +LTLIBRARIES = $(lib_LTLIBRARIES) +libtorrent_la_DEPENDENCIES = +am__libtorrent_la_SOURCES_DIST = allocate_resources.cpp \ + bandwidth_manager.cpp entry.cpp escape_string.cpp \ + peer_connection.cpp bt_peer_connection.cpp \ + web_peer_connection.cpp piece_picker.cpp policy.cpp \ + session.cpp session_impl.cpp sha1.cpp stat.cpp storage.cpp \ + torrent.cpp torrent_handle.cpp torrent_info.cpp \ + tracker_manager.cpp http_tracker_connection.cpp \ + udp_tracker_connection.cpp alert.cpp identify_client.cpp \ + ip_filter.cpp file.cpp metadata_transfer.cpp logger.cpp \ + file_pool.cpp ut_pex.cpp kademlia/closest_nodes.cpp \ + kademlia/dht_tracker.cpp kademlia/find_data.cpp \ + kademlia/node.cpp kademlia/node_id.cpp kademlia/refresh.cpp \ + kademlia/routing_table.cpp kademlia/rpc_manager.cpp \ + kademlia/traversal_algorithm.cpp +@USE_DHT_TRUE@am__objects_1 = closest_nodes.lo dht_tracker.lo \ +@USE_DHT_TRUE@ find_data.lo node.lo node_id.lo refresh.lo \ +@USE_DHT_TRUE@ routing_table.lo rpc_manager.lo \ +@USE_DHT_TRUE@ traversal_algorithm.lo +am_libtorrent_la_OBJECTS = allocate_resources.lo bandwidth_manager.lo \ + entry.lo escape_string.lo peer_connection.lo \ + bt_peer_connection.lo web_peer_connection.lo piece_picker.lo \ + policy.lo session.lo session_impl.lo sha1.lo stat.lo \ + storage.lo torrent.lo torrent_handle.lo torrent_info.lo \ + tracker_manager.lo http_tracker_connection.lo \ + udp_tracker_connection.lo alert.lo identify_client.lo \ + ip_filter.lo file.lo metadata_transfer.lo logger.lo \ + file_pool.lo ut_pex.lo $(am__objects_1) +libtorrent_la_OBJECTS = $(am_libtorrent_la_OBJECTS) +DEFAULT_INCLUDES = -I. -I$(srcdir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +LTCXXCOMPILE = $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CXXFLAGS) $(CXXFLAGS) +CXXLD = $(CXX) +CXXLINK = $(LIBTOOL) --tag=CXX --mode=link $(CXXLD) $(AM_CXXFLAGS) \ + $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +SOURCES = $(libtorrent_la_SOURCES) +DIST_SOURCES = $(am__libtorrent_la_SOURCES_DIST) +HEADERS = $(noinst_HEADERS) +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMDEP_FALSE = @AMDEP_FALSE@ +AMDEP_TRUE = @AMDEP_TRUE@ +AMTAR = @AMTAR@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BOOST_DATE_TIME_LIB = @BOOST_DATE_TIME_LIB@ +BOOST_FILESYSTEM_LIB = @BOOST_FILESYSTEM_LIB@ +BOOST_PROGRAM_OPTIONS_LIB = @BOOST_PROGRAM_OPTIONS_LIB@ +BOOST_REGEX_LIB = @BOOST_REGEX_LIB@ +BOOST_THREAD_LIB = @BOOST_THREAD_LIB@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CLIENT_TEST_BIN = @CLIENT_TEST_BIN@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEBUGFLAGS = @DEBUGFLAGS@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +ECHO = @ECHO@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXAMPLESDIR = @EXAMPLESDIR@ +EXEEXT = @EXEEXT@ +F77 = @F77@ +FFLAGS = @FFLAGS@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAKEINFO = @MAKEINFO@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PTHREAD_CC = @PTHREAD_CC@ +PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ +PTHREAD_LIBS = @PTHREAD_LIBS@ +RANLIB = @RANLIB@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +USE_DHT_FALSE = @USE_DHT_FALSE@ +USE_DHT_TRUE = @USE_DHT_TRUE@ +VERSION = @VERSION@ +ZLIB = @ZLIB@ +ZLIBDIR = @ZLIBDIR@ +ZLIBINCL = @ZLIBINCL@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_F77 = @ac_ct_F77@ +ac_ct_RANLIB = @ac_ct_RANLIB@ +ac_ct_STRIP = @ac_ct_STRIP@ +acx_pthread_config = @acx_pthread_config@ +am__fastdepCC_FALSE = @am__fastdepCC_FALSE@ +am__fastdepCC_TRUE = @am__fastdepCC_TRUE@ +am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@ +am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +datadir = @datadir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +lib_LTLIBRARIES = libtorrent.la +@USE_DHT_TRUE@kademlia_sources = kademlia/closest_nodes.cpp \ +@USE_DHT_TRUE@kademlia/dht_tracker.cpp \ +@USE_DHT_TRUE@kademlia/find_data.cpp \ +@USE_DHT_TRUE@kademlia/node.cpp \ +@USE_DHT_TRUE@kademlia/node_id.cpp \ +@USE_DHT_TRUE@kademlia/refresh.cpp \ +@USE_DHT_TRUE@kademlia/routing_table.cpp \ +@USE_DHT_TRUE@kademlia/rpc_manager.cpp \ +@USE_DHT_TRUE@kademlia/traversal_algorithm.cpp + +libtorrent_la_SOURCES = allocate_resources.cpp \ +bandwidth_manager.cpp entry.cpp escape_string.cpp \ +peer_connection.cpp bt_peer_connection.cpp web_peer_connection.cpp \ +piece_picker.cpp policy.cpp session.cpp session_impl.cpp sha1.cpp stat.cpp \ +storage.cpp torrent.cpp torrent_handle.cpp \ +torrent_info.cpp tracker_manager.cpp \ +http_tracker_connection.cpp udp_tracker_connection.cpp \ +alert.cpp identify_client.cpp ip_filter.cpp file.cpp metadata_transfer.cpp \ +logger.cpp file_pool.cpp ut_pex.cpp $(kademlia_sources) + +noinst_HEADERS = \ +$(top_srcdir)/include/libtorrent/alert.hpp \ +$(top_srcdir)/include/libtorrent/alert_types.hpp \ +$(top_srcdir)/include/libtorrent/allocate_resources.hpp \ +$(top_srcdir)/include/libtorrent/aux_/allocate_resources_impl.hpp \ +$(top_srcdir)/include/libtorrent/aux_/session_impl.hpp \ +$(top_srcdir)/include/libtorrent/bandwidth_manager.hpp \ +$(top_srcdir)/include/libtorrent/bencode.hpp \ +$(top_srcdir)/include/libtorrent/buffer.hpp \ +$(top_srcdir)/include/libtorrent/debug.hpp \ +$(top_srcdir)/include/libtorrent/entry.hpp \ +$(top_srcdir)/include/libtorrent/escape_string.hpp \ +$(top_srcdir)/include/libtorrent/extensions.hpp \ +$(top_srcdir)/include/libtorrent/extensions/metadata_transfer.hpp \ +$(top_srcdir)/include/libtorrent/extensions/logger.hpp \ +$(top_srcdir)/include/libtorrent/extensions/ut_pex.hpp \ +$(top_srcdir)/include/libtorrent/file.hpp \ +$(top_srcdir)/include/libtorrent/file_pool.hpp \ +$(top_srcdir)/include/libtorrent/fingerprint.hpp \ +$(top_srcdir)/include/libtorrent/hasher.hpp \ +$(top_srcdir)/include/libtorrent/session_settings.hpp \ +$(top_srcdir)/include/libtorrent/http_tracker_connection.hpp \ +$(top_srcdir)/include/libtorrent/identify_client.hpp \ +$(top_srcdir)/include/libtorrent/invariant_check.hpp \ +$(top_srcdir)/include/libtorrent/io.hpp \ +$(top_srcdir)/include/libtorrent/ip_filter.hpp \ +$(top_srcdir)/include/libtorrent/peer.hpp \ +$(top_srcdir)/include/libtorrent/peer_connection.hpp \ +$(top_srcdir)/include/libtorrent/bt_peer_connection.hpp \ +$(top_srcdir)/include/libtorrent/web_peer_connection.hpp \ +$(top_srcdir)/include/libtorrent/peer_id.hpp \ +$(top_srcdir)/include/libtorrent/peer_info.hpp \ +$(top_srcdir)/include/libtorrent/peer_request.hpp \ +$(top_srcdir)/include/libtorrent/piece_block_progress.hpp \ +$(top_srcdir)/include/libtorrent/piece_picker.hpp \ +$(top_srcdir)/include/libtorrent/policy.hpp \ +$(top_srcdir)/include/libtorrent/resource_request.hpp \ +$(top_srcdir)/include/libtorrent/session.hpp \ +$(top_srcdir)/include/libtorrent/size_type.hpp \ +$(top_srcdir)/include/libtorrent/socket.hpp \ +$(top_srcdir)/include/libtorrent/stat.hpp \ +$(top_srcdir)/include/libtorrent/storage.hpp \ +$(top_srcdir)/include/libtorrent/torrent.hpp \ +$(top_srcdir)/include/libtorrent/torrent_handle.hpp \ +$(top_srcdir)/include/libtorrent/torrent_info.hpp \ +$(top_srcdir)/include/libtorrent/tracker_manager.hpp \ +$(top_srcdir)/include/libtorrent/udp_tracker_connection.hpp \ +$(top_srcdir)/include/libtorrent/utf8.hpp \ +$(top_srcdir)/include/libtorrent/version.hpp + +libtorrent_la_LDFLAGS = $(LDFLAGS) -version-info 1:0:1 +libtorrent_la_LIBADD = @ZLIB@ -l@BOOST_DATE_TIME_LIB@ -l@BOOST_FILESYSTEM_LIB@ -l@BOOST_THREAD_LIB@ @PTHREAD_LIBS@ +AM_CXXFLAGS = -ftemplate-depth-50 -I$(top_srcdir)/include -I$(top_srcdir)/include/libtorrent @ZLIBINCL@ @DEBUGFLAGS@ @PTHREAD_CFLAGS@ +AM_LDFLAGS = $(LDFLAGS) -l@BOOST_DATE_TIME_LIB@ -l@BOOST_FILESYSTEM_LIB@ -l@BOOST_THREAD_LIB@ @PTHREAD_LIBS@ +all: all-am + +.SUFFIXES: +.SUFFIXES: .cpp .lo .o .obj +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \ + && exit 0; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/Makefile'; \ + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +install-libLTLIBRARIES: $(lib_LTLIBRARIES) + @$(NORMAL_INSTALL) + test -z "$(libdir)" || $(mkdir_p) "$(DESTDIR)$(libdir)" + @list='$(lib_LTLIBRARIES)'; for p in $$list; do \ + if test -f $$p; then \ + f=$(am__strip_dir) \ + echo " $(LIBTOOL) --mode=install $(libLTLIBRARIES_INSTALL) $(INSTALL_STRIP_FLAG) '$$p' '$(DESTDIR)$(libdir)/$$f'"; \ + $(LIBTOOL) --mode=install $(libLTLIBRARIES_INSTALL) $(INSTALL_STRIP_FLAG) "$$p" "$(DESTDIR)$(libdir)/$$f"; \ + else :; fi; \ + done + +uninstall-libLTLIBRARIES: + @$(NORMAL_UNINSTALL) + @set -x; list='$(lib_LTLIBRARIES)'; for p in $$list; do \ + p=$(am__strip_dir) \ + echo " $(LIBTOOL) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$p'"; \ + $(LIBTOOL) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$p"; \ + done + +clean-libLTLIBRARIES: + -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES) + @list='$(lib_LTLIBRARIES)'; for p in $$list; do \ + dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \ + test "$$dir" != "$$p" || dir=.; \ + echo "rm -f \"$${dir}/so_locations\""; \ + rm -f "$${dir}/so_locations"; \ + done +libtorrent.la: $(libtorrent_la_OBJECTS) $(libtorrent_la_DEPENDENCIES) + $(CXXLINK) -rpath $(libdir) $(libtorrent_la_LDFLAGS) $(libtorrent_la_OBJECTS) $(libtorrent_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/alert.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/allocate_resources.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bandwidth_manager.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bt_peer_connection.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/closest_nodes.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dht_tracker.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/entry.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/escape_string.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file_pool.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/find_data.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/http_tracker_connection.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/identify_client.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ip_filter.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/logger.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/metadata_transfer.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/node.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/node_id.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/peer_connection.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/piece_picker.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/policy.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refresh.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/routing_table.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rpc_manager.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/session.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/session_impl.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sha1.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stat.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/storage.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/torrent.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/torrent_handle.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/torrent_info.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tracker_manager.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/traversal_algorithm.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/udp_tracker_connection.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ut_pex.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/web_peer_connection.Plo@am__quote@ + +.cpp.o: +@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ $< + +.cpp.obj: +@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.cpp.lo: +@am__fastdepCXX_TRUE@ if $(LTCXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Plo"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LTCXXCOMPILE) -c -o $@ $< + +closest_nodes.lo: kademlia/closest_nodes.cpp +@am__fastdepCXX_TRUE@ if $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT closest_nodes.lo -MD -MP -MF "$(DEPDIR)/closest_nodes.Tpo" -c -o closest_nodes.lo `test -f 'kademlia/closest_nodes.cpp' || echo '$(srcdir)/'`kademlia/closest_nodes.cpp; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/closest_nodes.Tpo" "$(DEPDIR)/closest_nodes.Plo"; else rm -f "$(DEPDIR)/closest_nodes.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='kademlia/closest_nodes.cpp' object='closest_nodes.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o closest_nodes.lo `test -f 'kademlia/closest_nodes.cpp' || echo '$(srcdir)/'`kademlia/closest_nodes.cpp + +dht_tracker.lo: kademlia/dht_tracker.cpp +@am__fastdepCXX_TRUE@ if $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT dht_tracker.lo -MD -MP -MF "$(DEPDIR)/dht_tracker.Tpo" -c -o dht_tracker.lo `test -f 'kademlia/dht_tracker.cpp' || echo '$(srcdir)/'`kademlia/dht_tracker.cpp; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/dht_tracker.Tpo" "$(DEPDIR)/dht_tracker.Plo"; else rm -f "$(DEPDIR)/dht_tracker.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='kademlia/dht_tracker.cpp' object='dht_tracker.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o dht_tracker.lo `test -f 'kademlia/dht_tracker.cpp' || echo '$(srcdir)/'`kademlia/dht_tracker.cpp + +find_data.lo: kademlia/find_data.cpp +@am__fastdepCXX_TRUE@ if $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT find_data.lo -MD -MP -MF "$(DEPDIR)/find_data.Tpo" -c -o find_data.lo `test -f 'kademlia/find_data.cpp' || echo '$(srcdir)/'`kademlia/find_data.cpp; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/find_data.Tpo" "$(DEPDIR)/find_data.Plo"; else rm -f "$(DEPDIR)/find_data.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='kademlia/find_data.cpp' object='find_data.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o find_data.lo `test -f 'kademlia/find_data.cpp' || echo '$(srcdir)/'`kademlia/find_data.cpp + +node.lo: kademlia/node.cpp +@am__fastdepCXX_TRUE@ if $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT node.lo -MD -MP -MF "$(DEPDIR)/node.Tpo" -c -o node.lo `test -f 'kademlia/node.cpp' || echo '$(srcdir)/'`kademlia/node.cpp; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/node.Tpo" "$(DEPDIR)/node.Plo"; else rm -f "$(DEPDIR)/node.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='kademlia/node.cpp' object='node.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o node.lo `test -f 'kademlia/node.cpp' || echo '$(srcdir)/'`kademlia/node.cpp + +node_id.lo: kademlia/node_id.cpp +@am__fastdepCXX_TRUE@ if $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT node_id.lo -MD -MP -MF "$(DEPDIR)/node_id.Tpo" -c -o node_id.lo `test -f 'kademlia/node_id.cpp' || echo '$(srcdir)/'`kademlia/node_id.cpp; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/node_id.Tpo" "$(DEPDIR)/node_id.Plo"; else rm -f "$(DEPDIR)/node_id.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='kademlia/node_id.cpp' object='node_id.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o node_id.lo `test -f 'kademlia/node_id.cpp' || echo '$(srcdir)/'`kademlia/node_id.cpp + +refresh.lo: kademlia/refresh.cpp +@am__fastdepCXX_TRUE@ if $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT refresh.lo -MD -MP -MF "$(DEPDIR)/refresh.Tpo" -c -o refresh.lo `test -f 'kademlia/refresh.cpp' || echo '$(srcdir)/'`kademlia/refresh.cpp; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/refresh.Tpo" "$(DEPDIR)/refresh.Plo"; else rm -f "$(DEPDIR)/refresh.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='kademlia/refresh.cpp' object='refresh.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o refresh.lo `test -f 'kademlia/refresh.cpp' || echo '$(srcdir)/'`kademlia/refresh.cpp + +routing_table.lo: kademlia/routing_table.cpp +@am__fastdepCXX_TRUE@ if $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT routing_table.lo -MD -MP -MF "$(DEPDIR)/routing_table.Tpo" -c -o routing_table.lo `test -f 'kademlia/routing_table.cpp' || echo '$(srcdir)/'`kademlia/routing_table.cpp; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/routing_table.Tpo" "$(DEPDIR)/routing_table.Plo"; else rm -f "$(DEPDIR)/routing_table.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='kademlia/routing_table.cpp' object='routing_table.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o routing_table.lo `test -f 'kademlia/routing_table.cpp' || echo '$(srcdir)/'`kademlia/routing_table.cpp + +rpc_manager.lo: kademlia/rpc_manager.cpp +@am__fastdepCXX_TRUE@ if $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT rpc_manager.lo -MD -MP -MF "$(DEPDIR)/rpc_manager.Tpo" -c -o rpc_manager.lo `test -f 'kademlia/rpc_manager.cpp' || echo '$(srcdir)/'`kademlia/rpc_manager.cpp; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/rpc_manager.Tpo" "$(DEPDIR)/rpc_manager.Plo"; else rm -f "$(DEPDIR)/rpc_manager.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='kademlia/rpc_manager.cpp' object='rpc_manager.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o rpc_manager.lo `test -f 'kademlia/rpc_manager.cpp' || echo '$(srcdir)/'`kademlia/rpc_manager.cpp + +traversal_algorithm.lo: kademlia/traversal_algorithm.cpp +@am__fastdepCXX_TRUE@ if $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT traversal_algorithm.lo -MD -MP -MF "$(DEPDIR)/traversal_algorithm.Tpo" -c -o traversal_algorithm.lo `test -f 'kademlia/traversal_algorithm.cpp' || echo '$(srcdir)/'`kademlia/traversal_algorithm.cpp; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/traversal_algorithm.Tpo" "$(DEPDIR)/traversal_algorithm.Plo"; else rm -f "$(DEPDIR)/traversal_algorithm.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='kademlia/traversal_algorithm.cpp' object='traversal_algorithm.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o traversal_algorithm.lo `test -f 'kademlia/traversal_algorithm.cpp' || echo '$(srcdir)/'`kademlia/traversal_algorithm.cpp + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +distclean-libtool: + -rm -f libtool +uninstall-info-am: + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$tags $$unique; \ + fi +ctags: CTAGS +CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(CTAGS_ARGS)$$tags$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$tags $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + $(mkdir_p) $(distdir)/../include/libtorrent $(distdir)/../include/libtorrent/aux_ $(distdir)/../include/libtorrent/extensions + @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \ + list='$(DISTFILES)'; for file in $$list; do \ + case $$file in \ + $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \ + $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \ + esac; \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + dir="/$$dir"; \ + $(mkdir_p) "$(distdir)$$dir"; \ + else \ + dir=''; \ + fi; \ + if test -d $$d/$$file; then \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ + fi; \ + cp -pR $$d/$$file $(distdir)$$dir || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(LTLIBRARIES) $(HEADERS) +installdirs: + for dir in "$(DESTDIR)$(libdir)"; do \ + test -z "$$dir" || $(mkdir_p) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \ + mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-libtool distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +info: info-am + +info-am: + +install-data-am: + +install-exec-am: install-libLTLIBRARIES + +install-info: install-info-am + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-info-am uninstall-libLTLIBRARIES + +.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \ + clean-libLTLIBRARIES clean-libtool ctags distclean \ + distclean-compile distclean-generic distclean-libtool \ + distclean-tags distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am install-exec \ + install-exec-am install-info install-info-am \ + install-libLTLIBRARIES install-man install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags uninstall uninstall-am uninstall-info-am \ + uninstall-libLTLIBRARIES + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/encryption/libtorrent/src/alert.cpp b/encryption/libtorrent/src/alert.cpp new file mode 100755 index 000000000..b990db7e1 --- /dev/null +++ b/encryption/libtorrent/src/alert.cpp @@ -0,0 +1,126 @@ +/* + +Copyright (c) 2003, Arvid Norberg, Daniel Wallin +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/pch.hpp" + +#include "libtorrent/alert.hpp" + +namespace libtorrent { + + alert::alert(severity_t severity, const std::string& msg) + : m_msg(msg) + , m_severity(severity) + , m_timestamp(time_now()) + { + } + + alert::~alert() + { + } + + ptime alert::timestamp() const + { + return m_timestamp; + } + + const std::string& alert::msg() const + { + return m_msg; + } + + alert::severity_t alert::severity() const + { + return m_severity; + } + + + + alert_manager::alert_manager() + : m_severity(alert::none) + {} + + alert_manager::~alert_manager() + { + while (!m_alerts.empty()) + { + delete m_alerts.front(); + m_alerts.pop(); + } + } + + void alert_manager::post_alert(const alert& alert_) + { + boost::mutex::scoped_lock lock(m_mutex); + if (m_severity > alert_.severity()) return; + + // the internal limit is 100 alerts + if (m_alerts.size() == 100) + { + alert* result = m_alerts.front(); + m_alerts.pop(); + delete result; + } + m_alerts.push(alert_.clone().release()); + } + + std::auto_ptr alert_manager::get() + { + boost::mutex::scoped_lock lock(m_mutex); + + assert(!m_alerts.empty()); + + alert* result = m_alerts.front(); + m_alerts.pop(); + return std::auto_ptr(result); + } + + bool alert_manager::pending() const + { + boost::mutex::scoped_lock lock(m_mutex); + + return !m_alerts.empty(); + } + + void alert_manager::set_severity(alert::severity_t severity) + { + boost::mutex::scoped_lock lock(m_mutex); + + m_severity = severity; + } + + bool alert_manager::should_post(alert::severity_t severity) const + { + return severity >= m_severity; + } + +} // namespace libtorrent + diff --git a/encryption/libtorrent/src/allocate_resources.cpp b/encryption/libtorrent/src/allocate_resources.cpp new file mode 100644 index 000000000..deef06dc4 --- /dev/null +++ b/encryption/libtorrent/src/allocate_resources.cpp @@ -0,0 +1,225 @@ +/* + +Copyright (c) 2006, Magnus Jonsson, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +//The Standard Library defines the two template functions std::min() +//and std::max() in the header. In general, you should +//use these template functions for calculating the min and max values +//of a pair. Unfortunately, Visual C++ does not define these function +// templates. This is because the names min and max clash with +//the traditional min and max macros defined in . +//As a workaround, Visual C++ defines two alternative templates with +//identical functionality called _cpp_min() and _cpp_max(). You can +//use them instead of std::min() and std::max().To disable the +//generation of the min and max macros in Visual C++, #define +//NOMINMAX before #including . + +#include "libtorrent/pch.hpp" + +#ifdef _WIN32 + //support boost1.32.0(2004-11-19 18:47) + //now all libs can be compiled and linked with static module + #define NOMINMAX +#endif + +#include "libtorrent/allocate_resources.hpp" +#include "libtorrent/size_type.hpp" +#include "libtorrent/peer_connection.hpp" +#include "libtorrent/torrent.hpp" +#include "libtorrent/aux_/allocate_resources_impl.hpp" + +#include +#include +#include + +#if defined(_MSC_VER) && _MSC_VER < 1310 +#define for if (false) {} else for +#else +#include +#endif + +namespace libtorrent +{ + int saturated_add(int a, int b) + { + assert(a >= 0); + assert(b >= 0); + assert(a <= resource_request::inf); + assert(b <= resource_request::inf); + assert(resource_request::inf + resource_request::inf < 0); + + unsigned int sum = unsigned(a) + unsigned(b); + if (sum > unsigned(resource_request::inf)) + sum = resource_request::inf; + + assert(sum >= unsigned(a) && sum >= unsigned(b)); + return int(sum); + } + +#if defined(_MSC_VER) && _MSC_VER < 1310 + + namespace detail + { + struct iterator_wrapper + { + typedef std::map >::iterator orig_iter; + + orig_iter iter; + + iterator_wrapper(orig_iter i): iter(i) {} + void operator++() { ++iter; } + torrent& operator*() { return *(iter->second); } + bool operator==(const iterator_wrapper& i) const + { return iter == i.iter; } + bool operator!=(const iterator_wrapper& i) const + { return iter != i.iter; } + }; + + struct iterator_wrapper2 + { + typedef std::map::iterator orig_iter; + + orig_iter iter; + + iterator_wrapper2(orig_iter i): iter(i) {} + void operator++() { ++iter; } + peer_connection& operator*() { return *(iter->second); } + bool operator==(const iterator_wrapper2& i) const + { return iter == i.iter; } + bool operator!=(const iterator_wrapper2& i) const + { return iter != i.iter; } + }; + + } + + void allocate_resources( + int resources + , std::map >& c + , resource_request torrent::* res) + { + aux::allocate_resources_impl( + resources + , detail::iterator_wrapper(c.begin()) + , detail::iterator_wrapper(c.end()) + , res); + } + + void allocate_resources( + int resources + , std::map& c + , resource_request peer_connection::* res) + { + aux::allocate_resources_impl( + resources + , detail::iterator_wrapper2(c.begin()) + , detail::iterator_wrapper2(c.end()) + , res); + } + +#else + + namespace aux + { + peer_connection& pick_peer( + std::pair + , boost::intrusive_ptr > const& p) + { + return *p.second; + } + + peer_connection& pick_peer2( + std::pair const& p) + { + return *p.second; + } + + torrent& deref(std::pair > const& p) + { + return *p.second; + } + + session& deref(session* p) + { + return *p; + } + } + + void allocate_resources( + int resources + , std::map >& c + , resource_request torrent::* res) + { + typedef std::map >::iterator orig_iter; + typedef std::pair > in_param; + typedef boost::transform_iterator new_iter; + + aux::allocate_resources_impl( + resources + , new_iter(c.begin(), &aux::deref) + , new_iter(c.end(), &aux::deref) + , res); + } + + void allocate_resources( + int resources + , std::map& c + , resource_request peer_connection::* res) + { + typedef std::map::iterator orig_iter; + typedef std::pair in_param; + typedef boost::transform_iterator new_iter; + + aux::allocate_resources_impl( + resources + , new_iter(c.begin(), &aux::pick_peer2) + , new_iter(c.end(), &aux::pick_peer2) + , res); + } + + void allocate_resources( + int resources + , std::vector& _sessions + , resource_request session::* res) + { + typedef std::vector::iterator orig_iter; + typedef session* in_param; + typedef boost::transform_iterator new_iter; + + aux::allocate_resources_impl( + resources + , new_iter(_sessions.begin(), &aux::deref) + , new_iter(_sessions.end(), &aux::deref) + , res); + } + +#endif + +} // namespace libtorrent diff --git a/encryption/libtorrent/src/bandwidth_manager.cpp b/encryption/libtorrent/src/bandwidth_manager.cpp new file mode 100644 index 000000000..19dc6fcf7 --- /dev/null +++ b/encryption/libtorrent/src/bandwidth_manager.cpp @@ -0,0 +1,249 @@ +/* + +Copyright (c) 2007, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/pch.hpp" + +#include "libtorrent/invariant_check.hpp" +#include "libtorrent/bandwidth_manager.hpp" +#include "libtorrent/peer_connection.hpp" +#include "libtorrent/time.hpp" + +#if defined TORRENT_LOGGING || defined TORRENT_VERBOSE_LOGGING +#include "libtorrent/aux_/session_impl.hpp" +#endif + +namespace libtorrent +{ + namespace + { + const time_duration window_size = seconds(1); + } + + history_entry::history_entry(intrusive_ptr p + , weak_ptr t, int a, ptime exp) + : expires_at(exp), amount(a), peer(p), tor(t) + {} + + bw_queue_entry::bw_queue_entry(intrusive_ptr const& pe + , bool no_prio) + : peer(pe), non_prioritized(no_prio) + {} + + bandwidth_manager::bandwidth_manager(io_service& ios, int channel) + : m_ios(ios) + , m_history_timer(m_ios) + , m_limit(bandwidth_limit::inf) + , m_current_quota(0) + , m_channel(channel) + {} + + void bandwidth_manager::request_bandwidth(intrusive_ptr peer + , bool non_prioritized) + { + INVARIANT_CHECK; + + assert(!peer->ignore_bandwidth_limits()); + + // make sure this peer isn't already in line + // waiting for bandwidth +#ifndef NDEBUG + for (std::deque::iterator i = m_queue.begin() + , end(m_queue.end()); i != end; ++i) + { + assert(i->peer < peer || peer < i->peer); + } +#endif + + assert(peer->max_assignable_bandwidth(m_channel) > 0); + + // if the queue is empty, we have to push the new + // peer at the back of it. If the peer is non-prioritized + // it is not supposed to cut in fron of anybody, so then + // we also just add it at the end + if (m_queue.empty() || non_prioritized) + { + m_queue.push_back(bw_queue_entry(peer, non_prioritized)); + } + else + { + // skip forward in the queue until we find a prioritized peer + // or hit the front of it. + std::deque::reverse_iterator i = m_queue.rbegin(); + while (i != m_queue.rend() && i->non_prioritized) ++i; + m_queue.insert(i.base(), bw_queue_entry(peer, non_prioritized)); + } + if (m_queue.size() == 1) hand_out_bandwidth(); + } + + +#ifndef NDEBUG + void bandwidth_manager::check_invariant() const + { + int current_quota = 0; + for (std::deque::const_iterator i + = m_history.begin(), end(m_history.end()); i != end; ++i) + { + current_quota += i->amount; + } + + assert(current_quota == m_current_quota); + } +#endif + + void bandwidth_manager::add_history_entry(history_entry const& e) try + { + INVARIANT_CHECK; +#if defined TORRENT_LOGGING || defined TORRENT_VERBOSE_LOGGING +// (*m_ses->m_logger) << "bw history [" << m_channel << "]\n"; +#endif + + m_history.push_front(e); + m_current_quota += e.amount; + // in case the size > 1 there is already a timer + // active that will be invoked, no need to set one up + if (m_history.size() > 1) return; + + m_history_timer.expires_at(e.expires_at); + m_history_timer.async_wait(bind(&bandwidth_manager::on_history_expire, this, _1)); + } + catch (std::exception&) { assert(false); } + + void bandwidth_manager::on_history_expire(asio::error_code const& e) try + { + INVARIANT_CHECK; + + if (e) return; + +#if defined TORRENT_LOGGING || defined TORRENT_VERBOSE_LOGGING +// (*m_ses->m_logger) << "bw expire [" << m_channel << "]\n"; +#endif + + assert(!m_history.empty()); + + ptime now(time_now()); + while (!m_history.empty() && m_history.back().expires_at <= now) + { + history_entry e = m_history.back(); + m_history.pop_back(); + m_current_quota -= e.amount; + assert(m_current_quota >= 0); + intrusive_ptr c = e.peer; + shared_ptr t = e.tor.lock(); + if (!c->is_disconnecting()) c->expire_bandwidth(m_channel, e.amount); + if (t) t->expire_bandwidth(m_channel, e.amount); + } + + // now, wait for the next chunk to expire + if (!m_history.empty()) + { + m_history_timer.expires_at(m_history.back().expires_at); + m_history_timer.async_wait(bind(&bandwidth_manager::on_history_expire, this, _1)); + } + + // since some bandwidth just expired, it + // means we can hand out more (in case there + // are still consumers in line) + if (!m_queue.empty()) hand_out_bandwidth(); + } + catch (std::exception&) + { + assert(false); + }; + + void bandwidth_manager::hand_out_bandwidth() try + { + INVARIANT_CHECK; +#if defined TORRENT_LOGGING || defined TORRENT_VERBOSE_LOGGING +// (*m_ses->m_logger) << "hand out bw [" << m_channel << "]\n"; +#endif + + ptime now(time_now()); + + mutex_t::scoped_lock l(m_mutex); + int limit = m_limit; + l.unlock(); + + // available bandwidth to hand out + int amount = limit - m_current_quota; + + int bandwidth_block_size_limit = max_bandwidth_block_size; + if (m_queue.size() > 3 && bandwidth_block_size_limit > limit / int(m_queue.size())) + bandwidth_block_size_limit = std::max(max_bandwidth_block_size / int(m_queue.size() - 3) + , min_bandwidth_block_size); + + while (!m_queue.empty() && amount > 0) + { + assert(amount == limit - m_current_quota); + bw_queue_entry qe = m_queue.front(); + m_queue.pop_front(); + + shared_ptr t = qe.peer->associated_torrent().lock(); + if (!t) continue; + if (qe.peer->is_disconnecting()) + { + t->expire_bandwidth(m_channel, -1); + continue; + } + + // at this point, max_assignable may actually be zero. Since + // the bandwidth quota is subtracted once the data has been + // send. If the peer was added to the queue while the data was + // still being sent, max_assignable may have been > 0 at that time. + int max_assignable = qe.peer->max_assignable_bandwidth(m_channel); + if (max_assignable == 0) + { + t->expire_bandwidth(m_channel, -1); + continue; + } + // don't hand out chunks larger than the throttle + // per second on the torrent + if (max_assignable > t->bandwidth_throttle(m_channel)) + max_assignable = t->bandwidth_throttle(m_channel); + + // so, hand out max_assignable, but no more than + // the available bandwidth (amount) and no more + // than the max_bandwidth_block_size + int single_amount = std::min(amount + , std::min(bandwidth_block_size_limit + , max_assignable)); + assert(single_amount > 0); + amount -= single_amount; + qe.peer->assign_bandwidth(m_channel, single_amount); + t->assign_bandwidth(m_channel, single_amount); + add_history_entry(history_entry(qe.peer, t, single_amount, now + window_size)); + } + } + catch (std::exception& e) + { assert(false); }; + +} + diff --git a/encryption/libtorrent/src/bt_peer_connection.cpp b/encryption/libtorrent/src/bt_peer_connection.cpp new file mode 100755 index 000000000..0a677bc7a --- /dev/null +++ b/encryption/libtorrent/src/bt_peer_connection.cpp @@ -0,0 +1,2260 @@ +/* + +Copyright (c) 2003 - 2006, Arvid Norberg +Copyright (c) 2007, 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/pch.hpp" + +#include +#include +#include +#include +#include + +#include "libtorrent/bt_peer_connection.hpp" +#include "libtorrent/session.hpp" +#include "libtorrent/identify_client.hpp" +#include "libtorrent/entry.hpp" +#include "libtorrent/bencode.hpp" +#include "libtorrent/alert_types.hpp" +#include "libtorrent/invariant_check.hpp" +#include "libtorrent/io.hpp" +#include "libtorrent/version.hpp" +#include "libtorrent/extensions.hpp" +#include "libtorrent/aux_/session_impl.hpp" + +#ifndef TORRENT_DISABLE_ENCRYPTION +#include "libtorrent/pe_crypto.hpp" +#include "libtorrent/hasher.hpp" +#endif + +using boost::bind; +using boost::shared_ptr; +using libtorrent::aux::session_impl; + +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, 0, 0, 0, 0, 0, 0, 0, + &bt_peer_connection::on_extended + }; + + + bt_peer_connection::bt_peer_connection( + session_impl& ses + , boost::weak_ptr tor + , shared_ptr s + , tcp::endpoint const& remote + , policy::peer* peerinfo) + : peer_connection(ses, tor, s, remote + , peerinfo) + , m_state(read_protocol_identifier) +#ifndef TORRENT_DISABLE_EXTENSIONS + , m_supports_extensions(false) +#endif + , m_supports_dht_port(false) +#ifndef TORRENT_DISABLE_ENCRYPTION + , m_encrypted(false) + , m_rc4_encrypted(false) + , m_sync_bytes_read(0) + , m_enc_send_buffer(0, 0) +#endif +#ifndef NDEBUG + , m_sent_bitfield(false) + , m_in_constructor(true) +#endif + { +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << "*** bt_peer_connection\n"; +#endif + +#ifndef TORRENT_DISABLE_ENCRYPTION + + pe_settings::enc_policy const& out_enc_policy = m_ses.get_pe_settings().out_enc_policy; + + if (out_enc_policy == pe_settings::forced) + { + write_pe1_2_dhkey(); + + m_state = read_pe_dhkey; + reset_recv_buffer(dh_key_len); + setup_receive(); + } + else if (out_enc_policy == pe_settings::enabled) + { + assert(peer_info_struct()); + + policy::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 = !(pi->pe_support); + + write_pe1_2_dhkey(); + m_state = read_pe_dhkey; + reset_recv_buffer(dh_key_len); + setup_receive(); + } + else // pi->pe_support == false + { + // toggled back to false if standard handshake + // completes correctly (without encryption) + pi->pe_support = !(pi->pe_support); + + write_handshake(); + reset_recv_buffer(20); + setup_receive(); + } + } + else if (out_enc_policy == pe_settings::disabled) +#endif + { + write_handshake(); + + // start in the state where we are trying to read the + // handshake from the other side + reset_recv_buffer(20); + setup_receive(); + } + +#ifndef NDEBUG + m_in_constructor = false; +#endif + } + + bt_peer_connection::bt_peer_connection( + session_impl& ses + , boost::shared_ptr s + , policy::peer* peerinfo) + : peer_connection(ses, s, peerinfo) + , m_state(read_protocol_identifier) +#ifndef TORRENT_DISABLE_EXTENSIONS + , m_supports_extensions(false) +#endif + , m_supports_dht_port(false) +#ifndef TORRENT_DISABLE_ENCRYPTION + , m_encrypted(false) + , m_rc4_encrypted(false) + , m_sync_bytes_read(0) + , m_enc_send_buffer(0, 0) +#endif +#ifndef NDEBUG + , m_sent_bitfield(false) + , m_in_constructor(true) +#endif + { + + // we are not attached to any torrent yet. + // we have to wait for the handshake to see + // which torrent the connector want's to connect to + + + // upload bandwidth will only be given to connections + // that are part of a torrent. Since this is an incoming + // connection, we have to give it some initial bandwidth + // to send the handshake. +#ifndef TORRENT_DISABLE_ENCRYPTION + m_bandwidth_limit[download_channel].assign(2048); + m_bandwidth_limit[upload_channel].assign(2048); +#else + m_bandwidth_limit[download_channel].assign(80); + m_bandwidth_limit[upload_channel].assign(80); +#endif + + // start in the state where we are trying to read the + // handshake from the other side + reset_recv_buffer(20); + setup_receive(); +#ifndef NDEBUG + m_in_constructor = false; +#endif + } + + bt_peer_connection::~bt_peer_connection() + { + } + + void bt_peer_connection::on_metadata() + { + boost::shared_ptr t = associated_torrent().lock(); + assert(t); + write_bitfield(t->pieces()); + } + + void bt_peer_connection::write_dht_port(int listen_port) + { + INVARIANT_CHECK; +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << time_now_string() + << " ==> DHT_PORT [ " << listen_port << " ]\n"; +#endif + buffer::interval packet = allocate_send_buffer(7); + detail::write_uint32(3, packet.begin); + detail::write_uint8(msg_dht_port, packet.begin); + detail::write_uint16(listen_port, packet.begin); + assert(packet.begin == packet.end); + setup_send(); + } + + void bt_peer_connection::get_specific_peer_info(peer_info& p) const + { + 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_local()) p.flags |= peer_info::local_connection; + +#ifndef TORRENT_DISABLE_ENCRYPTION + if (m_encrypted) + { + m_rc4_encrypted ? + p.flags |= peer_info::rc4_encrypted : + p.flags |= peer_info::plaintext_encrypted; + } +#endif + + if (!is_connecting() && in_handshake()) + p.flags |= peer_info::handshake; + if (is_connecting() && !is_queued()) p.flags |= peer_info::connecting; + if (is_queued()) p.flags |= peer_info::queued; + + 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; + } + +#ifndef TORRENT_DISABLE_ENCRYPTION + + void bt_peer_connection::write_pe1_2_dhkey() + { + assert(!m_encrypted); + assert(!m_rc4_encrypted); + assert(!m_DH_key_exchange.get()); + +#ifdef TORRENT_VERBOSE_LOGGING + if (is_local()) + (*m_logger) << " initiating encrypted handshake\n"; +#endif + + m_DH_key_exchange.reset(new DH_key_exchange); + + int pad_size = std::rand() % 512; + buffer::interval send_buf = allocate_send_buffer(dh_key_len + pad_size); + + std::copy (m_DH_key_exchange->get_local_key(), + m_DH_key_exchange->get_local_key() + dh_key_len, + send_buf.begin); + + std::generate(send_buf.begin + dh_key_len, send_buf.end, std::rand); + setup_send(); + +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << " sent DH key\n"; +#endif + } + + void bt_peer_connection::write_pe3_sync() + { + assert (!m_encrypted); + assert (!m_rc4_encrypted); + assert (is_local()); + + boost::shared_ptr t = associated_torrent().lock(); + 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 = 0; // rand() % 512; // Keep 0 for now + + // synchash,skeyhash,vc,crypto_provide,len(pad),pad,len(ia) + buffer::interval send_buf = + allocate_send_buffer (20 + 20 + 8 + 4 + 2 + pad_size + 2); + + // sync hash (hash('req1',S)) + h.reset(); + h.update("req1",4); + h.update(secret, dh_key_len); + sha1_hash sync_hash = h.final(); + + std::copy (sync_hash.begin(), sync_hash.end(), send_buf.begin); + send_buf.begin += 20; + + // stream key obfuscated hash [ hash('req2',SKEY) xor hash('req3',S) ] + h.reset(); + h.update("req2",4); + h.update((const char*)info_hash.begin(), 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; + + std::copy (obfsc_hash.begin(), obfsc_hash.end(), send_buf.begin); + send_buf.begin += 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 + assert(send_buf.left() == 8 + 4 + 2 + pad_size + 2); + int encrypt_size = send_buf.left(); + + int crypto_provide = 0; + pe_settings::enc_level const& allowed_enc_level = m_ses.get_pe_settings().allowed_enc_level; + + if (allowed_enc_level == pe_settings::both) + crypto_provide = 0x03; + else if (allowed_enc_level == pe_settings::rc4) + crypto_provide = 0x02; + else if (allowed_enc_level == pe_settings::plaintext) + crypto_provide = 0x01; + +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << " crypto provide : [ "; + if (allowed_enc_level == pe_settings::both) + (*m_logger) << "plaintext rc4 ]\n"; + else if (allowed_enc_level == pe_settings::rc4) + (*m_logger) << "rc4 ]\n"; + else if (allowed_enc_level == pe_settings::plaintext) + (*m_logger) << "plaintext ]\n"; +#endif + + write_pe_vc_cryptofield(send_buf, crypto_provide, pad_size); + m_RC4_handler->encrypt(send_buf.end - encrypt_size, encrypt_size); + + assert(send_buf.begin == send_buf.end); + setup_send(); + } + + void bt_peer_connection::write_pe4_sync(int crypto_select) + { + assert(!is_local()); + assert(!m_encrypted); + assert(!m_rc4_encrypted); + assert(crypto_select == 0x02 || crypto_select == 0x01); + + int pad_size = 0; // rand() % 512; // Keep 0 for now + + const int buf_size = 8+4+2+pad_size; + buffer::interval send_buf = allocate_send_buffer(buf_size); + write_pe_vc_cryptofield(send_buf, crypto_select, pad_size); + + m_RC4_handler->encrypt(send_buf.end - buf_size, buf_size); + setup_send(); + + // encryption method has been negotiated + if (crypto_select == 0x02) + m_rc4_encrypted = true; + else // 0x01 + m_rc4_encrypted = false; + +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << " crypto select : [ "; + if (crypto_select == 0x01) + (*m_logger) << "plaintext ]\n"; + else + (*m_logger) << "rc4 ]\n"; +#endif + } + + void bt_peer_connection::write_pe_vc_cryptofield(buffer::interval& write_buf, + int crypto_field, int pad_size) + { + assert(crypto_field <= 0x03 && crypto_field > 0); + assert(pad_size == 0); // pad not used yet + // vc,crypto_field,len(pad),pad, (len(ia)) + assert( (write_buf.left() == 8+4+2+pad_size+2 && is_local()) || + (write_buf.left() == 8+4+2+pad_size && !is_local()) ); + + // encrypt(vc, crypto_provide/select, len(Pad), len(IA)) + // len(pad) is zero for now, len(IA) only for outgoing connections + + // vc + std::fill(write_buf.begin, write_buf.begin + 8, 0); + write_buf.begin += 8; + + detail::write_uint32(crypto_field, write_buf.begin); + detail::write_uint16(pad_size, write_buf.begin); // len (pad) + + // fill pad with zeroes + // std::fill(write_buf.begin, write_buf.begin+pad_size, 0); + // write_buf.begin += pad_size; + + // append len(ia) if we are initiating + if (is_local()) + detail::write_uint16(handshake_len, write_buf.begin); // len(IA) + + assert (write_buf.begin == write_buf.end); + } + + void bt_peer_connection::init_pe_RC4_handler(char const* secret, sha1_hash const& stream_key) + { + assert(secret); + + hasher h; + const char keyA[] = "keyA"; + const char keyB[] = "keyB"; + + // encryption rc4 longkeys + // outgoing connection : hash ('keyA',S,SKEY) + // incoming connection : hash ('keyB',S,SKEY) + + is_local() ? h.update(keyA, 4) : h.update(keyB, 4); + h.update(secret, dh_key_len); + h.update((char const*)stream_key.begin(), 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) + + is_local() ? h.update(keyB, 4) : h.update(keyA, 4); + h.update(secret, dh_key_len); + h.update((char const*)stream_key.begin(), 20); + const sha1_hash remote_key = h.final(); + + assert(!m_RC4_handler.get()); + m_RC4_handler.reset (new RC4_handler (local_key, remote_key)); + +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << " computed RC4 keys\n"; +#endif + } + + void bt_peer_connection::send_buffer(char* begin, char* end) + { + assert (begin); + assert (end); + assert (end > begin); + + if (m_rc4_encrypted && m_encrypted) + m_RC4_handler->encrypt(begin, end - begin); + + peer_connection::send_buffer(begin, end); + } + + buffer::interval bt_peer_connection::allocate_send_buffer(int size) + { + if (m_rc4_encrypted && m_encrypted) + { + m_enc_send_buffer = peer_connection::allocate_send_buffer(size); + return m_enc_send_buffer; + } + else + { + buffer::interval i = peer_connection::allocate_send_buffer(size); + return i; + } + } + + void bt_peer_connection::setup_send() + { + if (m_rc4_encrypted && m_encrypted) + { + assert (m_enc_send_buffer.begin); + assert (m_enc_send_buffer.end); + assert (m_enc_send_buffer.left() > 0); + + m_RC4_handler->encrypt (m_enc_send_buffer.begin, m_enc_send_buffer.left()); + } + peer_connection::setup_send(); + } + + int bt_peer_connection::get_syncoffset(char const* src, int src_size, + char const* target, int target_size) const + { + assert (target_size >= src_size); + assert (src_size > 0); + assert (src); + 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; + } +#endif // #ifndef TORRENT_DISABLE_ENCRYPTION + + void bt_peer_connection::write_handshake() + { + INVARIANT_CHECK; + + boost::shared_ptr t = associated_torrent().lock(); + assert(t); + + // add handshake to the send buffer + const char version_string[] = "BitTorrent protocol"; + const int string_len = sizeof(version_string)-1; + + buffer::interval i = allocate_send_buffer(1 + string_len + 8 + 20 + 20); + // length of version string + *i.begin = string_len; + ++i.begin; + + // version string itself + std::copy( + version_string + , version_string + string_len + , i.begin); + i.begin += string_len; + + // 8 zeroes + std::fill(i.begin, i.begin + 8, 0); + +#ifndef TORRENT_DISABLE_DHT + // indicate that we support the DHT messages + *(i.begin + 7) = 0x01; +#endif + +#ifndef TORRENT_DISABLE_EXTENSIONS + // we support extensions + *(i.begin + 5) = 0x10; +#endif + + i.begin += 8; + + // info hash + sha1_hash const& ih = t->torrent_file().info_hash(); + std::copy(ih.begin(), ih.end(), i.begin); + i.begin += 20; + + // peer id + std::copy( + m_ses.get_peer_id().begin() + , m_ses.get_peer_id().end() + , i.begin); + i.begin += 20; + assert(i.begin == i.end); + +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << time_now_string() << " ==> HANDSHAKE\n"; +#endif + setup_send(); + } + + boost::optional bt_peer_connection::downloading_piece_progress() const + { + boost::shared_ptr t = associated_torrent().lock(); + assert(t); + + buffer::const_interval recv_buffer = receive_buffer(); + // are we currently receiving a 'piece' message? + if (m_state != read_packet + || recv_buffer.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 = 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; + +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << time_now_string() << " <== KEEPALIVE\n"; +#endif + incoming_keepalive(); + } + + // ----------------------------- + // ----------- CHOKE ----------- + // ----------------------------- + + void bt_peer_connection::on_choke(int received) + { + INVARIANT_CHECK; + + assert(received > 0); + if (packet_size() != 1) + throw protocol_error("'choke' message size != 1"); + m_statistics.received_bytes(0, received); + if (!packet_finished()) return; + + incoming_choke(); + } + + // ----------------------------- + // ---------- UNCHOKE ---------- + // ----------------------------- + + void bt_peer_connection::on_unchoke(int received) + { + INVARIANT_CHECK; + + assert(received > 0); + if (packet_size() != 1) + throw protocol_error("'unchoke' message size != 1"); + m_statistics.received_bytes(0, received); + if (!packet_finished()) return; + + incoming_unchoke(); + } + + // ----------------------------- + // -------- INTERESTED --------- + // ----------------------------- + + void bt_peer_connection::on_interested(int received) + { + INVARIANT_CHECK; + + assert(received > 0); + if (packet_size() != 1) + throw protocol_error("'interested' message size != 1"); + m_statistics.received_bytes(0, received); + if (!packet_finished()) return; + + incoming_interested(); + } + + // ----------------------------- + // ------ NOT INTERESTED ------- + // ----------------------------- + + void bt_peer_connection::on_not_interested(int received) + { + INVARIANT_CHECK; + + assert(received > 0); + if (packet_size() != 1) + throw protocol_error("'not interested' message size != 1"); + m_statistics.received_bytes(0, received); + if (!packet_finished()) return; + + incoming_not_interested(); + } + + // ----------------------------- + // ----------- HAVE ------------ + // ----------------------------- + + void bt_peer_connection::on_have(int received) + { + INVARIANT_CHECK; + + assert(received > 0); + if (packet_size() != 5) + throw protocol_error("'have' message size != 5"); + m_statistics.received_bytes(0, received); + if (!packet_finished()) return; + + buffer::const_interval recv_buffer = receive_buffer(); + + const char* ptr = recv_buffer.begin + 1; + int index = detail::read_int32(ptr); + + incoming_have(index); + } + + // ----------------------------- + // --------- BITFIELD ---------- + // ----------------------------- + + void bt_peer_connection::on_bitfield(int received) + { + INVARIANT_CHECK; + + assert(received > 0); + + boost::shared_ptr t = associated_torrent().lock(); + assert(t); + + // if we don't have the metedata, we cannot + // verify the bitfield size + if (t->valid_metadata() + && packet_size() - 1 != ((int)get_bitfield().size() + 7) / 8) + throw protocol_error("bitfield with invalid size"); + + m_statistics.received_bytes(0, received); + if (!packet_finished()) return; + + buffer::const_interval recv_buffer = receive_buffer(); + + std::vector bitfield; + + if (!t->valid_metadata()) + bitfield.resize((packet_size() - 1) * 8); + else + bitfield.resize(get_bitfield().size()); + + // if we don't have metadata yet + // just remember the bitmask + // don't update the piecepicker + // (since it doesn't exist yet) + for (int i = 0; i < (int)bitfield.size(); ++i) + bitfield[i] = (recv_buffer[1 + (i>>3)] & (1 << (7 - (i&7)))) != 0; + + incoming_bitfield(bitfield); + } + + // ----------------------------- + // ---------- REQUEST ---------- + // ----------------------------- + + void bt_peer_connection::on_request(int received) + { + INVARIANT_CHECK; + + assert(received > 0); + if (packet_size() != 13) + throw protocol_error("'request' message size != 13"); + m_statistics.received_bytes(0, received); + if (!packet_finished()) return; + + buffer::const_interval recv_buffer = receive_buffer(); + + peer_request r; + const char* ptr = recv_buffer.begin + 1; + r.piece = detail::read_int32(ptr); + r.start = detail::read_int32(ptr); + r.length = detail::read_int32(ptr); + + incoming_request(r); + } + + // ----------------------------- + // ----------- PIECE ----------- + // ----------------------------- + + void bt_peer_connection::on_piece(int received) + { + INVARIANT_CHECK; + + assert(received > 0); + + buffer::const_interval recv_buffer = receive_buffer(); + int recv_pos = recv_buffer.end - recv_buffer.begin; + + // classify the received data as protocol chatter + // or data payload for the statistics + if (recv_pos <= 9) + // only received protocol data + m_statistics.received_bytes(0, received); + else if (recv_pos - received >= 9) + // only received payload data + m_statistics.received_bytes(received, 0); + else + { + // received a bit of both + assert(recv_pos - received < 9); + assert(recv_pos > 9); + assert(9 - (recv_pos - received) <= 9); + m_statistics.received_bytes( + recv_pos - 9 + , 9 - (recv_pos - received)); + } + + incoming_piece_fragment(); + if (!packet_finished()) return; + + const char* ptr = recv_buffer.begin + 1; + peer_request p; + p.piece = detail::read_int32(ptr); + p.start = detail::read_int32(ptr); + p.length = packet_size() - 9; + + incoming_piece(p, recv_buffer.begin + 9); + } + + // ----------------------------- + // ---------- CANCEL ----------- + // ----------------------------- + + void bt_peer_connection::on_cancel(int received) + { + INVARIANT_CHECK; + + assert(received > 0); + if (packet_size() != 13) + throw protocol_error("'cancel' message size != 13"); + m_statistics.received_bytes(0, received); + if (!packet_finished()) return; + + buffer::const_interval recv_buffer = receive_buffer(); + + peer_request r; + const char* ptr = recv_buffer.begin + 1; + r.piece = detail::read_int32(ptr); + r.start = detail::read_int32(ptr); + r.length = detail::read_int32(ptr); + + incoming_cancel(r); + } + + // ----------------------------- + // --------- DHT PORT ---------- + // ----------------------------- + + void bt_peer_connection::on_dht_port(int received) + { + INVARIANT_CHECK; + + assert(received > 0); + if (packet_size() != 3) + throw protocol_error("'dht_port' message size != 3"); + m_statistics.received_bytes(0, received); + if (!packet_finished()) return; + + buffer::const_interval recv_buffer = receive_buffer(); + + const char* ptr = recv_buffer.begin + 1; + int listen_port = detail::read_uint16(ptr); + + incoming_dht_port(listen_port); + } + + // ----------------------------- + // --------- EXTENDED ---------- + // ----------------------------- + + void bt_peer_connection::on_extended(int received) + { + INVARIANT_CHECK; + + assert(received > 0); + m_statistics.received_bytes(0, received); + if (packet_size() < 2) + throw protocol_error("'extended' message smaller than 2 bytes"); + + if (associated_torrent().expired()) + throw protocol_error("'extended' message sent before proper handshake"); + + buffer::const_interval recv_buffer = receive_buffer(); + if (recv_buffer.left() < 2) return; + + 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(); + return; + } + +#ifndef TORRENT_DISABLE_EXTENSIONS + for (extension_list_t::iterator i = m_extensions.begin() + , end(m_extensions.end()); i != end; ++i) + { + if ((*i)->on_extended(packet_size() - 2, extended_id + , recv_buffer)) + return; + } +#endif + + throw protocol_error("unknown extended message id: " + + boost::lexical_cast(extended_id)); + } + + void bt_peer_connection::on_extended_handshake() + { + if (!packet_finished()) return; + + boost::shared_ptr t = associated_torrent().lock(); + assert(t); + + buffer::const_interval recv_buffer = receive_buffer(); + + entry root; + try + { + root = bdecode(recv_buffer.begin + 2, recv_buffer.end); + } + catch (std::exception& exc) + { +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << "invalid extended handshake: " << exc.what() << "\n"; +#endif + return; + } + +#ifdef TORRENT_VERBOSE_LOGGING + std::stringstream ext; + root.print(ext); + (*m_logger) << "<== EXTENDED HANDSHAKE: \n" << ext.str(); +#endif + +#ifndef TORRENT_DISABLE_EXTENSIONS + for (extension_list_t::iterator i = m_extensions.begin() + , end(m_extensions.end()); i != 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; + } +#endif + + // there is supposed to be a remote listen port + if (entry* listen_port = root.find_key("p")) + { + if (listen_port->type() == entry::int_t) + { + tcp::endpoint adr(remote().address() + , (unsigned short)listen_port->integer()); + t->get_policy().peer_from_tracker(adr, pid(), 0, 0); + } + } + // there should be a version too + // but where do we put that info? + + if (entry* client_info = root.find_key("v")) + { + if (client_info->type() == entry::string_t) + m_client_version = client_info->string(); + } + + if (entry* reqq = root.find_key("reqq")) + { + if (reqq->type() == entry::int_t) + m_max_out_request_queue = reqq->integer(); + if (m_max_out_request_queue < 1) + m_max_out_request_queue = 1; + } + } + + bool bt_peer_connection::dispatch_message(int received) + { + INVARIANT_CHECK; + + assert(received > 0); + + // this means the connection has been closed already + if (associated_torrent().expired()) return false; + + buffer::const_interval recv_buffer = receive_buffer(); + + int packet_type = recv_buffer[0]; + if (packet_type < 0 + || packet_type >= num_supported_messages + || m_message_handler[packet_type] == 0) + { +#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(packet_size(), packet_type + , buffer::const_interval(recv_buffer.begin+1 + , recv_buffer.end))) + return packet_finished(); + } +#endif + + throw protocol_error("unknown message id: " + + boost::lexical_cast(packet_type) + + " size: " + boost::lexical_cast(packet_size())); + } + + assert(m_message_handler[packet_type] != 0); + + // call the correct handler for this packet type + (this->*m_message_handler[packet_type])(received); + + return packet_finished(); + } + + void bt_peer_connection::write_keepalive() + { + INVARIANT_CHECK; + + char buf[] = {0,0,0,0}; + send_buffer(buf, buf + sizeof(buf)); + } + + void bt_peer_connection::write_cancel(peer_request const& r) + { + INVARIANT_CHECK; + + assert(associated_torrent().lock()->valid_metadata()); + + char buf[] = {0,0,0,13, msg_cancel}; + + buffer::interval i = allocate_send_buffer(17); + + std::copy(buf, buf + 5, i.begin); + i.begin += 5; + + // index + detail::write_int32(r.piece, i.begin); + // begin + detail::write_int32(r.start, i.begin); + // length + detail::write_int32(r.length, i.begin); + assert(i.begin == i.end); + + setup_send(); + } + + void bt_peer_connection::write_request(peer_request const& r) + { + INVARIANT_CHECK; + + assert(associated_torrent().lock()->valid_metadata()); + + char buf[] = {0,0,0,13, msg_request}; + + buffer::interval i = allocate_send_buffer(17); + + std::copy(buf, buf + 5, i.begin); + i.begin += 5; + + // index + detail::write_int32(r.piece, i.begin); + // begin + detail::write_int32(r.start, i.begin); + // length + detail::write_int32(r.length, i.begin); + assert(i.begin == i.end); + + setup_send(); + } + + void bt_peer_connection::write_bitfield(std::vector const& bitfield) + { + INVARIANT_CHECK; + + boost::shared_ptr t = associated_torrent().lock(); + assert(t); + assert(m_sent_bitfield == false); + assert(t->valid_metadata()); + + int num_pieces = bitfield.size(); + int lazy_pieces[50]; + int num_lazy_pieces = 0; + int lazy_piece = 0; + + assert(t->is_seed() == (std::count(bitfield.begin(), bitfield.end(), true) == num_pieces)); + if (t->is_seed() && m_ses.settings().lazy_bitfields) + { + 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 (rand() % (num_pieces - i) >= num_lazy_pieces - lazy_piece) continue; + lazy_pieces[lazy_piece++] = i; + } + assert(lazy_piece == num_lazy_pieces); + lazy_piece = 0; + } + +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << time_now_string() << " ==> BITFIELD "; + + std::stringstream bitfield_string; + for (int i = 0; i < (int)get_bitfield().size(); ++i) + { + if (lazy_piece < num_lazy_pieces + && lazy_pieces[lazy_piece] == i) + { + bitfield_string << "0"; + ++lazy_piece; + continue; + } + if (bitfield[i]) bitfield_string << "1"; + else bitfield_string << "0"; + } + bitfield_string << "\n"; + (*m_logger) << bitfield_string.str(); + lazy_piece = 0; +#endif + const int packet_size = (num_pieces + 7) / 8 + 5; + + buffer::interval i = allocate_send_buffer(packet_size); + + detail::write_int32(packet_size - 4, i.begin); + detail::write_uint8(msg_bitfield, i.begin); + + std::fill(i.begin, i.end, 0); + for (int c = 0; c < num_pieces; ++c) + { + if (lazy_piece < num_lazy_pieces + && lazy_pieces[lazy_piece]) + { + ++lazy_piece; + continue; + } + if (bitfield[c]) + i.begin[c >> 3] |= 1 << (7 - (c & 7)); + } + assert(i.end - i.begin == (num_pieces + 7) / 8); + + if (num_lazy_pieces > 0) + { + for (int i = 0; i < num_lazy_pieces; ++i) + { + write_have(lazy_pieces[i]); +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << time_now_string() + << " ==> HAVE [ piece: " << lazy_pieces[i] << "]\n"; +#endif + } + } + +#ifndef NDEBUG + m_sent_bitfield = true; +#endif + setup_send(); + } + +#ifndef TORRENT_DISABLE_EXTENSIONS + void bt_peer_connection::write_extensions() + { + INVARIANT_CHECK; + +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << time_now_string() << " ==> EXTENSIONS\n"; +#endif + assert(m_supports_extensions); + + entry handshake(entry::dictionary_t); + entry extension_list(entry::dictionary_t); + + handshake["m"] = extension_list; + + // only send the port in case we bade the connection + // on incoming connections the other end already knows + // our listen port + if (is_local()) handshake["p"] = m_ses.listen_port(); + handshake["v"] = m_ses.settings().user_agent; + std::string remote_address; + std::back_insert_iterator out(remote_address); + detail::write_address(remote().address(), out); + handshake["ip"] = remote_address; + handshake["reqq"] = m_ses.settings().max_allowed_in_request_queue; + + // 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); + } + + std::vector msg; + bencode(std::back_inserter(msg), handshake); + + // make room for message + buffer::interval i = allocate_send_buffer(6 + msg.size()); + + // write the length of the message + detail::write_int32((int)msg.size() + 2, i.begin); + detail::write_uint8(msg_extended, i.begin); + // signal handshake message + detail::write_uint8(0, i.begin); + + std::copy(msg.begin(), msg.end(), i.begin); + i.begin += msg.size(); + assert(i.begin == i.end); + +#ifdef TORRENT_VERBOSE_LOGGING + std::stringstream ext; + handshake.print(ext); + (*m_logger) << "==> EXTENDED HANDSHAKE: \n" << ext.str(); +#endif + + setup_send(); + } +#endif + + void bt_peer_connection::write_choke() + { + INVARIANT_CHECK; + + if (is_choked()) return; + char msg[] = {0,0,0,1,msg_choke}; + send_buffer(msg, msg + sizeof(msg)); + } + + void bt_peer_connection::write_unchoke() + { + INVARIANT_CHECK; + + char msg[] = {0,0,0,1,msg_unchoke}; + send_buffer(msg, msg + sizeof(msg)); + } + + void bt_peer_connection::write_interested() + { + INVARIANT_CHECK; + + char msg[] = {0,0,0,1,msg_interested}; + send_buffer(msg, msg + sizeof(msg)); + } + + void bt_peer_connection::write_not_interested() + { + INVARIANT_CHECK; + + char msg[] = {0,0,0,1,msg_not_interested}; + send_buffer(msg, msg + sizeof(msg)); + } + + void bt_peer_connection::write_have(int index) + { + assert(associated_torrent().lock()->valid_metadata()); + assert(index >= 0); + assert(index < associated_torrent().lock()->torrent_file().num_pieces()); + INVARIANT_CHECK; + + const int packet_size = 9; + char msg[packet_size] = {0,0,0,5,msg_have}; + char* ptr = msg + 5; + detail::write_int32(index, ptr); + send_buffer(msg, msg + packet_size); + } + + void bt_peer_connection::write_piece(peer_request const& r) + { + INVARIANT_CHECK; + + const int packet_size = 4 + 5 + 4 + r.length; + + boost::shared_ptr t = associated_torrent().lock(); + assert(t); + + buffer::interval i = allocate_send_buffer(packet_size); + + detail::write_int32(packet_size-4, i.begin); + detail::write_uint8(msg_piece, i.begin); + detail::write_int32(r.piece, i.begin); + detail::write_int32(r.start, i.begin); + + t->filesystem().read( + i.begin, r.piece, r.start, r.length); + + assert(i.begin + r.length == i.end); + + m_payloads.push_back(range(send_buffer_size() - r.length, r.length)); + setup_send(); + } + + namespace + { + struct match_peer_id + { + match_peer_id(peer_id const& id, peer_connection const* pc) + : m_id(id), m_pc(pc) + { assert(pc); } + + bool operator()(policy::peer const& p) const + { + return p.connection != m_pc + && p.connection + && p.connection->pid() == m_id + && !p.connection->pid().is_all_zeros(); + } + + peer_id m_id; + peer_connection const* m_pc; + }; + } + + // -------------------------- + // RECEIVE DATA + // -------------------------- + + // throws exception when the client should be disconnected + void bt_peer_connection::on_receive(asio::error_code const& error + , std::size_t bytes_transferred) + { + INVARIANT_CHECK; + + if (error) return; + boost::shared_ptr t = associated_torrent().lock(); + + if (in_handshake()) + m_statistics.received_bytes(0, bytes_transferred); + +#ifndef TORRENT_DISABLE_ENCRYPTION + if (m_rc4_encrypted && m_encrypted) + { + buffer::interval wr_buf = wr_recv_buffer(); + m_RC4_handler->decrypt((wr_buf.end - bytes_transferred), bytes_transferred); + } +#endif + + buffer::const_interval recv_buffer = receive_buffer(); + +#ifndef TORRENT_DISABLE_ENCRYPTION + // 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) + { + assert (!m_encrypted); + assert (!m_rc4_encrypted); + assert (packet_size() == dh_key_len); + assert (recv_buffer == receive_buffer()); + + if (!packet_finished()) return; + + // write our dh public key. m_DH_key_exchange is + // initialized in write_pe1_2_dhkey() + if (!is_local()) + write_pe1_2_dhkey(); + + // read dh key, generate shared secret + m_DH_key_exchange->compute_secret (recv_buffer.begin); // TODO handle errors + +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << " received DH key\n"; +#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_local()) + { + 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. + m_rc4_encrypted = true; + m_encrypted = true; + write_handshake(); + m_rc4_encrypted = false; + m_encrypted = false; + + // vc,crypto_select,len(pad),pad, encrypt(handshake) + // 8+4+2+0+handshake_len + reset_recv_buffer(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) + reset_recv_buffer(20+20+8+4+2+0+handshake_len); + } + assert(!packet_finished()); + return; + } + + // cannot fall through into + if (m_state == read_pe_synchash) + { + assert(!m_encrypted); + assert(!m_rc4_encrypted); + assert(!is_local()); + assert(recv_buffer == receive_buffer()); + + if (recv_buffer.left() < 20) + { + if (packet_finished()) + { + throw protocol_error ("sync hash not found"); + } + // else + return; + } + + if (!m_sync_hash.get()) + { + 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 sha1_hash(h.final())); + } + + int syncoffset = get_syncoffset((char*)m_sync_hash->begin(), 20 + , recv_buffer.begin, recv_buffer.left()); + + // No sync + if (syncoffset == -1) + { + std::size_t bytes_processed = recv_buffer.left() - 20; + m_sync_bytes_read += bytes_processed; + if (m_sync_bytes_read >= 512) + throw protocol_error("sync hash not found within 532 bytes"); + + cut_receive_buffer(bytes_processed, std::min(packet_size(), (512+20) - m_sync_bytes_read)); + + assert(!packet_finished()); + return; + } + // found complete sync + else + { + std::size_t bytes_processed = syncoffset + 20; +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << " sync point (hash) found at offset " + << m_sync_bytes_read + bytes_processed - 20 << "\n"; +#endif + m_state = read_pe_skey_vc; + // skey,vc - 28 bytes + m_sync_hash.reset(); + cut_receive_buffer(bytes_processed, 28); + } + } + + if (m_state == read_pe_skey_vc) + { + assert(!m_encrypted); + assert(!m_rc4_encrypted); + assert(!is_local()); + assert(packet_size() == 28); + + if (!packet_finished()) return; + + recv_buffer = receive_buffer(); + + // only calls info_hash() on the torrent_handle's, which + // never throws. + session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); + + std::vector active_torrents = m_ses.get_torrents(); + std::vector::const_iterator i; + hasher h; + sha1_hash skey_hash, obfs_hash; + + for (i = active_torrents.begin(); i != active_torrents.end(); ++i) + { + torrent_handle const& t_h = *i; // TODO possible errors + sha1_hash const& info_hash = t_h.info_hash(); + // TODO Does info_hash need to be checked for validity? + + h.reset(); + h.update("req2", 4); + h.update((char*)info_hash.begin(), 20); + + skey_hash = h.final(); + + h.reset(); + h.update("req3", 4); + h.update(m_DH_key_exchange->get_secret(), dh_key_len); + + obfs_hash = h.final(); + obfs_hash ^= skey_hash; + + if (std::equal (recv_buffer.begin, recv_buffer.begin + 20, + (char*)obfs_hash.begin())) + { + if (!t) + { + attach_to_torrent(info_hash); + t = associated_torrent().lock(); + assert(t); + } + + init_pe_RC4_handler(m_DH_key_exchange->get_secret(), info_hash); +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << " stream key found, torrent located.\n"; +#endif + continue; // TODO Check flow control with multiple torrents + } + } + + if (!m_RC4_handler.get()) + throw protocol_error("invalid streamkey identifier (info hash) in encrypted handshake"); + + // verify constant + buffer::interval wr_recv_buf = wr_recv_buffer(); + m_RC4_handler->decrypt(wr_recv_buf.begin + 20, 8); + wr_recv_buf.begin += 28; + + const char sh_vc[] = {0,0,0,0, 0,0,0,0}; + if (!std::equal(sh_vc, sh_vc+8, recv_buffer.begin + 20)) + { + throw protocol_error("unable to verify constant"); + } + +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << " verification constant found\n"; +#endif + m_state = read_pe_cryptofield; + reset_recv_buffer(4 + 2); + } + + // cannot fall through into + if (m_state == read_pe_syncvc) + { + assert(is_local()); + assert(!m_encrypted); + assert(!m_rc4_encrypted); + assert(recv_buffer == receive_buffer()); + + if (recv_buffer.left() < 8) + { + if (packet_finished()) + { + throw protocol_error ("sync verification constant not found"); + } + // else + return; + } + + // generate the verification constant + if (!m_sync_vc.get()) + { + assert(m_sync_bytes_read == 0); + + m_sync_vc.reset (new char[8]); + std::fill(m_sync_vc.get(), m_sync_vc.get() + 8, 0); + m_RC4_handler->decrypt(m_sync_vc.get(), 8); + } + + 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; + if (m_sync_bytes_read >= 512) + throw protocol_error("sync verification constant not found within 520 bytes"); + + cut_receive_buffer(bytes_processed, std::min(packet_size(), (512+8) - m_sync_bytes_read)); + + assert(!packet_finished()); + return; + } + // found complete sync + else + { + std::size_t bytes_processed = syncoffset + 8; +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << " sync point (verification constant) found at offset " + << m_sync_bytes_read + bytes_processed - 8 << "\n"; +#endif + cut_receive_buffer (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 + { + assert(!m_encrypted); + assert(!m_rc4_encrypted); + assert(packet_size() == 4+2); + + if (!packet_finished()) return; + + buffer::interval wr_buf = wr_recv_buffer(); + m_RC4_handler->decrypt(wr_buf.begin, packet_size()); + + recv_buffer = receive_buffer(); + + int crypto_field = detail::read_int32(recv_buffer.begin); + +#ifdef TORRENT_VERBOSE_LOGGING + if (!is_local()) + (*m_logger) << " crypto provide : [ "; + else + (*m_logger) << " crypto select : [ "; + + if (crypto_field & 0x01) + (*m_logger) << "plaintext "; + if (crypto_field & 0x02) + (*m_logger) << "rc4 "; + (*m_logger) << "]\n"; +#endif + + if (!is_local()) + { + int crypto_select = 0; + // select a crypto method + switch (m_ses.get_pe_settings().allowed_enc_level) + { + case (pe_settings::plaintext): + { + if (!(crypto_field & 0x01)) + throw protocol_error("plaintext not provided"); + crypto_select = 0x01; + } + break; + case (pe_settings::rc4): + { + if (!(crypto_field & 0x02)) + throw protocol_error("rc4 not provided"); + crypto_select = 0x02; + } + break; + case (pe_settings::both): + { + if (m_ses.get_pe_settings().prefer_rc4) + { + if (crypto_field & 0x02) + crypto_select = 0x02; + else if (crypto_field & 0x01) + crypto_select = 0x01; + } + else + { + if (crypto_field & 0x01) + crypto_select = 0x01; + else if (crypto_field & 0x02) + crypto_select = 0x02; + } + if (!crypto_select) + throw protocol_error("rc4/plaintext not provided"); + } + } // switch + + // write the pe4 step + write_pe4_sync(crypto_select); + } + else // is_local() + { + // check if crypto select is valid + pe_settings::enc_level const& allowed_enc_level = m_ses.get_pe_settings().allowed_enc_level; + + if (crypto_field == 0x02) + { + if (allowed_enc_level == pe_settings::plaintext) + throw protocol_error("rc4 selected by peer when not provided"); + m_rc4_encrypted = true; + } + else if (crypto_field == 0x01) + { + if (allowed_enc_level == pe_settings::rc4) + throw protocol_error("plaintext selected by peer when not provided"); + m_rc4_encrypted = false; + } + else + throw protocol_error("unsupported crypto method selected by peer"); + } + + int len_pad = detail::read_int16(recv_buffer.begin); + if (len_pad < 0 || len_pad > 512) + throw protocol_error("invalid pad length"); + + m_state = read_pe_pad; + if (!is_local()) + reset_recv_buffer(len_pad + 2); // len(IA) at the end of pad + else + { + if (len_pad == 0) + { + m_encrypted = true; + m_state = init_bt_handshake; + } + else + reset_recv_buffer(len_pad); + } + } + + if (m_state == read_pe_pad) + { + assert(!m_encrypted); + if (!packet_finished()) return; + + int pad_size = is_local() ? packet_size() : packet_size() - 2; + + buffer::interval wr_buf = wr_recv_buffer(); + m_RC4_handler->decrypt(wr_buf.begin, packet_size()); + + recv_buffer = receive_buffer(); + + if (!is_local()) + { + recv_buffer.begin += pad_size; + int len_ia = detail::read_int16(recv_buffer.begin); +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << " len(IA) : " << len_ia << "\n"; +#endif + if (len_ia == 0) + { + // everything after this is Encrypt2 + m_encrypted = true; + m_state = init_bt_handshake; + } + else + { + m_state = read_pe_ia; + reset_recv_buffer(len_ia); + } + } + else // is_local() + { + // everything that arrives after this is Encrypt2 + m_encrypted = true; + m_state = init_bt_handshake; + } + } + + if (m_state == read_pe_ia) + { + assert(!is_local()); + assert(!m_encrypted); + + if (!packet_finished()) return; + + // ia is always rc4, so decrypt it + buffer::interval wr_buf = wr_recv_buffer(); + m_RC4_handler->decrypt(wr_buf.begin, packet_size()); + +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << " decrypted ia : " << packet_size() << " bytes\n"; +#endif + + if (!m_rc4_encrypted) + { + m_RC4_handler.reset(); +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << " destroyed rc4 keys\n"; +#endif + } + + // everything that arrives after this is Encrypt2 + m_encrypted = true; + + m_state = read_protocol_identifier; + cut_receive_buffer(0, 20); + } + + if (m_state == init_bt_handshake) + { + assert(m_encrypted); + + // decrypt remaining received bytes + if (m_rc4_encrypted) + { + buffer::interval wr_buf = wr_recv_buffer(); + wr_buf.begin += packet_size(); + m_RC4_handler->decrypt(wr_buf.begin, wr_buf.left()); +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << " decrypted remaining " << wr_buf.left() << " bytes\n"; +#endif + } + else // !m_rc4_encrypted + { + m_RC4_handler.reset(); +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << " destroyed rc4 keys\n"; +#endif + } + + // payload stream, start with 20 handshake bytes + m_state = read_protocol_identifier; + reset_recv_buffer(20); + + // encrypted portion of handshake completed, toggle + // peer_info pe_support flag back to true + if (is_local() && + m_ses.get_pe_settings().out_enc_policy == pe_settings::enabled) + { + policy::peer* pi = peer_info_struct(); + assert(pi); + assert(pi->pe_support == false); + + pi->pe_support = !(pi->pe_support); + } + } + +#endif // #ifndef TORRENT_DISABLE_ENCRYPTION + + if (m_state == read_protocol_identifier) + { + assert (packet_size() == 20); + + if (!packet_finished()) return; + recv_buffer = receive_buffer(); + + int packet_size = recv_buffer[0]; + const char protocol_string[] = "BitTorrent protocol"; + + if (packet_size != 19 || + !std::equal(recv_buffer.begin + 1, recv_buffer.begin + 19, protocol_string)) + { +#ifndef TORRENT_DISABLE_ENCRYPTION + if (!is_local() && m_ses.get_pe_settings().in_enc_policy == pe_settings::disabled) + throw protocol_error("encrypted incoming connections disabled"); + + // Don't attempt to perform an encrypted handshake + // within an encrypted connection + if (!m_encrypted && !is_local()) + { +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << " attempting encrypted connection\n"; +#endif + m_state = read_pe_dhkey; + cut_receive_buffer(0, dh_key_len); + assert(!packet_finished()); + return; + } + + assert ((!is_local() && m_encrypted) || is_local()); +#endif // #ifndef TORRENT_DISABLE_ENCRYPTION + throw protocol_error("incorrect protocol identifier"); + } + +#ifndef TORRENT_DISABLE_ENCRYPTION + assert (m_state != read_pe_dhkey); + + if (!is_local() && + (m_ses.get_pe_settings().in_enc_policy == pe_settings::forced) && + !m_encrypted) + throw protocol_error("non encrypted incoming connections disabled"); +#endif + +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << " BitTorrent protocol\n"; +#endif + + m_state = read_info_hash; + reset_recv_buffer(28); + } + + // fall through + if (m_state == read_info_hash) + { + assert(packet_size() == 28); + + if (!packet_finished()) return; + recv_buffer = receive_buffer(); + + +#ifdef TORRENT_VERBOSE_LOGGING + for (int i=0; i < 8; ++i) + { + for (int j=0; j < 8; ++j) + { + if (recv_buffer[i] & (0x80 >> j)) (*m_logger) << "1"; + else (*m_logger) << "0"; + } + } + (*m_logger) << "\n"; + if (recv_buffer[7] & 0x01) + (*m_logger) << "supports DHT port message\n"; + if (recv_buffer[7] & 0x04) + (*m_logger) << "supports FAST extensions\n"; + if (recv_buffer[5] & 0x10) + (*m_logger) << "supports extensions protocol\n"; +#endif + +#ifndef DISABLE_EXTENSIONS + if ((recv_buffer[5] & 0x10)) + m_supports_extensions = true; +#endif + if (recv_buffer[7] & 0x01) + m_supports_dht_port = true; + + // ok, now we have got enough of the handshake. Is this connection + // attached to a torrent? + if (!t) + { + // now, we have to see if there's a torrent with the + // info_hash we got from the peer + sha1_hash info_hash; + std::copy(recv_buffer.begin + 8, recv_buffer.begin + 28 + , (char*)info_hash.begin()); + + attach_to_torrent(info_hash); + t = associated_torrent().lock(); + assert(t); + } + else + { + // verify info hash + if (!std::equal(recv_buffer.begin + 8, recv_buffer.begin + 28 + , (const char*)t->torrent_file().info_hash().begin())) + { +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << " received invalid info_hash\n"; +#endif + throw protocol_error("invalid info-hash in handshake"); + } + +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << " info_hash received\n"; +#endif + t = associated_torrent().lock(); + assert(t); + } + + // Respond with handshake and bitfield + if (!is_local()) + { + write_handshake(); + } + if (t->valid_metadata()) + write_bitfield(t->pieces()); + + assert(t->get_policy().has_connection(this)); + + m_state = read_peer_id; + reset_recv_buffer(20); + } + + // fall through + if (m_state == read_peer_id) + { + if (!t) + { + assert(!packet_finished()); // TODO + return; + } + assert(packet_size() == 20); + + if (!packet_finished()) return; + recv_buffer = receive_buffer(); + +#ifdef TORRENT_VERBOSE_LOGGING + { + peer_id tmp; + std::copy(recv_buffer.begin, recv_buffer.begin + 20, (char*)tmp.begin()); + std::stringstream s; + s << "received peer_id: " << tmp << " client: " << identify_client(tmp) << "\n"; + s << "as ascii: "; + for (peer_id::iterator i = tmp.begin(); i != tmp.end(); ++i) + { + if (std::isprint(*i)) s << *i; + else s << "."; + } + s << "\n"; + (*m_logger) << s.str(); + } +#endif + peer_id pid; + std::copy(recv_buffer.begin, recv_buffer.begin + 20, (char*)pid.begin()); + set_pid(pid); + + if (t->settings().allow_multiple_connections_per_ip) + { + // now, let's see if this connection should be closed + policy& p = t->get_policy(); + policy::iterator i = std::find_if(p.begin_peer(), p.end_peer() + , match_peer_id(pid, this)); + if (i != p.end_peer()) + { + assert(i->connection->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_ses.get_peer_id() && is_local()) + { + i->connection->disconnect(); + } + else + { + throw protocol_error("duplicate peer-id, connection closed"); + } + } + } + + if (pid == m_ses.get_peer_id()) + { + throw protocol_error("closing connection to ourself"); + } + +#ifndef TORRENT_DISABLE_DHT + if (m_supports_dht_port && m_ses.m_dht) + write_dht_port(m_ses.get_dht_settings().service_port); +#endif + + m_client_version = identify_client(pid); + boost::optional f = client_fingerprint(pid); + if (f && std::equal(f->name, f->name + 2, "BC")) + { + // if this is a bitcomet client, lower the request queue size limit + if (m_max_out_request_queue > 50) m_max_out_request_queue = 50; + } + + // disconnect if the peer has the same peer-id as ourself + // since it most likely is ourself then + if (pid == m_ses.get_peer_id()) + throw std::runtime_error("closing connection to ourself"); + +#ifndef TORRENT_DISABLE_EXTENSIONS + for (extension_list_t::iterator i = m_extensions.begin() + , end(m_extensions.end()); i != end;) + { + if (!(*i)->on_handshake()) + { + i = m_extensions.erase(i); + } + else + { + ++i; + } + } + + if (m_supports_extensions) write_extensions(); +#endif + +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << time_now_string() << " <== HANDSHAKE\n"; +#endif + // consider this a successful connection, reset the failcount + if (peer_info_struct()) peer_info_struct()->failcount = 0; + +#ifndef TORRENT_DISABLE_ENCRYPTION + // Toggle pe_support back to false if this is a + // standard successful connection + if (is_local() && !m_encrypted && + m_ses.get_pe_settings().out_enc_policy == pe_settings::enabled) + { + policy::peer* pi = peer_info_struct(); + assert(pi); + assert(pi->pe_support == true); + + pi->pe_support = !(pi->pe_support); + } +#endif + + m_state = read_packet_size; + reset_recv_buffer(4); + + assert(!packet_finished()); + return; + } + + // cannot fall through into + if (m_state == read_packet_size) + { + // Make sure this is not fallen though into + assert (recv_buffer == receive_buffer()); + + if (!t) return; + m_statistics.received_bytes(0, bytes_transferred); + if (!packet_finished()) return; + + const char* ptr = recv_buffer.begin; + int packet_size = detail::read_int32(ptr); + + // don't accept packets larger than 1 MB + if (packet_size > 1024*1024 || packet_size < 0) + { + // packet too large + throw std::runtime_error("packet > 1 MB (" + + boost::lexical_cast( + (unsigned int)packet_size) + " bytes)"); + } + + if (packet_size == 0) + { + incoming_keepalive(); + // keepalive message + m_state = read_packet_size; + reset_recv_buffer(4); + } + else + { + m_state = read_packet; + reset_recv_buffer(packet_size); + } + assert(!packet_finished()); + return; + } + + if (m_state == read_packet) + { + assert(recv_buffer == receive_buffer()); + if (!t) return; + if (dispatch_message(bytes_transferred)) + { + m_state = read_packet_size; + reset_recv_buffer(4); + } + assert(!packet_finished()); + return; + } + + assert(!packet_finished()); + } + + // -------------------------- + // SEND DATA + // -------------------------- + + // throws exception when the client should be disconnected + void bt_peer_connection::on_sent(asio::error_code const& error + , std::size_t bytes_transferred) + { + INVARIANT_CHECK; + + if (error) return; + + // manage the payload markers + int amount_payload = 0; + if (!m_payloads.empty()) + { + for (std::deque::iterator i = m_payloads.begin(); + i != m_payloads.end(); ++i) + { + i->start -= bytes_transferred; + if (i->start < 0) + { + if (i->start + i->length <= 0) + { + amount_payload += i->length; + } + else + { + amount_payload += -i->start; + i->length -= -i->start; + i->start = 0; + } + } + } + } + + // TODO: move the erasing into the loop above + // remove all payload ranges that has been sent + m_payloads.erase( + std::remove_if(m_payloads.begin(), m_payloads.end(), range_below_zero) + , m_payloads.end()); + + assert(amount_payload <= (int)bytes_transferred); + m_statistics.sent_bytes(amount_payload, bytes_transferred - amount_payload); + } + +#ifndef NDEBUG + void bt_peer_connection::check_invariant() const + { +#ifndef TORRENT_DISABLE_ENCRYPTION + assert(bool(m_state == read_pe_dhkey) == bool(m_DH_key_exchange) + || !is_local()); +#endif + if (!m_in_constructor) + peer_connection::check_invariant(); + + if (!m_payloads.empty()) + { + for (std::deque::const_iterator i = m_payloads.begin(); + i != m_payloads.end() - 1; ++i) + { + assert(i->start + i->length <= (i+1)->start); + } + } + } +#endif + +} + diff --git a/encryption/libtorrent/src/connection_queue.cpp b/encryption/libtorrent/src/connection_queue.cpp new file mode 100644 index 000000000..f83baa196 --- /dev/null +++ b/encryption/libtorrent/src/connection_queue.cpp @@ -0,0 +1,169 @@ + +/* + +Copyright (c) 2007, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include +#include "libtorrent/invariant_check.hpp" +#include "libtorrent/connection_queue.hpp" + +namespace libtorrent +{ + + connection_queue::connection_queue(io_service& ios): m_next_ticket(0) + , m_num_connecting(0) + , m_half_open_limit(0) + , m_timer(ios) + {} + + bool connection_queue::free_slots() const + { return m_num_connecting < m_half_open_limit || m_half_open_limit <= 0; } + + void connection_queue::enqueue(boost::function const& on_connect + , boost::function const& on_timeout + , time_duration timeout) + { + INVARIANT_CHECK; + + m_queue.push_back(entry()); + entry& e = m_queue.back(); + e.on_connect = on_connect; + e.on_timeout = on_timeout; + e.ticket = m_next_ticket; + e.timeout = timeout; + ++m_next_ticket; + try_connect(); + } + + void connection_queue::done(int ticket) + { + INVARIANT_CHECK; + + std::list::iterator i = std::find_if(m_queue.begin() + , m_queue.end(), boost::bind(&entry::ticket, _1) == ticket); + if (i == m_queue.end()) + { + // this might not be here in case on_timeout calls remove + return; + } + if (i->connecting) --m_num_connecting; + m_queue.erase(i); + try_connect(); + } + + void connection_queue::limit(int limit) + { m_half_open_limit = limit; } + + int connection_queue::limit() const + { return m_half_open_limit; } + +#ifndef NDEBUG + + void connection_queue::check_invariant() const + { + int num_connecting = 0; + for (std::list::const_iterator i = m_queue.begin(); + i != m_queue.end(); ++i) + { + if (i->connecting) ++num_connecting; + } + assert(num_connecting == m_num_connecting); + } + +#endif + + void connection_queue::try_connect() + { + INVARIANT_CHECK; + + if (!free_slots() || m_queue.empty()) + return; + + std::list::iterator i = std::find_if(m_queue.begin() + , m_queue.end(), boost::bind(&entry::connecting, _1) == false); + while (i != m_queue.end()) + { + assert(i->connecting == false); + ptime expire = time_now() + i->timeout; + if (m_num_connecting == 0) + { + m_timer.expires_at(expire); + m_timer.async_wait(boost::bind(&connection_queue::on_timeout, this, _1)); + } + i->connecting = true; + ++m_num_connecting; + i->expires = expire; + + INVARIANT_CHECK; + + entry& ent = *i; + ++i; + try { ent.on_connect(ent.ticket); } catch (std::exception&) {} + + if (!free_slots()) break; + i = std::find_if(i, m_queue.end(), boost::bind(&entry::connecting, _1) == false); + } + } + + void connection_queue::on_timeout(asio::error_code const& e) + { + INVARIANT_CHECK; + + assert(!e || e == asio::error::operation_aborted); + if (e) return; + + ptime next_expire = max_time(); + ptime now = time_now(); + for (std::list::iterator i = m_queue.begin(); + i != m_queue.end();) + { + if (i->connecting && i->expires < now) + { + boost::function on_timeout = i->on_timeout; + m_queue.erase(i++); + --m_num_connecting; + try { on_timeout(); } catch (std::exception&) {} + continue; + } + if (i->expires < next_expire) + next_expire = i->expires; + ++i; + } + if (next_expire < max_time()) + { + m_timer.expires_at(next_expire); + m_timer.async_wait(boost::bind(&connection_queue::on_timeout, this, _1)); + } + try_connect(); + } + +} + diff --git a/encryption/libtorrent/src/entry.cpp b/encryption/libtorrent/src/entry.cpp new file mode 100755 index 000000000..6506ed4c2 --- /dev/null +++ b/encryption/libtorrent/src/entry.cpp @@ -0,0 +1,344 @@ +/* + +Copyright (c) 2003, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/pch.hpp" + +#include +#include +#include "libtorrent/entry.hpp" +#include "libtorrent/config.hpp" + +#if defined(_MSC_VER) +namespace std +{ + using ::isprint; +} +#define for if (false) {} else for +#endif + +namespace +{ + template + void call_destructor(T* o) + { + assert(o); + o->~T(); + } + + struct compare_string + { + compare_string(char const* s): m_str(s) {} + + bool operator()( + std::pair const& e) const + { + return m_str && e.first == m_str; + } + char const* m_str; + }; +} + +namespace libtorrent +{ + namespace detail + { + TORRENT_EXPORT char const* integer_to_str(char* buf, int size, entry::integer_type val) + { + int sign = 0; + if (val < 0) + { + sign = 1; + val = -val; + } + buf[--size] = '\0'; + if (val == 0) buf[--size] = '0'; + for (; size > sign && val != 0;) + { + buf[--size] = '0' + char(val % 10); + val /= 10; + } + if (sign) buf[--size] = '-'; + return buf + size; + } + } + + entry& entry::operator[](char const* key) + { + dictionary_type::iterator i = dict().find(key); + if (i != dict().end()) return i->second; + dictionary_type::iterator ret = dict().insert( + dict().begin() + , std::make_pair(std::string(key), entry())); + return ret->second; + } + + + entry& entry::operator[](std::string const& key) + { + return (*this)[key.c_str()]; + } + + entry* entry::find_key(char const* key) + { + dictionary_type::iterator i = std::find_if( + dict().begin() + , dict().end() + , compare_string(key)); + if (i == dict().end()) return 0; + return &i->second; + + } + + entry const* entry::find_key(char const* key) const + { + dictionary_type::const_iterator i = dict().find(key); + if (i == dict().end()) return 0; + return &i->second; + } + + const entry& entry::operator[](char const* key) const + { + dictionary_type::const_iterator i = dict().find(key); + if (i == dict().end()) throw type_error( + (std::string("key not found: ") + key).c_str()); + return i->second; + } + + const entry& entry::operator[](std::string const& key) const + { + return (*this)[key.c_str()]; + } + + entry::entry(dictionary_type const& v) + { + new(data) dictionary_type(v); + m_type = dictionary_t; + } + + entry::entry(string_type const& v) + { + new(data) string_type(v); + m_type = string_t; + } + + entry::entry(list_type const& v) + { + new(data) list_type(v); + m_type = list_t; + } + + entry::entry(integer_type const& v) + { + new(data) integer_type(v); + m_type = int_t; + } + + void entry::operator=(dictionary_type const& v) + { + destruct(); + new(data) dictionary_type(v); + m_type = dictionary_t; + } + + void entry::operator=(string_type const& v) + { + destruct(); + new(data) string_type(v); + m_type = string_t; + } + + void entry::operator=(list_type const& v) + { + destruct(); + new(data) list_type(v); + m_type = list_t; + } + + void entry::operator=(integer_type const& v) + { + destruct(); + new(data) integer_type(v); + m_type = int_t; + } + + bool entry::operator==(entry const& e) const + { + if (m_type != e.m_type) return false; + + switch(m_type) + { + case int_t: + return integer() == e.integer(); + case string_t: + return string() == e.string(); + case list_t: + return list() == e.list(); + case dictionary_t: + return dict() == e.dict(); + default: + assert(m_type == undefined_t); + return true; + } + } + + void entry::construct(data_type t) + { + m_type = t; + switch(m_type) + { + case int_t: + new(data) integer_type; + break; + case string_t: + new(data) string_type; + break; + case list_t: + new(data) list_type; + break; + case dictionary_t: + new (data) dictionary_type; + break; + default: + assert(m_type == undefined_t); + m_type = undefined_t; + } + } + + void entry::copy(entry const& e) + { + m_type = e.m_type; + switch(m_type) + { + case int_t: + new(data) integer_type(e.integer()); + break; + case string_t: + new(data) string_type(e.string()); + break; + case list_t: + new(data) list_type(e.list()); + break; + case dictionary_t: + new (data) dictionary_type(e.dict()); + break; + default: + m_type = undefined_t; + } + } + + void entry::destruct() + { + switch(m_type) + { + case int_t: + call_destructor(reinterpret_cast(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; + default: + assert(m_type == undefined_t); + break; + } + } + + void entry::print(std::ostream& os, int indent) const + { + assert(indent >= 0); + for (int i = 0; i < indent; ++i) os << " "; + switch (m_type) + { + case int_t: + os << integer() << "\n"; + break; + case string_t: + { + bool binary_string = false; + for (std::string::const_iterator i = string().begin(); i != string().end(); ++i) + { + if (!std::isprint(static_cast(*i))) + { + binary_string = true; + break; + } + } + if (binary_string) + { + os.unsetf(std::ios_base::dec); + os.setf(std::ios_base::hex); + for (std::string::const_iterator i = string().begin(); i != string().end(); ++i) + os << std::setfill('0') << std::setw(2) + << static_cast((unsigned char)*i); + os.unsetf(std::ios_base::hex); + os.setf(std::ios_base::dec); + os << "\n"; + } + else + { + os << string() << "\n"; + } + } break; + case list_t: + { + os << "list\n"; + for (list_type::const_iterator i = list().begin(); i != list().end(); ++i) + { + i->print(os, indent+1); + } + } break; + case dictionary_t: + { + os << "dictionary\n"; + for (dictionary_type::const_iterator i = dict().begin(); i != dict().end(); ++i) + { + for (int j = 0; j < indent+1; ++j) os << " "; + os << "[" << i->first << "]"; + if (i->second.type() != entry::string_t + && i->second.type() != entry::int_t) + os << "\n"; + else os << " "; + i->second.print(os, indent+2); + } + } break; + default: + os << "\n"; + } + } +} + diff --git a/encryption/libtorrent/src/escape_string.cpp b/encryption/libtorrent/src/escape_string.cpp new file mode 100755 index 000000000..02a4fa085 --- /dev/null +++ b/encryption/libtorrent/src/escape_string.cpp @@ -0,0 +1,150 @@ +/* + +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 "libtorrent/pch.hpp" + +#include +#include +#include +#include +#include +#include +#include + +namespace libtorrent +{ + std::string unescape_string(std::string const& s) + { + std::string ret; + for (std::string::const_iterator i = s.begin(); i != s.end(); ++i) + { + if(*i == '+') + { + ret += ' '; + } + else if (*i != '%') + { + ret += *i; + } + else + { + ++i; + if (i == s.end()) + throw std::runtime_error("invalid escaped string"); + + int high; + if(*i >= '0' && *i <= '9') high = *i - '0'; + else if(*i >= 'A' && *i <= 'F') high = *i + 10 - 'A'; + else if(*i >= 'a' && *i <= 'f') high = *i + 10 - 'a'; + else throw std::runtime_error("invalid escaped string"); + + ++i; + if (i == s.end()) + throw std::runtime_error("invalid escaped string"); + + int low; + if(*i >= '0' && *i <= '9') low = *i - '0'; + else if(*i >= 'A' && *i <= 'F') low = *i + 10 - 'A'; + else if(*i >= 'a' && *i <= 'f') low = *i + 10 - 'a'; + else throw std::runtime_error("invalid escaped string"); + + ret += char(high * 16 + low); + } + } + return ret; + } + + + std::string escape_string(const char* str, int len) + { + assert(str != 0); + assert(len >= 0); + // http://www.ietf.org/rfc/rfc2396.txt + // section 2.3 + // some trackers seems to require that ' is escaped +// static const char unreserved_chars[] = "-_.!~*'()"; + static const char unreserved_chars[] = "-_.!~*()" + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" + "0123456789"; + + std::stringstream ret; + ret << std::hex << std::setfill('0'); + for (int i = 0; i < len; ++i) + { + if (std::count( + unreserved_chars + , unreserved_chars+sizeof(unreserved_chars)-1 + , *str)) + { + ret << *str; + } + else + { + ret << '%' + << std::setw(2) + << (int)static_cast(*str); + } + ++str; + } + return ret.str(); + } + + std::string escape_path(const char* str, int len) + { + assert(str != 0); + assert(len >= 0); + static const char unreserved_chars[] = "/-_.!~*()" + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" + "0123456789"; + + std::stringstream ret; + ret << std::hex << std::setfill('0'); + for (int i = 0; i < len; ++i) + { + if (std::count( + unreserved_chars + , unreserved_chars+sizeof(unreserved_chars)-1 + , *str)) + { + ret << *str; + } + else + { + ret << '%' + << std::setw(2) + << (int)static_cast(*str); + } + ++str; + } + return ret.str(); + } +} diff --git a/encryption/libtorrent/src/file.cpp b/encryption/libtorrent/src/file.cpp new file mode 100755 index 000000000..71bc795a4 --- /dev/null +++ b/encryption/libtorrent/src/file.cpp @@ -0,0 +1,347 @@ +/* + +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 "libtorrent/pch.hpp" + +#ifdef _WIN32 +// windows part +#include "libtorrent/utf8.hpp" + +#include +#include +#include +#include + +#ifndef _MODE_T_ +typedef int mode_t; +#endif + +#ifdef UNICODE +#include "libtorrent/storage.hpp" +#endif + +#else +// unix part +#define _FILE_OFFSET_BITS 64 +#include +#include +#include +#include +#include + +#include +// make sure the _FILE_OFFSET_BITS define worked +// on this platform +BOOST_STATIC_ASSERT(sizeof(lseek(0, 0, 0)) >= 8); + +#endif + +#include +#include "libtorrent/file.hpp" +#include + +#ifndef O_BINARY +#define O_BINARY 0 +#endif + +#ifndef O_RANDOM +#define O_RANDOM 0 +#endif + +#ifdef UNICODE +#include "libtorrent/storage.hpp" +#endif + + +namespace fs = boost::filesystem; + +namespace +{ + enum { mode_in = 1, mode_out = 2 }; + + mode_t map_open_mode(int m) + { + if (m == (mode_in | mode_out)) return O_RDWR | O_CREAT | O_BINARY | O_RANDOM; + if (m == mode_out) return O_WRONLY | O_CREAT | O_BINARY | O_RANDOM; + if (m == mode_in) return O_RDONLY | O_BINARY | O_RANDOM; + assert(false); + return 0; + } + +#ifdef WIN32 + std::string utf8_native(std::string const& s) + { + try + { + std::wstring ws; + libtorrent::utf8_wchar(s, ws); + std::size_t size = wcstombs(0, ws.c_str(), 0); + if (size == std::size_t(-1)) return s; + std::string ret; + ret.resize(size); + size = wcstombs(&ret[0], ws.c_str(), size + 1); + if (size == wchar_t(-1)) return s; + ret.resize(size); + return ret; + } + catch(std::exception) + { + return s; + } + } +#else + std::string utf8_native(std::string const& s) + { + return s; + } +#endif + +} + +namespace libtorrent +{ + + const file::open_mode file::in(mode_in); + const file::open_mode file::out(mode_out); + + const file::seek_mode file::begin(1); + const file::seek_mode file::end(2); + + struct file::impl + { + impl() + : m_fd(-1) + , m_open_mode(0) + {} + + impl(fs::path const& path, int mode) + : m_fd(-1) + , m_open_mode(0) + { + open(path, mode); + } + + ~impl() + { + close(); + } + + void open(fs::path const& path, int mode) + { + assert(path.is_complete()); + close(); +#if defined(_WIN32) && defined(UNICODE) + std::wstring wpath(safe_convert(path.native_file_string())); + m_fd = ::_wopen( + wpath.c_str() + , map_open_mode(mode) + , S_IREAD | S_IWRITE); +#else +#ifdef _WIN32 + m_fd = ::_open( +#else + m_fd = ::open( +#endif + utf8_native(path.native_file_string()).c_str() + , map_open_mode(mode) +#ifdef _WIN32 + , S_IREAD | S_IWRITE); +#else + , S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); +#endif +#endif + if (m_fd == -1) + { + std::stringstream msg; + msg << "open failed: '" << path.native_file_string() << "'. " + << strerror(errno); + throw file_error(msg.str()); + } + m_open_mode = mode; + } + + void close() + { + if (m_fd == -1) return; + +#ifdef _WIN32 + ::_close(m_fd); +#else + ::close(m_fd); +#endif + m_fd = -1; + m_open_mode = 0; + } + + size_type read(char* buf, size_type num_bytes) + { + assert(m_open_mode & mode_in); + assert(m_fd != -1); + +#ifdef _WIN32 + size_type ret = ::_read(m_fd, buf, num_bytes); +#else + size_type ret = ::read(m_fd, buf, num_bytes); +#endif + if (ret == -1) + { + std::stringstream msg; + msg << "read failed: " << strerror(errno); + throw file_error(msg.str()); + } + return ret; + } + + size_type write(const char* buf, size_type num_bytes) + { + assert(m_open_mode & mode_out); + assert(m_fd != -1); + + // TODO: Test this a bit more, what happens with random failures in + // the files? +// if ((rand() % 100) > 80) +// throw file_error("debug"); + +#ifdef _WIN32 + size_type ret = ::_write(m_fd, buf, num_bytes); +#else + size_type ret = ::write(m_fd, buf, num_bytes); +#endif + if (ret == -1) + { + std::stringstream msg; + msg << "write failed: " << strerror(errno); + throw file_error(msg.str()); + } + return ret; + } + + void set_size(size_type s) + { + size_type pos = tell(); + seek(1, 0); + char dummy = 0; + read(&dummy, 1); + seek(1, 0); + write(&dummy, 1); + seek(pos, 1); + } + + size_type seek(size_type offset, int m) + { + assert(m_open_mode); + assert(m_fd != -1); + + int seekdir = (m == 1)?SEEK_SET:SEEK_END; +#ifdef _WIN32 + size_type ret = _lseeki64(m_fd, offset, seekdir); +#else + size_type ret = lseek(m_fd, offset, seekdir); +#endif + + // For some strange reason this fails + // on win32. Use windows specific file + // wrapper instead. + if (ret == -1) + { + std::stringstream msg; + msg << "seek failed: '" << strerror(errno) + << "' fd: " << m_fd + << " offset: " << offset + << " seekdir: " << seekdir; + throw file_error(msg.str()); + } + return ret; + } + + size_type tell() + { + assert(m_open_mode); + assert(m_fd != -1); + +#ifdef _WIN32 + return _telli64(m_fd); +#else + return lseek(m_fd, 0, SEEK_CUR); +#endif + } + + int m_fd; + int m_open_mode; + }; + + // pimpl forwardings + + file::file() : m_impl(new impl()) {} + + file::file(boost::filesystem::path const& p, file::open_mode m) + : m_impl(new impl(p, m.m_mask)) + {} + + file::~file() {} + + void file::open(boost::filesystem::path const& p, file::open_mode m) + { + m_impl->open(p, m.m_mask); + } + + void file::close() + { + m_impl->close(); + } + + size_type file::write(const char* buf, size_type num_bytes) + { + return m_impl->write(buf, num_bytes); + } + + size_type file::read(char* buf, size_type num_bytes) + { + return m_impl->read(buf, num_bytes); + } + + void file::set_size(size_type s) + { + m_impl->set_size(s); + } + + size_type file::seek(size_type pos, file::seek_mode m) + { + return m_impl->seek(pos, m.m_val); + } + + size_type file::tell() + { + return m_impl->tell(); + } + +} diff --git a/encryption/libtorrent/src/file_pool.cpp b/encryption/libtorrent/src/file_pool.cpp new file mode 100644 index 000000000..ab4ea8d6c --- /dev/null +++ b/encryption/libtorrent/src/file_pool.cpp @@ -0,0 +1,134 @@ +/* + +Copyright (c) 2006, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/pch.hpp" + +#include "libtorrent/file_pool.hpp" + +#include + +namespace libtorrent +{ + using boost::multi_index::nth_index; + using boost::multi_index::get; + + boost::shared_ptr file_pool::open_file(void* st, fs::path const& p, file::open_mode m) + { + assert(st != 0); + assert(p.is_complete()); + assert(m == file::in || m == (file::in | file::out)); + boost::mutex::scoped_lock l(m_mutex); + typedef nth_index::type path_view; + path_view& pt = get<0>(m_files); + path_view::iterator i = pt.find(p); + if (i != pt.end()) + { + lru_file_entry e = *i; + e.last_use = time_now(); + + if (e.key != st) + { + // this means that another instance of the storage + // is using the exact same file. + throw file_error("torrent uses the same file as another torrent " + "(" + p.string() + ")"); + } + + e.key = st; + if ((e.mode & m) != m) + { + // close the file before we open it with + // the new read/write privilages + i->file_ptr.reset(); + assert(e.file_ptr.unique()); + e.file_ptr.reset(); + e.file_ptr.reset(new file(p, m)); + e.mode = m; + } + pt.replace(i, e); + return e.file_ptr; + } + // the file is not in our cache + if ((int)m_files.size() >= m_size) + { + // the file cache is at its maximum size, close + // the least recently used (lru) file from it + typedef nth_index::type lru_view; + lru_view& lt = get<1>(m_files); + lru_view::iterator i = lt.begin(); + // the first entry in this view is the least recently used + assert(lt.size() == 1 || (i->last_use <= boost::next(i)->last_use)); + lt.erase(i); + } + lru_file_entry e(boost::shared_ptr(new file(p, m))); + e.mode = m; + e.key = st; + e.file_path = p; + pt.insert(e); + return e.file_ptr; + } + + void file_pool::release(void* st) + { + boost::mutex::scoped_lock l(m_mutex); + assert(st != 0); + using boost::tie; + + typedef nth_index::type key_view; + key_view& kt = get<2>(m_files); + + key_view::iterator start, end; + tie(start, end) = kt.equal_range(st); + kt.erase(start, end); + } + + void file_pool::resize(int size) + { + assert(size > 0); + if (size == m_size) return; + boost::mutex::scoped_lock l(m_mutex); + m_size = size; + if (int(m_files.size()) <= m_size) return; + + // close the least recently used files + typedef nth_index::type lru_view; + lru_view& lt = get<1>(m_files); + lru_view::iterator i = lt.begin(); + while (int(m_files.size()) > m_size) + { + // the first entry in this view is the least recently used + assert(lt.size() == 1 || (i->last_use <= boost::next(i)->last_use)); + lt.erase(i++); + } + } + +} diff --git a/encryption/libtorrent/src/file_win.cpp b/encryption/libtorrent/src/file_win.cpp new file mode 100644 index 000000000..9d2c2f4bf --- /dev/null +++ b/encryption/libtorrent/src/file_win.cpp @@ -0,0 +1,366 @@ +/* + +Copyright (c) 2003, Magnus Jonsson & Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/file.hpp" +#include "libtorrent/utf8.hpp" + +#ifdef UNICODE +#include "libtorrent/storage.hpp" +#endif + +#include +#include +#include + +namespace +{ + // must be used to not leak memory in case something would throw + class auto_localfree + { + public: + auto_localfree(HLOCAL memory) + : m_memory(memory) + { + } + ~auto_localfree() + { + if (m_memory) + LocalFree(m_memory); + } + private: + HLOCAL m_memory; + }; + + std::string utf8_native(std::string const& s) + { + try + { + std::wstring ws; + libtorrent::utf8_wchar(s, ws); + std::size_t size = wcstombs(0, ws.c_str(), 0); + if (size == std::size_t(-1)) return s; + std::string ret; + ret.resize(size); + size = wcstombs(&ret[0], ws.c_str(), size + 1); + if (size == wchar_t(-1)) return s; + ret.resize(size); + return ret; + } + catch(std::exception) + { + return s; + } + } + + void throw_exception(const char* thrower) + { + DWORD err = GetLastError(); + +#ifdef UNICODE + wchar_t *wbuffer = 0; + FormatMessage( + FORMAT_MESSAGE_FROM_SYSTEM + |FORMAT_MESSAGE_ALLOCATE_BUFFER + , 0, err, 0, (LPWSTR)&wbuffer, 0, 0); + auto_localfree auto_free(wbuffer); + std::string tmp_utf8; + libtorrent::wchar_utf8(wbuffer, tmp_utf8); + char const* buffer = tmp_utf8.c_str(); +#else + char* buffer = 0; + FormatMessage( + FORMAT_MESSAGE_FROM_SYSTEM + |FORMAT_MESSAGE_ALLOCATE_BUFFER + , 0, err, 0, (LPSTR)&buffer, 0, 0); + auto_localfree auto_free(buffer); +#endif + + std::stringstream s; + s << (thrower ? thrower : "NULL") << ": " << (buffer ? buffer : "NULL"); + + throw libtorrent::file_error(s.str()); + } +} + +namespace libtorrent +{ + + struct file::impl : boost::noncopyable + { + enum open_flags + { + read_flag = 1, + write_flag = 2 + }; + + enum seek_mode + { + seek_begin = FILE_BEGIN, + seek_from_here = FILE_CURRENT, + seek_end = FILE_END + }; + + impl() + { + m_file_handle = INVALID_HANDLE_VALUE; + } + + void open(const char *file_name, open_flags flags) + { + assert(file_name); + assert(flags & (read_flag | write_flag)); + + DWORD access_mask = 0; + if (flags & read_flag) + access_mask |= GENERIC_READ; + if (flags & write_flag) + access_mask |= GENERIC_WRITE; + + assert(access_mask & (GENERIC_READ | GENERIC_WRITE)); + + #ifdef UNICODE + std::wstring wfile_name(safe_convert(file_name)); + HANDLE new_handle = CreateFile( + wfile_name.c_str() + , access_mask + , FILE_SHARE_READ + , 0 + , (flags & write_flag)?OPEN_ALWAYS:OPEN_EXISTING + , FILE_ATTRIBUTE_NORMAL + , 0); + #else + HANDLE new_handle = CreateFile( + utf8_native(file_name).c_str() + , access_mask + , FILE_SHARE_READ + , 0 + , (flags & write_flag)?OPEN_ALWAYS:OPEN_EXISTING + , FILE_ATTRIBUTE_NORMAL + , 0); + #endif + + if (new_handle == INVALID_HANDLE_VALUE) + throw_exception(file_name); + // try to make the file sparse if supported + if (access_mask & GENERIC_WRITE) + { + DWORD temp; + ::DeviceIoControl(new_handle, FSCTL_SET_SPARSE, 0, 0 + , 0, 0, &temp, 0); + } + // will only close old file if the open succeeded + close(); + m_file_handle = new_handle; + } + + void close() + { + if (m_file_handle != INVALID_HANDLE_VALUE) + { + CloseHandle(m_file_handle); + m_file_handle = INVALID_HANDLE_VALUE; + } + } + + ~impl() + { + close(); + } + + size_type write(const char* buffer, size_type num_bytes) + { + assert(buffer); + assert((DWORD)num_bytes == num_bytes); + DWORD bytes_written = 0; + if (num_bytes != 0) + { + if (FALSE == WriteFile( + m_file_handle + , buffer + , (DWORD)num_bytes + , &bytes_written + , 0)) + { + throw_exception("file::write"); + } + } + return bytes_written; + } + + size_type read(char* buffer, size_type num_bytes) + { + assert(buffer); + assert(num_bytes >= 0); + assert((DWORD)num_bytes == num_bytes); + + DWORD bytes_read = 0; + if (num_bytes != 0) + { + if (FALSE == ReadFile( + m_file_handle + , buffer + , (DWORD)num_bytes + , &bytes_read + , 0)) + { + throw_exception("file::read"); + } + } + return bytes_read; + } + + void set_size(size_type s) + { + size_type pos = tell(); + seek(s, seek_begin); + if (FALSE == ::SetEndOfFile(m_file_handle)) + throw_exception("file::set_size"); + + seek(pos, seek_begin); + } + + size_type seek(size_type pos, seek_mode from_where) + { + assert(pos >= 0 || from_where != seek_begin); + assert(pos <= 0 || from_where != seek_end); + LARGE_INTEGER offs; + offs.QuadPart = pos; + if (FALSE == SetFilePointerEx( + m_file_handle + , offs + , &offs + , from_where)) + { + throw_exception("file::seek"); + } + return offs.QuadPart; + } + + size_type tell() + { + LARGE_INTEGER offs; + offs.QuadPart = 0; + + // is there any other way to get offset? + if (FALSE == SetFilePointerEx( + m_file_handle + , offs + , &offs + , FILE_CURRENT)) + { + throw_exception("file::tell"); + } + + size_type pos = offs.QuadPart; + assert(pos >= 0); + return pos; + } +/* + size_type size() + { + LARGE_INTEGER s; + if (FALSE == GetFileSizeEx(m_file_handle, &s)) + { + throw_exception("file::size"); + } + + size_type size = s.QuadPart; + assert(size >= 0); + return size; + } +*/ + private: + + HANDLE m_file_handle; + + }; +} + +namespace libtorrent +{ + + const file::seek_mode file::begin(file::impl::seek_begin); + const file::seek_mode file::end(file::impl::seek_end); + + const file::open_mode file::in(file::impl::read_flag); + const file::open_mode file::out(file::impl::write_flag); + + file::file() + : m_impl(new libtorrent::file::impl()) + { + } + file::file(boost::filesystem::path const& p, open_mode m) + : m_impl(new libtorrent::file::impl()) + { + open(p,m); + } + + file::~file() + { + } + + void file::open(boost::filesystem::path const& p, open_mode m) + { + assert(p.is_complete()); + m_impl->open(p.native_file_string().c_str(), impl::open_flags(m.m_mask)); + } + + void file::close() + { + m_impl->close(); + } + + size_type file::write(const char* buffer, size_type num_bytes) + { + return m_impl->write(buffer, num_bytes); + } + + size_type file::read(char* buffer, size_type num_bytes) + { + return m_impl->read(buffer, num_bytes); + } + + void file::set_size(size_type s) + { + m_impl->set_size(s); + } + + size_type file::seek(size_type pos, seek_mode m) + { + return m_impl->seek(pos,impl::seek_mode(m.m_val)); + } + + size_type file::tell() + { + return m_impl->tell(); + } +} diff --git a/encryption/libtorrent/src/http_connection.cpp b/encryption/libtorrent/src/http_connection.cpp new file mode 100644 index 000000000..ba1592898 --- /dev/null +++ b/encryption/libtorrent/src/http_connection.cpp @@ -0,0 +1,384 @@ +/* + +Copyright (c) 2007, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/http_connection.hpp" + +#include +#include +#include +#include + +using boost::bind; + +namespace libtorrent +{ + +void http_connection::get(std::string const& url, time_duration timeout + , bool handle_redirect) +{ + m_redirect = handle_redirect; + std::string protocol; + std::string auth; + std::string hostname; + std::string path; + int port; + boost::tie(protocol, auth, hostname, port, path) = parse_url_components(url); + std::stringstream headers; + headers << "GET " << path << " HTTP/1.0\r\n" + "Host:" << hostname << + "\r\nConnection: close\r\n"; + if (!auth.empty()) + headers << "Authorization: Basic " << base64encode(auth) << "\r\n"; + headers << "\r\n"; + sendbuffer = headers.str(); + start(hostname, boost::lexical_cast(port), timeout); +} + +void http_connection::start(std::string const& hostname, std::string const& port + , time_duration timeout, bool handle_redirect) +{ + m_redirect = handle_redirect; + m_timeout = timeout; + m_timer.expires_from_now(m_timeout); + m_timer.async_wait(bind(&http_connection::on_timeout + , boost::weak_ptr(shared_from_this()), _1)); + m_called = false; + if (m_sock.is_open() && m_hostname == hostname && m_port == port) + { + m_parser.reset(); + asio::async_write(m_sock, asio::buffer(sendbuffer) + , bind(&http_connection::on_write, shared_from_this(), _1)); + } + else + { + m_sock.close(); + tcp::resolver::query query(hostname, port); + m_resolver.async_resolve(query, bind(&http_connection::on_resolve + , shared_from_this(), _1, _2)); + m_hostname = hostname; + m_port = port; + } +} + +void http_connection::on_connect_timeout() +{ + if (m_connection_ticket > -1) m_cc.done(m_connection_ticket); + m_connection_ticket = -1; + + if (m_bottled && m_called) return; + m_called = true; + m_handler(asio::error::timed_out, m_parser, 0, 0); + close(); +} + +void http_connection::on_timeout(boost::weak_ptr p + , asio::error_code const& e) +{ + boost::shared_ptr c = p.lock(); + if (!c) return; + if (c->m_connection_ticket > -1) c->m_cc.done(c->m_connection_ticket); + c->m_connection_ticket = -1; + + if (e == asio::error::operation_aborted) return; + + if (c->m_bottled && c->m_called) return; + + if (c->m_last_receive + c->m_timeout < time_now()) + { + c->m_called = true; + c->m_handler(asio::error::timed_out, c->m_parser, 0, 0); + return; + } + + c->m_timer.expires_at(c->m_last_receive + c->m_timeout); + c->m_timer.async_wait(bind(&http_connection::on_timeout, p, _1)); +} + +void http_connection::close() +{ + m_timer.cancel(); + m_limiter_timer.cancel(); + m_sock.close(); + m_hostname.clear(); + m_port.clear(); + + if (m_connection_ticket > -1) m_cc.done(m_connection_ticket); + m_connection_ticket = -1; +} + +void http_connection::on_resolve(asio::error_code const& e + , tcp::resolver::iterator i) +{ + if (e) + { + close(); + if (m_bottled && m_called) return; + m_called = true; + m_handler(e, m_parser, 0, 0); + return; + } + assert(i != tcp::resolver::iterator()); + m_cc.enqueue(bind(&http_connection::connect, shared_from_this(), _1, *i) + , bind(&http_connection::on_connect_timeout, shared_from_this()) + , m_timeout); +} + +void http_connection::connect(int ticket, tcp::endpoint target_address) +{ + m_connection_ticket = ticket; + m_sock.async_connect(target_address, boost::bind(&http_connection::on_connect + , shared_from_this(), _1/*, ++i*/)); +} + +void http_connection::on_connect(asio::error_code const& e + /*, tcp::resolver::iterator i*/) +{ + if (!e) + { + m_last_receive = time_now(); + asio::async_write(m_sock, asio::buffer(sendbuffer) + , bind(&http_connection::on_write, shared_from_this(), _1)); + } +/* else if (i != tcp::resolver::iterator()) + { + // The connection failed. Try the next endpoint in the list. + m_sock.close(); + m_cc.enqueue(bind(&http_connection::connect, shared_from_this(), _1, *i) + , bind(&http_connection::on_connect_timeout, shared_from_this()) + , m_timeout); + } +*/ else + { + close(); + if (m_bottled && m_called) return; + m_called = true; + m_handler(e, m_parser, 0, 0); + } +} + +void http_connection::on_write(asio::error_code const& e) +{ + if (e) + { + close(); + if (m_bottled && m_called) return; + m_called = true; + m_handler(e, m_parser, 0, 0); + return; + } + + std::string().swap(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) + on_assign_bandwidth(asio::error_code()); + return; + } + } + m_sock.async_read_some(asio::buffer(&m_recvbuffer[0] + m_read_pos + , amount_to_read) + , bind(&http_connection::on_read + , shared_from_this(), _1, _2)); +} + +void http_connection::on_read(asio::error_code const& e + , std::size_t bytes_transferred) +{ + if (m_rate_limit) + { + m_download_quota -= bytes_transferred; + assert(m_download_quota >= 0); + } + + if (e == asio::error::eof) + { + close(); + if (m_bottled && m_called) return; + m_called = true; + char const* data = 0; + std::size_t size = 0; + if (m_bottled) + { + data = m_parser.get_body().begin; + size = m_parser.get_body().left(); + } + m_handler(asio::error_code(), m_parser, data, size); + return; + } + + if (e) + { + close(); + if (m_bottled && m_called) return; + m_called = true; + m_handler(e, m_parser, 0, 0); + return; + } + + m_read_pos += bytes_transferred; + assert(m_read_pos <= int(m_recvbuffer.size())); + + // having a nonempty path means we should handle redirects + if (m_redirect && m_parser.header_finished()) + { + int code = m_parser.status_code(); + if (code >= 300 && code < 400) + { + // attempt a redirect + std::string url = m_parser.header("location"); + if (url.empty()) + { + // missing location header + if (m_bottled && m_called) return; + m_called = true; + m_handler(e, m_parser, 0, 0); + return; + } + + m_limiter_timer_active = false; + close(); + + get(url, m_timeout); + return; + } + + m_redirect = false; + } + + if (m_bottled || !m_parser.header_finished()) + { + libtorrent::buffer::const_interval rcv_buf(&m_recvbuffer[0] + , &m_recvbuffer[0] + m_read_pos); + m_parser.incoming(rcv_buf); + if (!m_bottled && m_parser.header_finished()) + { + if (m_read_pos > m_parser.body_start()) + m_handler(e, m_parser, &m_recvbuffer[0] + m_parser.body_start() + , m_read_pos - m_parser.body_start()); + m_read_pos = 0; + m_last_receive = time_now(); + } + else if (m_bottled && m_parser.finished()) + { + m_timer.cancel(); + if (m_bottled && m_called) return; + m_called = true; + m_handler(e, m_parser, m_parser.get_body().begin, m_parser.get_body().left()); + } + } + else + { + assert(!m_bottled); + m_handler(e, m_parser, &m_recvbuffer[0], m_read_pos); + m_read_pos = 0; + m_last_receive = time_now(); + } + + if (int(m_recvbuffer.size()) == m_read_pos) + m_recvbuffer.resize((std::min)(m_read_pos + 2048, 1024*500)); + if (m_read_pos == 1024 * 500) + { + close(); + if (m_bottled && m_called) return; + m_called = true; + m_handler(asio::error::eof, m_parser, 0, 0); + 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) + on_assign_bandwidth(asio::error_code()); + return; + } + } + m_sock.async_read_some(asio::buffer(&m_recvbuffer[0] + m_read_pos + , amount_to_read) + , bind(&http_connection::on_read + , shared_from_this(), _1, _2)); +} + +void http_connection::on_assign_bandwidth(asio::error_code const& e) +{ + if ((e == asio::error::operation_aborted + && m_limiter_timer_active) + || !m_sock.is_open()) + { + if (!m_bottled || !m_called) + m_handler(e, m_parser, 0, 0); + 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; + + m_sock.async_read_some(asio::buffer(&m_recvbuffer[0] + m_read_pos + , amount_to_read) + , bind(&http_connection::on_read + , shared_from_this(), _1, _2)); + + m_limiter_timer_active = true; + m_limiter_timer.expires_from_now(milliseconds(250)); + m_limiter_timer.async_wait(bind(&http_connection::on_assign_bandwidth + , shared_from_this(), _1)); +} + +void http_connection::rate_limit(int limit) +{ + if (!m_limiter_timer_active) + { + m_limiter_timer_active = true; + m_limiter_timer.expires_from_now(milliseconds(250)); + m_limiter_timer.async_wait(bind(&http_connection::on_assign_bandwidth + , shared_from_this(), _1)); + } + m_rate_limit = limit; +} + +} + diff --git a/encryption/libtorrent/src/http_stream.cpp b/encryption/libtorrent/src/http_stream.cpp new file mode 100644 index 000000000..0973af798 --- /dev/null +++ b/encryption/libtorrent/src/http_stream.cpp @@ -0,0 +1,162 @@ +/* + +Copyright (c) 2007, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/pch.hpp" + +#include "libtorrent/http_stream.hpp" +#include "libtorrent/tracker_manager.hpp" // for base64encode + +namespace libtorrent +{ + + void http_stream::name_lookup(asio::error_code const& e, tcp::resolver::iterator i + , boost::shared_ptr h) + { + if (e || i == tcp::resolver::iterator()) + { + (*h)(e); + close(); + return; + } + + m_sock.async_connect(i->endpoint(), boost::bind( + &http_stream::connected, this, _1, h)); + } + + void http_stream::connected(asio::error_code const& e, boost::shared_ptr h) + { + if (e) + { + (*h)(e); + close(); + 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); + write_string("CONNECT " + boost::lexical_cast(m_remote_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); + asio::async_write(m_sock, asio::buffer(m_buffer) + , boost::bind(&http_stream::handshake1, this, _1, h)); + } + + void http_stream::handshake1(asio::error_code const& e, boost::shared_ptr h) + { + if (e) + { + (*h)(e); + close(); + return; + } + + // read one byte from the socket + m_buffer.resize(1); + asio::async_read(m_sock, asio::buffer(m_buffer) + , boost::bind(&http_stream::handshake2, this, _1, h)); + } + + void http_stream::handshake2(asio::error_code const& e, boost::shared_ptr h) + { + if (e) + { + (*h)(e); + close(); + 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 = strchr(&m_buffer[0], ' '); + if (status == 0) + { + (*h)(asio::error::operation_not_supported); + close(); + return; + } + + status++; + int code = atoi(status); + if (code != 200) + { + (*h)(asio::error::operation_not_supported); + close(); + return; + } + + (*h)(e); + std::vector().swap(m_buffer); + return; + } + + // read another byte from the socket + m_buffer.resize(read_pos + 1); + asio::async_read(m_sock, asio::buffer(&m_buffer[0] + read_pos, 1) + , boost::bind(&http_stream::handshake2, this, _1, h)); + } + +} + diff --git a/encryption/libtorrent/src/http_tracker_connection.cpp b/encryption/libtorrent/src/http_tracker_connection.cpp new file mode 100755 index 000000000..cc039f198 --- /dev/null +++ b/encryption/libtorrent/src/http_tracker_connection.cpp @@ -0,0 +1,925 @@ +/* + +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 "libtorrent/pch.hpp" + +#include +#include +#include +#include +#include +#include + +#include "libtorrent/config.hpp" +#include "zlib.h" + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/tracker_manager.hpp" +#include "libtorrent/http_tracker_connection.hpp" +#include "libtorrent/entry.hpp" +#include "libtorrent/bencode.hpp" +#include "libtorrent/torrent.hpp" +#include "libtorrent/io.hpp" +#include "libtorrent/instantiate_connection.hpp" + +using namespace libtorrent; +using boost::bind; + +namespace +{ + enum + { + minimum_tracker_response_length = 3, + http_buffer_size = 2048 + }; + + + enum + { + FTEXT = 0x01, + FHCRC = 0x02, + FEXTRA = 0x04, + FNAME = 0x08, + FCOMMENT = 0x10, + FRESERVED = 0xe0, + + GZIP_MAGIC0 = 0x1f, + GZIP_MAGIC1 = 0x8b + }; + +} + +namespace +{ + bool url_has_argument(std::string const& url, std::string argument) + { + size_t i = url.find('?'); + if (i == std::string::npos) return false; + + argument += '='; + + if (url.compare(i + 1, argument.size(), argument) == 0) return true; + argument.insert(0, "&"); + return url.find(argument, i) + != std::string::npos; + } + + char to_lower(char c) { return std::tolower(c); } +} + +namespace libtorrent +{ + http_parser::http_parser() + : m_recv_pos(0) + , m_status_code(-1) + , m_content_length(-1) + , m_state(read_status) + , m_recv_buffer(0, 0) + , m_body_start_pos(0) + , m_finished(false) + {} + + boost::tuple http_parser::incoming(buffer::const_interval recv_buffer) + { + assert(recv_buffer.left() >= m_recv_buffer.left()); + boost::tuple ret(0, 0); + + // early exit if there's nothing new in the receive buffer + if (recv_buffer.left() == m_recv_buffer.left()) return ret; + m_recv_buffer = recv_buffer; + + char const* pos = recv_buffer.begin + m_recv_pos; + if (m_state == read_status) + { + assert(!m_finished); + char const* newline = std::find(pos, recv_buffer.end, '\n'); + // if we don't have a full line yet, wait. + if (newline == recv_buffer.end) return ret; + + if (newline == pos) + throw std::runtime_error("unexpected newline in HTTP response"); + + char const* line_end = newline; + if (pos != line_end && *(line_end - 1) == '\r') --line_end; + + std::istringstream line(std::string(pos, line_end)); + ++newline; + int incoming = (int)std::distance(pos, newline); + m_recv_pos += incoming; + boost::get<1>(ret) += incoming; + pos = newline; + + line >> m_protocol; + if (m_protocol.substr(0, 5) != "HTTP/") + { + throw std::runtime_error("unknown protocol in HTTP response: " + + m_protocol + " line: " + std::string(pos, newline)); + } + line >> m_status_code; + std::getline(line, m_server_message); + m_state = read_header; + } + + if (m_state == read_header) + { + assert(!m_finished); + char const* newline = std::find(pos, recv_buffer.end, '\n'); + std::string line; + + while (newline != recv_buffer.end && m_state == read_header) + { + // if 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; + boost::get<1>(ret) += newline - pos; + 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. + m_state = read_body; + m_body_start_pos = 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") + { + try + { + m_content_length = boost::lexical_cast(value); + } + catch(boost::bad_lexical_cast&) {} + } + else if (name == "content-range") + { + std::stringstream range_str(value); + char dummy; + std::string bytes; + size_type range_start, range_end; + range_str >> bytes >> range_start >> dummy >> range_end; + if (!range_str || range_end < range_start) + { + throw std::runtime_error("invalid content-range in HTTP response: " + range_str.str()); + } + // the http range is inclusive + m_content_length = range_end - range_start + 1; + } + + assert(m_recv_pos <= (int)recv_buffer.left()); + newline = std::find(pos, recv_buffer.end, '\n'); + } + } + + if (m_state == read_body) + { + int incoming = recv_buffer.end - pos; + if (m_recv_pos - m_body_start_pos + incoming > m_content_length + && m_content_length >= 0) + incoming = m_content_length - m_recv_pos + m_body_start_pos; + + assert(incoming >= 0); + m_recv_pos += incoming; + boost::get<0>(ret) += incoming; + + if (m_content_length >= 0 + && m_recv_pos - m_body_start_pos >= m_content_length) + { + m_finished = true; + } + } + return ret; + } + + buffer::const_interval http_parser::get_body() const + { + assert(m_state == read_body); + if (m_content_length >= 0) + return buffer::const_interval(m_recv_buffer.begin + m_body_start_pos + , m_recv_buffer.begin + std::min(m_recv_pos + , m_body_start_pos + m_content_length)); + else + return buffer::const_interval(m_recv_buffer.begin + m_body_start_pos + , m_recv_buffer.begin + m_recv_pos); + } + + void http_parser::reset() + { + m_recv_pos = 0; + m_body_start_pos = 0; + m_status_code = -1; + m_content_length = -1; + m_finished = false; + m_state = read_status; + m_recv_buffer.begin = 0; + m_recv_buffer.end = 0; + m_header.clear(); + } + + http_tracker_connection::http_tracker_connection( + asio::strand& str + , connection_queue& cc + , tracker_manager& man + , tracker_request const& req + , std::string const& hostname + , unsigned short port + , std::string request + , address bind_infc + , boost::weak_ptr c + , session_settings const& stn + , proxy_settings const& ps + , std::string const& auth) + : tracker_connection(man, req, str, bind_infc, c) + , m_man(man) + , m_strand(str) + , m_name_lookup(m_strand.io_service()) + , m_port(port) + , m_recv_pos(0) + , m_buffer(http_buffer_size) + , m_settings(stn) + , m_proxy(ps) + , m_password(auth) + , m_timed_out(false) + , m_connection_ticket(-1) + , m_cc(cc) + { + m_send_buffer.assign("GET "); + + // should we use the proxy? + if (m_proxy.type == proxy_settings::http + || m_proxy.type == proxy_settings::http_pw) + { + m_send_buffer += "http://"; + m_send_buffer += hostname; + if (port != 80) + { + m_send_buffer += ":"; + m_send_buffer += boost::lexical_cast(port); + } + } + + if (tracker_req().kind == tracker_request::scrape_request) + { + // find and replace "announce" with "scrape" + // in request + + std::size_t pos = request.find("announce"); + if (pos == std::string::npos) + throw std::runtime_error("scrape is not available on url: '" + + tracker_req().url +"'"); + request.replace(pos, 8, "scrape"); + } + + m_send_buffer += request; + + // if request-string already contains + // some parameters, append an ampersand instead + // of a question mark + size_t arguments_start = request.find('?'); + if (arguments_start != std::string::npos) + m_send_buffer += "&"; + else + m_send_buffer += "?"; + + if (!url_has_argument(request, "info_hash")) + { + m_send_buffer += "info_hash="; + m_send_buffer += escape_string( + reinterpret_cast(req.info_hash.begin()), 20); + m_send_buffer += '&'; + } + + if (tracker_req().kind == tracker_request::announce_request) + { + if (!url_has_argument(request, "peer_id")) + { + m_send_buffer += "peer_id="; + m_send_buffer += escape_string( + reinterpret_cast(req.pid.begin()), 20); + m_send_buffer += '&'; + } + + if (!url_has_argument(request, "port")) + { + m_send_buffer += "port="; + m_send_buffer += boost::lexical_cast(req.listen_port); + m_send_buffer += '&'; + } + + if (!url_has_argument(request, "uploaded")) + { + m_send_buffer += "uploaded="; + m_send_buffer += boost::lexical_cast(req.uploaded); + m_send_buffer += '&'; + } + + if (!url_has_argument(request, "downloaded")) + { + m_send_buffer += "downloaded="; + m_send_buffer += boost::lexical_cast(req.downloaded); + m_send_buffer += '&'; + } + + if (!url_has_argument(request, "left")) + { + m_send_buffer += "left="; + m_send_buffer += boost::lexical_cast(req.left); + m_send_buffer += '&'; + } + + if (req.event != tracker_request::none) + { + if (!url_has_argument(request, "event")) + { + const char* event_string[] = {"completed", "started", "stopped"}; + m_send_buffer += "event="; + m_send_buffer += event_string[req.event - 1]; + m_send_buffer += '&'; + } + } + if (!url_has_argument(request, "key")) + { + m_send_buffer += "key="; + std::stringstream key_string; + key_string << std::hex << req.key; + m_send_buffer += key_string.str(); + m_send_buffer += '&'; + } + + if (!url_has_argument(request, "compact")) + { + m_send_buffer += "compact=1&"; + } + if (!url_has_argument(request, "numwant")) + { + m_send_buffer += "numwant="; + m_send_buffer += boost::lexical_cast( + std::min(req.num_want, 999)); + m_send_buffer += '&'; + } + +#ifndef TORRENT_DISABLE_ENCRYPTION + m_send_buffer += "supportcrypto=1&"; +#endif + + // extension that tells the tracker that + // we don't need any peer_id's in the response + if (!url_has_argument(request, "no_peer_id")) + { + m_send_buffer += "no_peer_id=1"; + } + else + { + // remove the trailing '&' + m_send_buffer.resize(m_send_buffer.size() - 1); + } + } + + m_send_buffer += " HTTP/1.0\r\nAccept-Encoding: gzip\r\n" + "User-Agent: "; + m_send_buffer += m_settings.user_agent; + m_send_buffer += "\r\n" + "Host: "; + m_send_buffer += hostname; + if (port != 80) + { + m_send_buffer += ':'; + m_send_buffer += boost::lexical_cast(port); + } + if (m_proxy.type == proxy_settings::http_pw) + { + m_send_buffer += "\r\nProxy-Authorization: Basic "; + m_send_buffer += base64encode(m_proxy.username + ":" + m_proxy.password); + } + if (!auth.empty()) + { + m_send_buffer += "\r\nAuthorization: Basic "; + m_send_buffer += base64encode(auth); + } + m_send_buffer += "\r\n\r\n"; + +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + if (has_requester()) + { + requester().debug_log("==> TRACKER_REQUEST [ str: " + m_send_buffer + " ]"); + std::stringstream info_hash_str; + info_hash_str << req.info_hash; + requester().debug_log("info_hash: " + + boost::lexical_cast(req.info_hash)); + requester().debug_log("name lookup: " + hostname); + } +#endif + + tcp::resolver::query q(hostname + , boost::lexical_cast(m_port)); + m_name_lookup.async_resolve(q, m_strand.wrap( + boost::bind(&http_tracker_connection::name_lookup, self(), _1, _2))); + set_timeout(m_settings.tracker_completion_timeout + , m_settings.tracker_receive_timeout); + } + + void http_tracker_connection::on_timeout() + { + m_timed_out = true; + m_socket.reset(); + m_name_lookup.cancel(); + if (m_connection_ticket > -1) m_cc.done(m_connection_ticket); + m_connection_ticket = -1; + fail_timeout(); + } + + void http_tracker_connection::name_lookup(asio::error_code const& error + , tcp::resolver::iterator i) try + { +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + if (has_requester()) requester().debug_log("tracker name lookup handler called"); +#endif + if (error == asio::error::operation_aborted) return; + if (m_timed_out) return; + + if (error || i == tcp::resolver::iterator()) + { + fail(-1, error.message().c_str()); + return; + } + +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + if (has_requester()) requester().debug_log("tracker name lookup successful"); +#endif + 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. + tcp::resolver::iterator target = i; + tcp::resolver::iterator end; + tcp::endpoint target_address = *i; + for (; target != end && target->endpoint().address().is_v4() + != bind_interface().is_v4(); ++target); + if (target == end) + { + assert(target_address.address().is_v4() != bind_interface().is_v4()); + if (has_requester()) + { + std::string tracker_address_type = target_address.address().is_v4() ? "IPv4" : "IPv6"; + std::string bind_address_type = bind_interface().is_v4() ? "IPv4" : "IPv6"; + requester().tracker_warning("the tracker only resolves to an " + + tracker_address_type + " address, and you're listening on an " + + bind_address_type + " socket. This may prevent you from receiving incoming connections."); + } + } + else + { + target_address = *target; + } + + if (has_requester()) requester().m_tracker_address = target_address; + m_socket = instantiate_connection(m_name_lookup.io_service(), m_proxy); + + if (m_proxy.type == proxy_settings::http + || m_proxy.type == proxy_settings::http_pw) + { + // the tracker connection will talk immediately to + // the proxy, without requiring CONNECT support + m_socket->get().set_no_connect(true); + } + + m_socket->open(target_address.protocol()); + m_socket->bind(tcp::endpoint(bind_interface(), 0)); + m_cc.enqueue(bind(&http_tracker_connection::connect, self(), _1, target_address) + , bind(&http_tracker_connection::on_timeout, self()) + , seconds(m_settings.tracker_receive_timeout)); + } + catch (std::exception& e) + { + fail(-1, e.what()); + }; + + void http_tracker_connection::connect(int ticket, tcp::endpoint target_address) + { + m_connection_ticket = ticket; + m_socket->async_connect(target_address, bind(&http_tracker_connection::connected, self(), _1)); + } + + void http_tracker_connection::connected(asio::error_code const& error) try + { + if (m_connection_ticket > -1) m_cc.done(m_connection_ticket); + m_connection_ticket = -1; + if (error == asio::error::operation_aborted) return; + if (m_timed_out) return; + if (error) + { + fail(-1, error.message().c_str()); + return; + } + +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + if (has_requester()) requester().debug_log("tracker connection successful"); +#endif + + restart_read_timeout(); + async_write(*m_socket, asio::buffer(m_send_buffer.c_str() + , m_send_buffer.size()), bind(&http_tracker_connection::sent + , self(), _1)); + } + catch (std::exception& e) + { + fail(-1, e.what()); + } + + void http_tracker_connection::sent(asio::error_code const& error) try + { + if (error == asio::error::operation_aborted) return; + if (m_timed_out) return; + if (error) + { + fail(-1, error.message().c_str()); + return; + } + +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + if (has_requester()) requester().debug_log("tracker send data completed"); +#endif + restart_read_timeout(); + assert(m_buffer.size() - m_recv_pos > 0); + m_socket->async_read_some(asio::buffer(&m_buffer[m_recv_pos] + , m_buffer.size() - m_recv_pos), bind(&http_tracker_connection::receive + , self(), _1, _2)); + } + catch (std::exception& e) + { + fail(-1, e.what()); + }; // msvc 7.1 seems to require this semi-colon + + + void http_tracker_connection::receive(asio::error_code const& error + , std::size_t bytes_transferred) try + { + if (error == asio::error::operation_aborted) return; + if (m_timed_out) return; + + if (error) + { + if (error == asio::error::eof) + { + on_response(); + close(); + return; + } + + fail(-1, error.message().c_str()); + return; + } + + restart_read_timeout(); + assert(bytes_transferred > 0); +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + if (has_requester()) requester().debug_log("tracker connection reading " + + boost::lexical_cast(bytes_transferred)); +#endif + + m_recv_pos += bytes_transferred; + m_parser.incoming(buffer::const_interval(&m_buffer[0] + , &m_buffer[0] + m_recv_pos)); + + // if the receive buffer is full, expand it with http_buffer_size + if ((int)m_buffer.size() == m_recv_pos) + { + if ((int)m_buffer.size() >= m_settings.tracker_maximum_response_length) + { + fail(200, "too large tracker response"); + return; + } + assert(http_buffer_size > 0); + if ((int)m_buffer.size() + http_buffer_size + > m_settings.tracker_maximum_response_length) + m_buffer.resize(m_settings.tracker_maximum_response_length); + else + m_buffer.resize(m_buffer.size() + http_buffer_size); + } + + if (m_parser.header_finished()) + { + int cl = m_parser.header("content-length"); + if (cl > m_settings.tracker_maximum_response_length) + { + fail(-1, "content-length is greater than maximum response length"); + return; + } + + if (cl > 0 && cl < minimum_tracker_response_length && m_parser.status_code() == 200) + { + fail(-1, "content-length is smaller than minimum response length"); + return; + } + } + + if (m_parser.finished()) + { + on_response(); + close(); + return; + } + + assert(m_buffer.size() - m_recv_pos > 0); + m_socket->async_read_some(asio::buffer(&m_buffer[m_recv_pos] + , m_buffer.size() - m_recv_pos), bind(&http_tracker_connection::receive + , self(), _1, _2)); + } + catch (std::exception& e) + { + fail(-1, e.what()); + }; + + void http_tracker_connection::on_response() + { + if (!m_parser.header_finished()) + { + fail(-1, "premature end of file"); + return; + } + + std::string location = m_parser.header("location"); + + if (m_parser.status_code() >= 300 && m_parser.status_code() < 400) + { + if (location.empty()) + { + std::string error_str = "got redirection response ("; + error_str += boost::lexical_cast(m_parser.status_code()); + error_str += ") without 'Location' header"; + fail(-1, error_str.c_str()); + return; + } + + // if the protocol isn't specified, assume http + if (location.compare(0, 7, "http://") != 0 + && location.compare(0, 6, "udp://") != 0) + { + location.insert(0, "http://"); + } + +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + if (has_requester()) requester().debug_log("Redirecting to \"" + location + "\""); +#endif + if (has_requester()) requester().tracker_warning("Redirecting to \"" + location + "\""); + tracker_request req = tracker_req(); + + req.url = location; + + m_man.queue_request(m_strand, m_cc, req + , m_password, bind_interface(), m_requester); + close(); + return; + } + + if (m_parser.status_code() != 200) + { + fail(m_parser.status_code(), m_parser.message().c_str()); + close(); + return; + } + + buffer::const_interval buf(&m_buffer[0] + m_parser.body_start(), &m_buffer[0] + m_recv_pos); + + std::string content_encoding = m_parser.header("content-encoding"); + +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + if (has_requester()) requester().debug_log("content-encoding: \"" + content_encoding + "\""); +#endif + + if (content_encoding == "gzip" || content_encoding == "x-gzip") + { + boost::shared_ptr r = m_requester.lock(); + + if (!r) + { + close(); + return; + } + m_buffer.erase(m_buffer.begin(), m_buffer.begin() + m_parser.body_start()); + if (inflate_gzip(m_buffer, tracker_request(), r.get(), + m_settings.tracker_maximum_response_length)) + { + close(); + return; + } + buf.begin = &m_buffer[0]; + buf.end = &m_buffer[0] + m_buffer.size(); + } + else if (!content_encoding.empty()) + { + std::string error_str = "unknown content encoding in response: \""; + error_str += content_encoding; + error_str += "\""; + fail(-1, error_str.c_str()); + return; + } + + // handle tracker response + try + { + entry e = bdecode(buf.begin, buf.end); + parse(e); + } + catch (std::exception& e) + { + std::string error_str(e.what()); + error_str += ": \""; + for (char const* i = buf.begin, *end(buf.end); i != end; ++i) + { + if (std::isprint(*i)) error_str += *i; + else error_str += "0x" + boost::lexical_cast((unsigned int)*i) + " "; + } + error_str += "\""; + fail(m_parser.status_code(), error_str.c_str()); + } + #ifndef NDEBUG + catch (...) + { + assert(false); + } + #endif + } + + peer_entry http_tracker_connection::extract_peer_info(const entry& info) + { + peer_entry ret; + + // extract peer id (if any) + entry const* i = info.find_key("peer id"); + if (i != 0) + { + if (i->string().length() != 20) + throw std::runtime_error("invalid response from tracker"); + std::copy(i->string().begin(), i->string().end(), ret.pid.begin()); + } + else + { + // if there's no peer_id, just initialize it to a bunch of zeroes + std::fill_n(ret.pid.begin(), 20, 0); + } + + // extract ip + i = info.find_key("ip"); + if (i == 0) throw std::runtime_error("invalid response from tracker"); + ret.ip = i->string(); + + // extract port + i = info.find_key("port"); + if (i == 0) throw std::runtime_error("invalid response from tracker"); + ret.port = (unsigned short)i->integer(); + + return ret; + } + + void http_tracker_connection::parse(entry const& e) + { + if (!has_requester()) return; + + try + { + // parse the response + try + { + entry const& failure = e["failure reason"]; + + fail(m_parser.status_code(), failure.string().c_str()); + return; + } + catch (type_error const&) {} + + try + { + entry const& warning = e["warning message"]; + if (has_requester()) + requester().tracker_warning(warning.string()); + } + catch(type_error const&) {} + + std::vector peer_list; + + if (tracker_req().kind == tracker_request::scrape_request) + { + std::string ih; + std::copy(tracker_req().info_hash.begin(), tracker_req().info_hash.end() + , std::back_inserter(ih)); + entry scrape_data = e["files"][ih]; + int complete = scrape_data["complete"].integer(); + int incomplete = scrape_data["incomplete"].integer(); + requester().tracker_response(tracker_request(), peer_list, 0, complete + , incomplete); + return; + } + + int interval = (int)e["interval"].integer(); + + if (e["peers"].type() == entry::string_t) + { + std::string const& peers = e["peers"].string(); + for (std::string::const_iterator i = peers.begin(); + i != peers.end();) + { + if (std::distance(i, peers.end()) < 6) break; + + peer_entry p; + p.pid.clear(); + std::stringstream ip_str; + ip_str << (int)detail::read_uint8(i) << "."; + ip_str << (int)detail::read_uint8(i) << "."; + ip_str << (int)detail::read_uint8(i) << "."; + ip_str << (int)detail::read_uint8(i); + p.ip = ip_str.str(); + p.port = detail::read_uint16(i); + peer_list.push_back(p); + } + } + else + { + entry::list_type const& l = e["peers"].list(); + for(entry::list_type::const_iterator i = l.begin(); i != l.end(); ++i) + { + peer_entry p = extract_peer_info(*i); + peer_list.push_back(p); + } + } + + // look for optional scrape info + int complete = -1; + int incomplete = -1; + + try { complete = e["complete"].integer(); } + catch(type_error&) {} + + try { incomplete = e["incomplete"].integer(); } + catch(type_error&) {} + + requester().tracker_response(tracker_request(), peer_list, interval, complete + , incomplete); + } + catch(type_error& e) + { + requester().tracker_request_error(tracker_request(), m_parser.status_code(), e.what()); + } + catch(std::runtime_error& e) + { + requester().tracker_request_error(tracker_request(), m_parser.status_code(), e.what()); + } + } + +} + diff --git a/encryption/libtorrent/src/identify_client.cpp b/encryption/libtorrent/src/identify_client.cpp new file mode 100755 index 000000000..cf837a05b --- /dev/null +++ b/encryption/libtorrent/src/identify_client.cpp @@ -0,0 +1,351 @@ +/* + +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 "libtorrent/pch.hpp" + +#include +#include + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/identify_client.hpp" +#include "libtorrent/fingerprint.hpp" + +namespace +{ + + using namespace libtorrent; + + int decode_digit(char c) + { + if (std::isdigit(c)) return c - '0'; + return unsigned(c) - 'A' + 10; + } + + // takes a peer id and returns a valid boost::optional + // object if the peer id matched the azureus style encoding + // the returned fingerprint contains information about the + // client's id + boost::optional parse_az_style(const peer_id& id) + { + fingerprint ret("..", 0, 0, 0, 0); + + if (id[0] != '-' || !std::isprint(id[1]) || (id[2] < '0') + || (id[3] < '0') || (id[4] < '0') + || (id[5] < '0') || (id[6] < '0') + || id[7] != '-') + return boost::optional(); + + 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 (!std::isalnum(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 + || !std::isprint(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[] = + { + {"A", "ABC"} + , {"AG", "Ares"} + , {"AR", "Arctic Torrent"} + , {"AV", "Avicora"} + , {"AX", "BitPump"} + , {"AZ", "Azureus"} + , {"A~", "Ares"} + , {"BB", "BitBuddy"} + , {"BC", "BitComet"} + , {"BF", "Bitflu"} + , {"BG", "BTG"} + , {"BR", "BitRocket"} + , {"BS", "BTSlave"} + , {"BX", "BittorrentX"} + , {"CD", "Enhanced CTorrent"} + , {"CT", "CTorrent"} + , {"DE", "Deluge Torrent"} + , {"EB", "EBit"} + , {"ES", "electric sheep"} + , {"HL", "Halite"} + , {"HN", "Hydranode"} + , {"KT", "KTorrent"} + , {"LK", "Linkage"} + , {"LP", "lphant"} + , {"LT", "libtorrent"} + , {"M", "Mainline"} + , {"ML", "MLDonkey"} + , {"MO", "Mono Torrent"} + , {"MP", "MooPolice"} + , {"MT", "Moonlight Torrent"} + , {"O", "Osprey Permaseed"} + , {"PD", "Pando"} + , {"Q", "BTQueue"} + , {"QT", "Qt 4"} + , {"R", "Tribler"} + , {"S", "Shadow"} + , {"SB", "Swiftbit"} + , {"SN", "ShareNet"} + , {"SS", "SwarmScope"} + , {"SZ", "Shareaza"} + , {"S~", "Shareaza (beta)"} + , {"T", "BitTornado"} + , {"TN", "Torrent.NET"} + , {"TR", "Transmission"} + , {"TS", "TorrentStorm"} + , {"TT", "TuoTu"} + , {"U", "UPnP"} + , {"UL", "uLeecher"} + , {"UT", "MicroTorrent"} + , {"XT", "XanTorrent"} + , {"XX", "Xtorrent"} + , {"ZT", "ZipTorrent"} + , {"lt", "libTorrent (libtorrent.rakshasa.no/}"} + , {"pX", "pHoeniX"} + , {"qB", "qBittorrent"} + }; + + 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) + { + std::stringstream identity; + + 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 i = 1; i < size; ++i) + { + assert(compare_id(name_map[i-1] + , name_map[i])); + } +#endif + + if (i < name_map + size && std::equal(f.name, f.name + 2, i->id)) + identity << i->name; + else + { + identity << f.name[0]; + if (f.name[1] != 0) identity << f.name[1]; + } + + identity << " " << (int)f.major_version + << "." << (int)f.minor_version + << "." << (int)f.revision_version; + + if (f.name[1] != 0) + identity << "." << (int)f.tag_version; + + return identity.str(); + } + + bool find_string(unsigned char const* id, char const* search) + { + return std::equal(search, search + std::strlen(search), id); + } +} + +namespace libtorrent +{ + + boost::optional 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; + } + + std::string identify_client(peer_id const& p) + { + peer_id::const_iterator PID = p.begin(); + boost::optional f; + + if (p.is_all_zeros()) return "Unknown"; + + // ---------------------- + // non standard encodings + // ---------------------- + + if (find_string(PID, "Deadman Walking-")) return "Deadman"; + if (find_string(PID + 5, "Azureus")) return "Azureus 2.0.3.2"; + if (find_string(PID, "DansClient")) return "XanTorrent"; + if (find_string(PID + 4, "btfans")) return "SimpleBT"; + if (find_string(PID, "PRC.P---")) return "Bittorrent Plus! II"; + if (find_string(PID, "P87.P---")) return "Bittorrent Plus!"; + if (find_string(PID, "S587Plus")) return "Bittorrent Plus!"; + if (find_string(PID, "martini")) return "Martini Man"; + if (find_string(PID, "Plus---")) return "Bittorrent Plus"; + if (find_string(PID, "turbobt")) return "TurboBT"; + if (find_string(PID, "a00---0")) return "Swarmy"; + if (find_string(PID, "a02---0")) return "Swarmy"; + if (find_string(PID, "T00---0")) return "Teeweety"; + if (find_string(PID, "BTDWV-")) return "Deadman Walking"; + if (find_string(PID + 2, "BS")) return "BitSpirit"; + if (find_string(PID, "btuga")) return "BTugaXP"; + if (find_string(PID, "oernu")) return "BTugaXP"; + if (find_string(PID, "Mbrst")) return "Burst!"; + if (find_string(PID, "Plus")) return "Plus!"; + if (find_string(PID, "-Qt-")) return "Qt"; + if (find_string(PID, "exbc")) return "BitComet"; + if (find_string(PID, "-G3")) return "G3 Torrent"; + if (find_string(PID, "XBT")) return "XBT"; + if (find_string(PID, "OP")) return "Opera"; + + if (find_string(PID, "-BOW") && PID[7] == '-') + return "Bits on Wheels " + std::string(PID + 4, PID + 7); + + + if (find_string(PID, "eX")) + { + std::string user(PID + 2, PID + 14); + return std::string("eXeem ('") + user.c_str() + "')"; + } + + if (std::equal(PID, PID + 13, "\0\0\0\0\0\0\0\0\0\0\0\0\x97")) + return "Experimental 3.2.1b2"; + + if (std::equal(PID, PID + 13, "\0\0\0\0\0\0\0\0\0\0\0\0\0")) + return "Experimental 3.1"; + + + // look for azureus style id + f = parse_az_style(p); + if (f) return lookup(*f); + + // look for shadow style id + f = parse_shadow_style(p); + if (f) return lookup(*f); + + // look for mainline style id + f = parse_mainline_style(p); + if (f) return lookup(*f); + + + if (std::equal(PID, PID + 12, "\0\0\0\0\0\0\0\0\0\0\0\0")) + return "Generic"; + + std::string unknown("Unknown ["); + for (peer_id::const_iterator i = p.begin(); i != p.end(); ++i) + { + unknown += std::isprint(*i)?*i:'.'; + } + unknown += "]"; + return unknown; + } + +} diff --git a/encryption/libtorrent/src/instantiate_connection.cpp b/encryption/libtorrent/src/instantiate_connection.cpp new file mode 100644 index 000000000..ff4efbc59 --- /dev/null +++ b/encryption/libtorrent/src/instantiate_connection.cpp @@ -0,0 +1,78 @@ +/* + +Copyright (c) 2007, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/pch.hpp" + +#include "libtorrent/socket.hpp" +#include "libtorrent/session_settings.hpp" +#include "libtorrent/socket_type.hpp" +#include +#include +#include + +namespace libtorrent +{ + + boost::shared_ptr instantiate_connection( + asio::io_service& ios, proxy_settings const& ps) + { + boost::shared_ptr s(new socket_type(ios)); + + if (ps.type == proxy_settings::none) + { + s->instantiate(); + } + else if (ps.type == proxy_settings::http + || ps.type == proxy_settings::http_pw) + { + s->instantiate(); + s->get().set_proxy(ps.hostname, ps.port); + if (ps.type == proxy_settings::socks5_pw) + s->get().set_username(ps.username, ps.password); + } + else if (ps.type == proxy_settings::socks5 + || ps.type == proxy_settings::socks5_pw) + { + s->instantiate(); + s->get().set_proxy(ps.hostname, ps.port); + if (ps.type == proxy_settings::socks5_pw) + s->get().set_username(ps.username, ps.password); + } + else + { + throw std::runtime_error("unsupported proxy type"); + } + return s; + } + +} + diff --git a/encryption/libtorrent/src/ip_filter.cpp b/encryption/libtorrent/src/ip_filter.cpp new file mode 100644 index 000000000..92ea711c7 --- /dev/null +++ b/encryption/libtorrent/src/ip_filter.cpp @@ -0,0 +1,82 @@ +/* + +Copyright (c) 2005, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/pch.hpp" + +#include "libtorrent/ip_filter.hpp" +#include +//#include + + +namespace libtorrent +{ + void ip_filter::add_rule(address first, address last, int flags) + { + if (first.is_v4()) + { + assert(last.is_v4()); + m_filter4.add_rule(first.to_v4(), last.to_v4(), flags); + } + else if (first.is_v6()) + { + assert(last.is_v6()); + m_filter6.add_rule(first.to_v6(), last.to_v6(), flags); + } + else + assert(false); + } + + int ip_filter::access(address const& addr) const + { + if (addr.is_v4()) + return m_filter4.access(addr.to_v4()); + assert(addr.is_v6()); + return m_filter6.access(addr.to_v6()); + } + + ip_filter::filter_tuple_t ip_filter::export_filter() const + { + return boost::make_tuple(m_filter4.export_filter() + , m_filter6.export_filter()); + } + +/* + void ip_filter::print() const + { + for (range_t::iterator i = m_access_list.begin(); i != m_access_list.end(); ++i) + { + std::cout << i->start.as_string() << " " << i->access << "\n"; + } + } +*/ +} + diff --git a/encryption/libtorrent/src/kademlia/closest_nodes.cpp b/encryption/libtorrent/src/kademlia/closest_nodes.cpp new file mode 100644 index 000000000..0c7d9d276 --- /dev/null +++ b/encryption/libtorrent/src/kademlia/closest_nodes.cpp @@ -0,0 +1,132 @@ +/* + +Copyright (c) 2006, Arvid Norberg & Daniel Wallin +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/pch.hpp" + +#include +#include +#include + +namespace libtorrent { namespace dht +{ + +using asio::ip::udp; + +closest_nodes_observer::~closest_nodes_observer() +{ + if (m_algorithm) m_algorithm->failed(m_self, true); +} + +void closest_nodes_observer::reply(msg const& in) +{ + if (!m_algorithm) + { + assert(false); + return; + } + + if (!in.nodes.empty()) + { + for (msg::nodes_t::const_iterator i = in.nodes.begin() + , end(in.nodes.end()); i != end; ++i) + { + m_algorithm->traverse(i->id, i->addr); + } + } + m_algorithm->finished(m_self); + m_algorithm = 0; +} + +void closest_nodes_observer::timeout() +{ + if (!m_algorithm) return; + m_algorithm->failed(m_self); + m_algorithm = 0; +} + + +closest_nodes::closest_nodes( + node_id target + , int branch_factor + , int max_results + , routing_table& table + , rpc_manager& rpc + , done_callback const& callback +) + : traversal_algorithm( + target + , branch_factor + , max_results + , table + , rpc + , table.begin() + , table.end() + ) + , m_done_callback(callback) +{ + boost::intrusive_ptr self(this); + add_requests(); +} + +void closest_nodes::invoke(node_id const& id, udp::endpoint addr) +{ + observer_ptr o(new (m_rpc.allocator().malloc()) closest_nodes_observer(this, id, m_target)); + m_rpc.invoke(messages::find_node, addr, o); +} + +void closest_nodes::done() +{ + std::vector results; + int result_size = m_table.bucket_size(); + if (result_size > (int)m_results.size()) result_size = (int)m_results.size(); + for (std::vector::iterator i = m_results.begin() + , end(m_results.begin() + result_size); i != end; ++i) + { + results.push_back(node_entry(i->id, i->addr)); + } + m_done_callback(results); +} + +void closest_nodes::initiate( + node_id target + , int branch_factor + , int max_results + , routing_table& table + , rpc_manager& rpc + , done_callback const& callback +) +{ + new closest_nodes(target, branch_factor, max_results, table, rpc, callback); +} + +} } // namespace libtorrent::dht + diff --git a/encryption/libtorrent/src/kademlia/dht_tracker.cpp b/encryption/libtorrent/src/kademlia/dht_tracker.cpp new file mode 100644 index 000000000..eda6cd864 --- /dev/null +++ b/encryption/libtorrent/src/kademlia/dht_tracker.cpp @@ -0,0 +1,974 @@ +/* + +Copyright (c) 2006, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/pch.hpp" + +#include +#include +#include +#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/dht_tracker.hpp" + +#include "libtorrent/socket.hpp" +#include "libtorrent/bencode.hpp" +#include "libtorrent/io.hpp" +#include "libtorrent/version.hpp" + +using boost::ref; +using boost::lexical_cast; +using libtorrent::dht::node_impl; +using libtorrent::dht::node_id; +using libtorrent::dht::packet_t; +using libtorrent::dht::msg; +using libtorrent::dht::packet_iterator; +namespace messages = libtorrent::dht::messages; +using namespace libtorrent::detail; + +enum +{ + key_refresh = 5 // generate a new write token key every 5 minutes +}; + +using asio::ip::udp; +typedef asio::ip::address_v4 address; + +namespace +{ + const int tick_period = 1; // minutes + + struct count_peers + { + int& count; + count_peers(int& c): count(c) {} + void operator()(std::pair const& t) + { + count += std::distance(t.second.peers.begin() + , t.second.peers.end()); + } + }; + + boost::optional read_id(libtorrent::entry const& d) + { + using namespace libtorrent; + using libtorrent::dht::node_id; + + if (d.type() != entry::dictionary_t) return boost::optional(); + entry const* nid = d.find_key("node-id"); + if (!nid + || nid->type() != entry::string_t + || nid->string().length() != 40) + return boost::optional(); + return boost::optional( + boost::lexical_cast(nid->string())); + } + + template + void read_endpoint_list(libtorrent::entry const* n, std::vector& epl) + { + using namespace libtorrent; + entry::list_type const& contacts = n->list(); + for (entry::list_type::const_iterator i = contacts.begin() + , end(contacts.end()); i != end; ++i) + { + std::string const& p = i->string(); + if (p.size() < 6) continue; + std::string::const_iterator in = p.begin(); + if (p.size() == 6) + epl.push_back(read_v4_endpoint(in)); + else if (p.size() == 18) + epl.push_back(read_v6_endpoint(in)); + } + } + +} + +namespace libtorrent { namespace dht +{ + + void intrusive_ptr_add_ref(dht_tracker const* c) + { + assert(c != 0); + assert(c->m_refs >= 0); + ++c->m_refs; + } + + void intrusive_ptr_release(dht_tracker const* c) + { + assert(c != 0); + assert(c->m_refs > 0); + if (--c->m_refs == 0) + delete c; + } + +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_DEFINE_LOG(dht_tracker) +#endif + + // class that puts the networking and the kademlia node in a single + // unit and connecting them together. + dht_tracker::dht_tracker(asio::io_service& ios, dht_settings const& settings + , asio::ip::address listen_interface, entry const& bootstrap) + : m_strand(ios) + , m_socket(ios, udp::endpoint(listen_interface, settings.service_port)) + , m_dht(bind(&dht_tracker::send_packet, this, _1), settings + , read_id(bootstrap)) + , m_buffer(0) + , m_last_new_key(time_now() - minutes(key_refresh)) + , m_timer(ios) + , m_connection_timer(ios) + , m_refresh_timer(ios) + , m_settings(settings) + , m_refresh_bucket(160) + , m_host_resolver(ios) + , m_refs(0) + { + using boost::bind; + + m_in_buf[0].resize(1000); + m_in_buf[1].resize(1000); +#ifdef TORRENT_DHT_VERBOSE_LOGGING + m_counter = 0; + std::fill_n(m_replies_bytes_sent, 5, 0); + std::fill_n(m_queries_bytes_received, 5, 0); + std::fill_n(m_replies_sent, 5, 0); + std::fill_n(m_queries_received, 5, 0); + m_announces = 0; + m_failed_announces = 0; + m_total_message_input = 0; + m_ut_message_input = 0; + m_lt_message_input = 0; + m_mp_message_input = 0; + m_gr_message_input = 0; + m_mo_message_input = 0; + m_total_in_bytes = 0; + m_total_out_bytes = 0; + m_queries_out_bytes = 0; + + // turns on and off individual components' logging + +// rpc_log().enable(false); +// node_log().enable(false); +// traversal_log().enable(false); +// dht_tracker_log.enable(false); + +#endif + std::vector initial_nodes; + + if (bootstrap.type() == entry::dictionary_t) + { + try + { + if (entry const* nodes = bootstrap.find_key("nodes")) + read_endpoint_list(nodes, initial_nodes); + } catch (std::exception&) {} + } + + m_socket.async_receive_from(asio::buffer(&m_in_buf[m_buffer][0] + , m_in_buf[m_buffer].size()), m_remote_endpoint[m_buffer] + , m_strand.wrap(bind(&dht_tracker::on_receive, self(), _1, _2))); + m_timer.expires_from_now(seconds(1)); + m_timer.async_wait(m_strand.wrap(bind(&dht_tracker::tick, self(), _1))); + + m_connection_timer.expires_from_now(seconds(10)); + m_connection_timer.async_wait(m_strand.wrap( + bind(&dht_tracker::connection_timeout, self(), _1))); + + m_refresh_timer.expires_from_now(seconds(5)); + m_refresh_timer.async_wait(m_strand.wrap(bind(&dht_tracker::refresh_timeout, self(), _1))); + + m_dht.bootstrap(initial_nodes, bind(&dht_tracker::on_bootstrap, self())); + } + + void dht_tracker::stop() + { + m_timer.cancel(); + m_connection_timer.cancel(); + m_refresh_timer.cancel(); + m_socket.close(); + } + + void dht_tracker::dht_status(session_status& s) + { + boost::tie(s.dht_nodes, s.dht_node_cache) = m_dht.size(); + s.dht_torrents = m_dht.data_size(); + s.dht_global_nodes = m_dht.num_global_nodes(); + } + + void dht_tracker::connection_timeout(asio::error_code const& e) + try + { + if (e) return; + time_duration d = m_dht.connection_timeout(); + m_connection_timer.expires_from_now(d); + m_connection_timer.async_wait(m_strand.wrap(bind(&dht_tracker::connection_timeout, self(), _1))); + } + catch (std::exception& exc) + { +#ifndef NDEBUG + std::cerr << "exception-type: " << typeid(exc).name() << std::endl; + std::cerr << "what: " << exc.what() << std::endl; + assert(false); +#endif + }; + + void dht_tracker::refresh_timeout(asio::error_code const& e) + try + { + if (e) return; + time_duration d = m_dht.refresh_timeout(); + m_refresh_timer.expires_from_now(d); + m_refresh_timer.async_wait(m_strand.wrap( + bind(&dht_tracker::refresh_timeout, self(), _1))); + } + catch (std::exception&) + { + assert(false); + }; + + void dht_tracker::rebind(asio::ip::address listen_interface, int listen_port) + { + m_socket.close(); + udp::endpoint ep(listen_interface, listen_port); + m_socket.open(ep.protocol()); + m_socket.bind(ep); + } + + void dht_tracker::tick(asio::error_code const& e) + try + { + if (e) return; + m_timer.expires_from_now(minutes(tick_period)); + m_timer.async_wait(m_strand.wrap(bind(&dht_tracker::tick, this, _1))); + + ptime now = time_now(); + if (now - m_last_new_key > minutes(key_refresh)) + { + m_last_new_key = now; + m_dht.new_write_key(); +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(dht_tracker) << time_now_string() << " new write key"; +#endif + } + +#ifdef TORRENT_DHT_VERBOSE_LOGGING + static bool first = true; + if (first) + { + boost::filesystem::create_directory("libtorrent_logs"); + } + + std::ofstream st("libtorrent_logs/routing_table_state.txt", std::ios_base::trunc); + m_dht.print_state(st); + + // count torrents + int torrents = std::distance(m_dht.begin_data(), m_dht.end_data()); + + // count peers + int peers = 0; + std::for_each(m_dht.begin_data(), m_dht.end_data(), count_peers(peers)); + + std::ofstream pc("libtorrent_logs/dht_stats.log", std::ios_base::app); + if (first) + { + first = false; + pc << "\n\n ***** starting log at " << time_now_string() << " *****\n\n" + << "minute:active nodes:passive nodes" + ":ping replies sent:ping queries recvd:ping" + ":ping replies sent:ping queries recvd:ping" + ":find_node replies bytes sent:find_node queries bytes recv" + ":find_node replies bytes sent:find_node queries bytes recv" + ":get_peers replies sent:get_peers queries recvd:get_peers" + ":get_peers replies bytes sent:get_peers queries bytes recv" + ":announce_peer replies sent:announce_peer queries recvd:announce_peer" + ":announce_peer replies bytes sent:announce_peer queries bytes recv" + ":error replies sent:error queries recvd:error" + ":error replies bytes sent:error queries bytes recv" + ":num torrents:num peers:announces per min" + ":failed announces per min:total msgs per min" + ":ut msgs per min:lt msgs per min:mp msgs per min" + ":gr msgs per min:bytes in per sec:bytes out per sec" + ":queries out bytes per sec\n\n"; + } + + int active; + int passive; + boost::tie(active, passive) = m_dht.size(); + pc << (m_counter * tick_period) + << "\t" << active + << "\t" << passive; + for (int i = 0; i < 5; ++i) + pc << "\t" << (m_replies_sent[i] / float(tick_period)) + << "\t" << (m_queries_received[i] / float(tick_period)) + << "\t" << (m_replies_bytes_sent[i] / float(tick_period*60)) + << "\t" << (m_queries_bytes_received[i] / float(tick_period*60)); + + pc << "\t" << torrents + << "\t" << peers + << "\t" << m_announces / float(tick_period) + << "\t" << m_failed_announces / float(tick_period) + << "\t" << (m_total_message_input / float(tick_period)) + << "\t" << (m_ut_message_input / float(tick_period)) + << "\t" << (m_lt_message_input / float(tick_period)) + << "\t" << (m_mp_message_input / float(tick_period)) + << "\t" << (m_gr_message_input / float(tick_period)) + << "\t" << (m_mo_message_input / float(tick_period)) + << "\t" << (m_total_in_bytes / float(tick_period*60)) + << "\t" << (m_total_out_bytes / float(tick_period*60)) + << "\t" << (m_queries_out_bytes / float(tick_period*60)) + << std::endl; + ++m_counter; + std::fill_n(m_replies_bytes_sent, 5, 0); + std::fill_n(m_queries_bytes_received, 5, 0); + std::fill_n(m_replies_sent, 5, 0); + std::fill_n(m_queries_received, 5, 0); + m_announces = 0; + m_failed_announces = 0; + m_total_message_input = 0; + m_ut_message_input = 0; + m_lt_message_input = 0; + m_total_in_bytes = 0; + m_total_out_bytes = 0; + m_queries_out_bytes = 0; +#endif + } + catch (std::exception&) + { + assert(false); + }; + + void dht_tracker::announce(sha1_hash const& ih, int listen_port + , boost::function const& + , sha1_hash const&)> f) + { + m_dht.announce(ih, listen_port, f); + } + + // translate bittorrent kademlia message into the generice kademlia message + // used by the library + void dht_tracker::on_receive(asio::error_code const& error, size_t bytes_transferred) + try + { + if (error == asio::error::operation_aborted) return; + + int current_buffer = m_buffer; + m_buffer = (m_buffer + 1) & 1; + m_socket.async_receive_from(asio::buffer(&m_in_buf[m_buffer][0] + , m_in_buf[m_buffer].size()), m_remote_endpoint[m_buffer] + , m_strand.wrap(bind(&dht_tracker::on_receive, self(), _1, _2))); + + if (error) return; + +#ifdef TORRENT_DHT_VERBOSE_LOGGING + ++m_total_message_input; + m_total_in_bytes += bytes_transferred; +#endif + + try + { + using libtorrent::entry; + using libtorrent::bdecode; + + assert(bytes_transferred > 0); + + entry e = bdecode(m_in_buf[current_buffer].begin() + , m_in_buf[current_buffer].end()); + +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(dht_tracker) << time_now_string() << " RECEIVED [" + << m_remote_endpoint[current_buffer] << "]:"; +#endif + + libtorrent::dht::msg m; + m.message_id = 0; + m.addr = m_remote_endpoint[current_buffer]; + m.transaction_id = e["t"].string(); + +#ifdef TORRENT_DHT_VERBOSE_LOGGING + try + { + entry const* ver = e.find_key("v"); + if (!ver) throw std::exception(); + + std::string const& client = ver->string(); + if (client.size() > 1 && std::equal(client.begin(), client.begin() + 2, "UT")) + { + ++m_ut_message_input; + TORRENT_LOG(dht_tracker) << " client: uTorrent"; + } + else if (client.size() > 1 && std::equal(client.begin(), client.begin() + 2, "LT")) + { + ++m_lt_message_input; + TORRENT_LOG(dht_tracker) << " client: libtorrent"; + } + else if (client.size() > 1 && std::equal(client.begin(), client.begin() + 2, "MP")) + { + ++m_mp_message_input; + TORRENT_LOG(dht_tracker) << " client: MooPolice"; + } + else if (client.size() > 1 && std::equal(client.begin(), client.begin() + 2, "GR")) + { + ++m_gr_message_input; + TORRENT_LOG(dht_tracker) << " client: GetRight"; + } + else if (client.size() > 1 && std::equal(client.begin(), client.begin() + 2, "MO")) + { + ++m_mo_message_input; + TORRENT_LOG(dht_tracker) << " client: Mono Torrent"; + } + else + { + TORRENT_LOG(dht_tracker) << " client: " << client; + } + } + catch (std::exception&) + { + TORRENT_LOG(dht_tracker) << " client: generic"; + }; +#endif + + std::string const& msg_type = e["y"].string(); + + if (msg_type == "r") + { +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(dht_tracker) << " reply: transaction: " + << m.transaction_id; +#endif + + m.reply = true; + entry const& r = e["r"]; + std::string const& id = r["id"].string(); + if (id.size() != 20) throw std::runtime_error("invalid size of id"); + std::copy(id.begin(), id.end(), m.id.begin()); + + if (entry const* n = r.find_key("values")) + { + m.peers.clear(); + if (n->list().size() == 1) + { + // assume it's mainline format + std::string const& peers = n->list().front().string(); + std::string::const_iterator i = peers.begin(); + std::string::const_iterator end = peers.end(); + + while (std::distance(i, end) >= 6) + m.peers.push_back(read_v4_endpoint(i)); + } + else + { + // assume it's uTorrent/libtorrent format + read_endpoint_list(n, m.peers); + } +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(dht_tracker) << " peers: " << m.peers.size(); +#endif + } + + m.nodes.clear(); + if (entry const* n = r.find_key("nodes")) + { + std::string const& nodes = n->string(); + std::string::const_iterator i = nodes.begin(); + std::string::const_iterator end = nodes.end(); + + while (std::distance(i, end) >= 26) + { + node_id id; + std::copy(i, i + 20, id.begin()); + i += 20; + m.nodes.push_back(libtorrent::dht::node_entry( + id, read_v4_endpoint(i))); + } +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(dht_tracker) << " nodes: " << m.nodes.size(); +#endif + } + + if (entry const* n = r.find_key("nodes2")) + { + entry::list_type const& contacts = n->list(); + for (entry::list_type::const_iterator i = contacts.begin() + , end(contacts.end()); i != end; ++i) + { + std::string const& p = i->string(); + if (p.size() < 6 + 20) continue; + std::string::const_iterator in = p.begin(); + + node_id id; + std::copy(in, in + 20, id.begin()); + in += 20; + if (p.size() == 6 + 20) + m.nodes.push_back(libtorrent::dht::node_entry( + id, read_v4_endpoint(in))); + else if (p.size() == 18 + 20) + m.nodes.push_back(libtorrent::dht::node_entry( + id, read_v6_endpoint(in))); + } +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(dht_tracker) << " nodes2 + nodes: " << m.nodes.size(); +#endif + } + + entry const* token = r.find_key("token"); + if (token) m.write_token = *token; + } + else if (msg_type == "q") + { + m.reply = false; + entry const& a = e["a"]; + std::string const& id = a["id"].string(); + if (id.size() != 20) throw std::runtime_error("invalid size of id"); + std::copy(id.begin(), id.end(), m.id.begin()); + + std::string request_kind(e["q"].string()); +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(dht_tracker) << " query: " << request_kind; +#endif + + if (request_kind == "ping") + { + m.message_id = libtorrent::dht::messages::ping; + } + else if (request_kind == "find_node") + { + std::string const& target = a["target"].string(); + if (target.size() != 20) throw std::runtime_error("invalid size of target id"); + std::copy(target.begin(), target.end(), m.info_hash.begin()); +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(dht_tracker) << " target: " + << boost::lexical_cast(m.info_hash); +#endif + + m.message_id = libtorrent::dht::messages::find_node; + } + else if (request_kind == "get_peers") + { + std::string const& info_hash = a["info_hash"].string(); + if (info_hash.size() != 20) throw std::runtime_error("invalid size of info-hash"); + std::copy(info_hash.begin(), info_hash.end(), m.info_hash.begin()); + m.message_id = libtorrent::dht::messages::get_peers; +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(dht_tracker) << " info_hash: " + << boost::lexical_cast(m.info_hash); +#endif + } + else if (request_kind == "announce_peer") + { +#ifdef TORRENT_DHT_VERBOSE_LOGGING + ++m_announces; +#endif + std::string const& info_hash = a["info_hash"].string(); + if (info_hash.size() != 20) + throw std::runtime_error("invalid size of info-hash"); + std::copy(info_hash.begin(), info_hash.end(), m.info_hash.begin()); + m.port = a["port"].integer(); + m.write_token = a["token"]; + m.message_id = libtorrent::dht::messages::announce_peer; +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(dht_tracker) << " info_hash: " + << boost::lexical_cast(m.info_hash); + TORRENT_LOG(dht_tracker) << " port: " << m.port; + + if (!m_dht.verify_token(m)) + ++m_failed_announces; +#endif + } + else + { +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(dht_tracker) << " *** UNSUPPORTED REQUEST *** : " + << request_kind; +#endif + throw std::runtime_error("unsupported request: " + request_kind); + } + } + else if (msg_type == "e") + { + entry::list_type const& list = e["e"].list(); + m.message_id = messages::error; + m.error_msg = list.back().string(); + m.error_code = list.front().integer(); +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(dht_tracker) << " incoming error: " << m.error_code << " " + << m.error_msg; +#endif + throw std::runtime_error("DHT error message"); + } + else + { +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(dht_tracker) << " *** UNSUPPORTED MESSAGE TYPE *** : " + << msg_type; +#endif + throw std::runtime_error("unsupported message type: " + msg_type); + } + +#ifdef TORRENT_DHT_VERBOSE_LOGGING + if (!m.reply) + { + ++m_queries_received[m.message_id]; + m_queries_bytes_received[m.message_id] += int(bytes_transferred); + } + TORRENT_LOG(dht_tracker) << e; +#endif + assert(m.message_id != messages::error); + m_dht.incoming(m); + } + catch (std::exception& e) + { +#ifdef TORRENT_DHT_VERBOSE_LOGGING + int current_buffer = (m_buffer + 1) & 1; + std::string msg(m_in_buf[current_buffer].begin() + , m_in_buf[current_buffer].begin() + bytes_transferred); + TORRENT_LOG(dht_tracker) << "invalid incoming packet: " + << e.what() << "\n" << msg << "\n"; +#endif + } + } + catch (std::exception& e) + { + assert(false); + }; + + entry dht_tracker::state() const + { + entry ret(entry::dictionary_t); + { + entry nodes(entry::list_t); + for (node_impl::iterator i(m_dht.begin()) + , end(m_dht.end()); i != end; ++i) + { + std::string node; + std::back_insert_iterator out(node); + write_endpoint(i->addr, out); + nodes.list().push_back(entry(node)); + } + bucket_t cache; + m_dht.replacement_cache(cache); + for (bucket_t::iterator i(cache.begin()) + , end(cache.end()); i != end; ++i) + { + std::string node; + std::back_insert_iterator out(node); + write_endpoint(i->addr, out); + nodes.list().push_back(entry(node)); + } + if (!nodes.list().empty()) + ret["nodes"] = nodes; + } + + ret["node-id"] = boost::lexical_cast(m_dht.nid()); + return ret; + } + + void dht_tracker::add_node(udp::endpoint node) + { + m_dht.add_node(node); + } + + void dht_tracker::add_node(std::pair const& node) + { + udp::resolver::query q(node.first, lexical_cast(node.second)); + m_host_resolver.async_resolve(q, m_strand.wrap( + bind(&dht_tracker::on_name_lookup, self(), _1, _2))); + } + + void dht_tracker::on_name_lookup(asio::error_code const& e + , udp::resolver::iterator host) try + { + if (e || host == udp::resolver::iterator()) return; + add_node(host->endpoint()); + } + catch (std::exception&) + { + assert(false); + }; + + void dht_tracker::add_router_node(std::pair const& node) + { + udp::resolver::query q(node.first, lexical_cast(node.second)); + m_host_resolver.async_resolve(q, m_strand.wrap( + bind(&dht_tracker::on_router_name_lookup, self(), _1, _2))); + } + + void dht_tracker::on_router_name_lookup(asio::error_code const& e + , udp::resolver::iterator host) try + { + if (e || host == udp::resolver::iterator()) return; + m_dht.add_router_node(host->endpoint()); + } + catch (std::exception&) + { + assert(false); + }; + + void dht_tracker::on_bootstrap() + {} + + namespace + { + void write_nodes_entry(entry& r, libtorrent::dht::msg const& m) + { + bool ipv6_nodes = false; + r["nodes"] = entry(entry::string_t); + entry& n = r["nodes"]; + std::back_insert_iterator out(n.string()); + for (msg::nodes_t::const_iterator i = m.nodes.begin() + , end(m.nodes.end()); i != end; ++i) + { + if (!i->addr.address().is_v4()) + { + ipv6_nodes = true; + continue; + } + std::copy(i->id.begin(), i->id.end(), out); + write_endpoint(i->addr, out); + } + + if (ipv6_nodes) + { + r["nodes2"] = entry(entry::list_t); + entry& p = r["nodes2"]; + std::string endpoint; + for (msg::nodes_t::const_iterator i = m.nodes.begin() + , end(m.nodes.end()); i != end; ++i) + { + if (!i->addr.address().is_v6()) continue; + endpoint.resize(18 + 20); + std::string::iterator out = endpoint.begin(); + std::copy(i->id.begin(), i->id.end(), out); + out += 20; + write_endpoint(i->addr, out); + endpoint.resize(out - endpoint.begin()); + p.list().push_back(entry(endpoint)); + } + } +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(dht_tracker) << " nodes: " << m.nodes.size(); +#endif + } + } + + void dht_tracker::send_packet(msg const& m) + try + { + using libtorrent::bencode; + using libtorrent::entry; + entry e(entry::dictionary_t); + assert(!m.transaction_id.empty() || m.message_id == messages::error); + e["t"] = m.transaction_id; + static char const version_str[] = {'L', 'T' + , LIBTORRENT_VERSION_MAJOR, LIBTORRENT_VERSION_MINOR}; + e["v"] = std::string(version_str, version_str + 4); + +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(dht_tracker) << time_now_string() + << " SENDING [" << m.addr << "]:"; + TORRENT_LOG(dht_tracker) << " transaction: " << m.transaction_id; +#endif + + if (m.message_id == messages::error) + { + assert(m.reply); + e["y"] = "e"; + entry error_list(entry::list_t); + assert(m.error_code > 200 && m.error_code <= 204); + error_list.list().push_back(entry(m.error_code)); + error_list.list().push_back(entry(m.error_msg)); + e["e"] = error_list; +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(dht_tracker) << time_now_string() + << " outgoing error: " << m.error_code << " " << m.error_msg; +#endif + } + else if (m.reply) + { + e["y"] = "r"; + e["r"] = entry(entry::dictionary_t); + entry& r = e["r"]; + r["id"] = std::string(m.id.begin(), m.id.end()); + +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(dht_tracker) << time_now_string() + << " reply: " << messages::ids[m.message_id]; +#endif + + if (m.write_token.type() != entry::undefined_t) + r["token"] = m.write_token; + + switch (m.message_id) + { + case messages::ping: + break; + case messages::find_node: + { + write_nodes_entry(r, m); + break; + } + case messages::get_peers: + { + if (m.peers.empty()) + { + write_nodes_entry(r, m); + } + else + { + r["values"] = entry(entry::list_t); + entry& p = r["values"]; + std::string endpoint; + for (msg::peers_t::const_iterator i = m.peers.begin() + , end(m.peers.end()); i != end; ++i) + { + endpoint.resize(18); + std::string::iterator out = endpoint.begin(); + write_endpoint(*i, out); + endpoint.resize(out - endpoint.begin()); + p.list().push_back(entry(endpoint)); + } +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(dht_tracker) << " peers: " << m.peers.size(); +#endif + } + break; + } + + case messages::announce_peer: + break; + break; + } + } + else + { + e["y"] = "q"; + e["a"] = entry(entry::dictionary_t); + entry& a = e["a"]; + a["id"] = std::string(m.id.begin(), m.id.end()); + + if (m.write_token.type() != entry::undefined_t) + a["token"] = m.write_token; + assert(m.message_id <= messages::error); + e["q"] = messages::ids[m.message_id]; + +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(dht_tracker) << " query: " + << messages::ids[m.message_id]; +#endif + + switch (m.message_id) + { + case messages::find_node: + { + a["target"] = std::string(m.info_hash.begin(), m.info_hash.end()); +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(dht_tracker) << " target: " + << boost::lexical_cast(m.info_hash); +#endif + break; + } + case messages::get_peers: + { + a["info_hash"] = std::string(m.info_hash.begin(), m.info_hash.end()); +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(dht_tracker) << " info_hash: " + << boost::lexical_cast(m.info_hash); +#endif + break; + } + case messages::announce_peer: + a["port"] = m_settings.service_port; + a["info_hash"] = std::string(m.info_hash.begin(), m.info_hash.end()); + a["token"] = m.write_token; +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(dht_tracker) << " port: " + << m_settings.service_port + << " info_hash: " << boost::lexical_cast(m.info_hash); +#endif + break; + default: break; + } + + } + + m_send_buf.clear(); + bencode(std::back_inserter(m_send_buf), e); + asio::error_code ec; + m_socket.send_to(asio::buffer(&m_send_buf[0] + , (int)m_send_buf.size()), m.addr, 0, ec); + if (ec) return; + +#ifdef TORRENT_DHT_VERBOSE_LOGGING + m_total_out_bytes += m_send_buf.size(); + + if (m.reply) + { + ++m_replies_sent[m.message_id]; + m_replies_bytes_sent[m.message_id] += int(m_send_buf.size()); + } + else + { + m_queries_out_bytes += m_send_buf.size(); + } + TORRENT_LOG(dht_tracker) << e; +#endif + + if (!m.piggy_backed_ping) return; + + msg pm; + pm.reply = false; + pm.piggy_backed_ping = false; + pm.message_id = messages::ping; + pm.transaction_id = m.ping_transaction_id; + pm.id = m.id; + pm.addr = m.addr; + + send_packet(pm); + } + catch (std::exception&) + { + // m_send may fail with "no route to host" + // but it shouldn't throw since an error code + // is passed in instead + assert(false); + } + +}} + diff --git a/encryption/libtorrent/src/kademlia/find_data.cpp b/encryption/libtorrent/src/kademlia/find_data.cpp new file mode 100644 index 000000000..4ada42fb3 --- /dev/null +++ b/encryption/libtorrent/src/kademlia/find_data.cpp @@ -0,0 +1,142 @@ +/* + +Copyright (c) 2006, Arvid Norberg & Daniel Wallin +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/pch.hpp" + +#include +#include +#include +#include + +namespace libtorrent { namespace dht +{ + +find_data_observer::~find_data_observer() +{ + if (m_algorithm) m_algorithm->failed(m_self); +} + +void find_data_observer::reply(msg const& m) +{ + if (!m_algorithm) + { + assert(false); + return; + } + + if (!m.peers.empty()) + { + m_algorithm->got_data(&m); + } + else + { + for (msg::nodes_t::const_iterator i = m.nodes.begin() + , end(m.nodes.end()); i != end; ++i) + { + m_algorithm->traverse(i->id, i->addr); + } + } + m_algorithm->finished(m_self); + m_algorithm = 0; +} + +void find_data_observer::timeout() +{ + if (!m_algorithm) return; + m_algorithm->failed(m_self); + m_algorithm = 0; +} + + +find_data::find_data( + node_id target + , int branch_factor + , int max_results + , routing_table& table + , rpc_manager& rpc + , done_callback const& callback +) + : traversal_algorithm( + target + , branch_factor + , max_results + , table + , rpc + , table.begin() + , table.end() + ) + , m_done_callback(callback) + , m_done(false) +{ + boost::intrusive_ptr self(this); + add_requests(); +} + +void find_data::invoke(node_id const& id, asio::ip::udp::endpoint addr) +{ + if (m_done) + { + m_invoke_count = -1; + return; + } + + observer_ptr o(new (m_rpc.allocator().malloc()) find_data_observer(this, id, m_target)); + m_rpc.invoke(messages::get_peers, addr, o); +} + +void find_data::got_data(msg const* m) +{ + m_done = true; + m_done_callback(m); +} + +void find_data::done() +{ + if (m_invoke_count != 0) return; + if (!m_done) m_done_callback(0); +} + +void find_data::initiate( + node_id target + , int branch_factor + , int max_results + , routing_table& table + , rpc_manager& rpc + , done_callback const& callback +) +{ + std::cerr << "find_data::initiate, key: " << target << "\n"; + new find_data(target, branch_factor, max_results, table, rpc, callback); +} + +} } // namespace libtorrent::dht + diff --git a/encryption/libtorrent/src/kademlia/node.cpp b/encryption/libtorrent/src/kademlia/node.cpp new file mode 100644 index 000000000..74641ec43 --- /dev/null +++ b/encryption/libtorrent/src/kademlia/node.cpp @@ -0,0 +1,497 @@ +/* + +Copyright (c) 2006, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/pch.hpp" + +#include +#include +#include +#include +#include + +#include "libtorrent/io.hpp" +#include "libtorrent/hasher.hpp" +#include "libtorrent/random_sample.hpp" +#include "libtorrent/kademlia/node_id.hpp" +#include "libtorrent/kademlia/rpc_manager.hpp" +#include "libtorrent/kademlia/packet_iterator.hpp" +#include "libtorrent/kademlia/routing_table.hpp" +#include "libtorrent/kademlia/node.hpp" + +#include "libtorrent/kademlia/refresh.hpp" +#include "libtorrent/kademlia/closest_nodes.hpp" +#include "libtorrent/kademlia/find_data.hpp" + +using boost::bind; + +namespace libtorrent { namespace dht +{ + +#ifdef _MSC_VER +namespace +{ + char rand() { return (char)std::rand(); } +} +#endif + +// TODO: configurable? +enum { announce_interval = 30 }; + +using asio::ip::udp; + +#ifdef TORRENT_DHT_VERBOSE_LOGGING +TORRENT_DEFINE_LOG(node) +#endif + +node_id generate_id() +{ + char random[20]; + std::srand(std::time(0)); +#ifdef _MSC_VER + std::generate(random, random + 20, &rand); +#else + std::generate(random, random + 20, &std::rand); +#endif + + hasher h; + h.update(random, 20); + return h.final(); +} + +// remove peers that have timed out +void purge_peers(std::set& 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)) < time_now()) + { +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(node) << "peer timed out at: " << i->addr.address(); +#endif + peers.erase(i++); + } + else + ++i; + } +} + +void nop() {} + +node_impl::node_impl(boost::function const& f + , dht_settings const& settings, boost::optional node_id) + : m_settings(settings) + , m_id(node_id ? *node_id : generate_id()) + , m_table(m_id, 8, settings) + , m_rpc(bind(&node_impl::incoming_request, this, _1) + , m_id, m_table, f) + , m_last_tracker_tick(time_now()) +{ + m_secret[0] = std::rand(); + m_secret[1] = std::rand(); +} + +bool node_impl::verify_token(msg const& m) +{ + if (m.write_token.type() != entry::string_t) + { +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(node) << "token of incorrect type " << m.write_token.type(); +#endif + return false; + } + std::string const& token = m.write_token.string(); + if (token.length() != 4) + { +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(node) << "token of incorrect length: " << token.length(); +#endif + return false; + } + + hasher h1; + std::string address = m.addr.address().to_string(); + h1.update(&address[0], address.length()); + h1.update((char*)&m_secret[0], sizeof(m_secret[0])); + h1.update((char*)&m.info_hash[0], sha1_hash::size); + + sha1_hash h = h1.final(); + if (std::equal(token.begin(), token.end(), (signed char*)&h[0])) + return true; + + hasher h2; + h2.update(&address[0], address.length()); + h2.update((char*)&m_secret[1], sizeof(m_secret[1])); + h2.update((char*)&m.info_hash[0], sha1_hash::size); + h = h2.final(); + if (std::equal(token.begin(), token.end(), (signed char*)&h[0])) + return true; + return false; +} + +entry node_impl::generate_token(msg const& m) +{ + std::string token; + token.resize(4); + hasher h; + std::string address = m.addr.address().to_string(); + h.update(&address[0], address.length()); + h.update((char*)&m_secret[0], sizeof(m_secret[0])); + h.update((char*)&m.info_hash[0], sha1_hash::size); + + sha1_hash hash = h.final(); + std::copy(hash.begin(), hash.begin() + 4, (signed char*)&token[0]); + return entry(token); +} + +void node_impl::refresh(node_id const& id + , boost::function0 f) +{ + // use the 'bucket size' closest nodes + // to start the refresh with + std::vector start; + start.reserve(m_table.bucket_size()); + m_table.find_node(id, start, false); + refresh::initiate(id, m_settings.search_branching, 10, m_table.bucket_size() + , m_table, start.begin(), start.end(), m_rpc, f); +} + +void node_impl::bootstrap(std::vector const& nodes + , boost::function0 f) +{ + std::vector start; + start.reserve(nodes.size()); + std::copy(nodes.begin(), nodes.end(), std::back_inserter(start)); + refresh::initiate(m_id, m_settings.search_branching, 10, m_table.bucket_size() + , m_table, start.begin(), start.end(), m_rpc, f); +} + +void node_impl::refresh() +{ + std::vector start; + start.reserve(m_table.size().get<0>()); + std::copy(m_table.begin(), m_table.end(), std::back_inserter(start)); + + refresh::initiate(m_id, m_settings.search_branching, 10, m_table.bucket_size() + , m_table, start.begin(), start.end(), m_rpc, bind(&nop)); +} + +int node_impl::bucket_size(int bucket) +{ + return m_table.bucket_size(bucket); +} + +void node_impl::new_write_key() +{ + m_secret[1] = m_secret[0]; + m_secret[0] = std::rand(); +} + +void node_impl::refresh_bucket(int bucket) try +{ + assert(bucket >= 0 && bucket < 160); + + // generate a random node_id within the given bucket + node_id target = generate_id(); + int num_bits = 160 - bucket; + node_id mask(0); + for (int i = 0; i < num_bits; ++i) + { + int byte = i / 8; + mask[byte] |= 0x80 >> (i % 8); + } + + node_id root = m_id; + root &= mask; + target &= ~mask; + target |= root; + + // make sure this is in another subtree than m_id + // clear the (num_bits - 1) bit and then set it to the + // inverse of m_id's corresponding bit. + target[(num_bits - 1) / 8] &= ~(0x80 >> ((num_bits - 1) % 8)); + target[(num_bits - 1) / 8] |= + (~(m_id[(num_bits - 1) / 8])) & (0x80 >> ((num_bits - 1) % 8)); + + assert(distance_exp(m_id, target) == bucket); + + std::vector start; + start.reserve(m_table.bucket_size()); + m_table.find_node(target, start, false, m_table.bucket_size()); + + refresh::initiate(target, m_settings.search_branching, 10, m_table.bucket_size() + , m_table, start.begin(), start.end(), m_rpc, bind(&nop)); + m_table.touch_bucket(bucket); +} +catch (std::exception&) {} + +void node_impl::incoming(msg const& m) +{ + if (m_rpc.incoming(m)) + { + refresh(); + } +} + +namespace +{ + void announce_fun(std::vector const& v, rpc_manager& rpc + , int listen_port, sha1_hash const& ih + , boost::function const&, sha1_hash const&)> f) + { + bool nodes = false; + // only store on the first k nodes + for (std::vector::const_iterator i = v.begin() + , end(v.end()); i != end; ++i) + { + rpc.invoke(messages::get_peers, i->addr, observer_ptr( + new (rpc.allocator().malloc()) get_peers_observer(ih, listen_port, rpc, f))); + nodes = true; + } + } +} + +void node_impl::add_router_node(udp::endpoint router) +{ + m_table.add_router_node(router); +} + +void node_impl::add_node(udp::endpoint node) +{ + // ping the node, and if we get a reply, it + // will be added to the routing table + observer_ptr o(new (m_rpc.allocator().malloc()) null_observer(m_rpc.allocator())); + m_rpc.invoke(messages::ping, node, o); +} + +void node_impl::announce(sha1_hash const& info_hash, int listen_port + , boost::function const&, sha1_hash const&)> f) +{ + // search for nodes with ids close to id, and then invoke the + // get_peers and then announce_peer rpc on them. + closest_nodes::initiate(info_hash, m_settings.search_branching + , m_table.bucket_size(), m_table, m_rpc + , boost::bind(&announce_fun, _1, boost::ref(m_rpc), listen_port + , info_hash, f)); +} + +time_duration node_impl::refresh_timeout() +{ + int refresh = -1; + ptime now = time_now(); + ptime next = now + minutes(15); + try + { + for (int i = 0; i < 160; ++i) + { + ptime r = m_table.next_refresh(i); + if (r <= next) + { + refresh = i; + next = r; + } + } + if (next < now) + { + assert(refresh > -1); +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(node) << "refreshing bucket: " << refresh; +#endif + refresh_bucket(refresh); + } + } + catch (std::exception&) {} + + time_duration next_refresh = next - now; + time_duration min_next_refresh + = minutes(15) / (m_table.num_active_buckets()); + if (min_next_refresh > seconds(40)) + min_next_refresh = seconds(40); + + if (next_refresh < min_next_refresh) + next_refresh = min_next_refresh; + +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(node) << "next refresh: " << total_seconds(next_refresh) << " seconds"; +#endif + + return next_refresh; +} + +time_duration node_impl::connection_timeout() +{ + time_duration d = m_rpc.tick(); + try + { + ptime now(time_now()); + if (now - m_last_tracker_tick < minutes(10)) return d; + m_last_tracker_tick = now; + + // look through all peers and see if any have timed out + for (data_iterator i = begin_data(), end(end_data()); i != end;) + { + torrent_entry& t = i->second; + node_id const& key = i->first; + ++i; + purge_peers(t.peers); + + // if there are no more peers, remove the entry altogether + if (t.peers.empty()) + { + table_t::iterator i = m_map.find(key); + if (i != m_map.end()) m_map.erase(i); + } + } + } + catch (std::exception&) {} + + return d; +} + +void node_impl::on_announce(msg const& m, msg& reply) +{ + if (!verify_token(m)) + { + reply.message_id = messages::error; + reply.error_code = 203; + reply.error_msg = "Incorrect token in announce_peer"; + return; + } + + // the token was correct. That means this + // node is not spoofing its address. So, let + // the table get a chance to add it. + m_table.node_seen(m.id, m.addr); + + torrent_entry& v = m_map[m.info_hash]; + peer_entry e; + e.addr = tcp::endpoint(m.addr.address(), m.addr.port()); + e.added = time_now(); + std::set::iterator i = v.peers.find(e); + if (i != v.peers.end()) v.peers.erase(i++); + v.peers.insert(i, e); +} + +namespace +{ + tcp::endpoint get_endpoint(peer_entry const& p) + { + return p.addr; + } +} + +bool node_impl::on_find(msg const& m, std::vector& peers) const +{ + table_t::const_iterator i = m_map.find(m.info_hash); + if (i == m_map.end()) return false; + + torrent_entry const& v = i->second; + + int num = (std::min)((int)v.peers.size(), m_settings.max_peers_reply); + peers.clear(); + peers.reserve(num); + random_sample_n(boost::make_transform_iterator(v.peers.begin(), &get_endpoint) + , boost::make_transform_iterator(v.peers.end(), &get_endpoint) + , std::back_inserter(peers), num); + +#ifdef TORRENT_DHT_VERBOSE_LOGGING + for (std::vector::iterator i = peers.begin() + , end(peers.end()); i != end; ++i) + { + TORRENT_LOG(node) << " " << *i; + } +#endif + return true; +} + +void node_impl::incoming_request(msg const& m) +{ + msg reply; + reply.message_id = m.message_id; + reply.addr = m.addr; + reply.reply = true; + reply.transaction_id = m.transaction_id; + + switch (m.message_id) + { + case messages::ping: + break; + case messages::get_peers: + { + reply.info_hash = m.info_hash; + reply.write_token = generate_token(m); + + if (!on_find(m, reply.peers)) + { + // we don't have any peers for this info_hash, + // return nodes instead + m_table.find_node(m.info_hash, reply.nodes, false); +#ifdef TORRENT_DHT_VERBOSE_LOGGING + for (std::vector::iterator i = reply.nodes.begin() + , end(reply.nodes.end()); i != end; ++i) + { + TORRENT_LOG(node) << " " << i->id << " " << i->addr; + } +#endif + } + } + break; + case messages::find_node: + { + reply.info_hash = m.info_hash; + + m_table.find_node(m.info_hash, reply.nodes, false); +#ifdef TORRENT_DHT_VERBOSE_LOGGING + for (std::vector::iterator i = reply.nodes.begin() + , end(reply.nodes.end()); i != end; ++i) + { + TORRENT_LOG(node) << " " << i->id << " " << i->addr; + } +#endif + } + break; + case messages::announce_peer: + on_announce(m, reply); + break; + default: + assert(false); + }; + + if (m_table.need_node(m.id)) + m_rpc.reply_with_ping(reply); + else + m_rpc.reply(reply); +} + + +} } // namespace libtorrent::dht diff --git a/encryption/libtorrent/src/kademlia/node_id.cpp b/encryption/libtorrent/src/kademlia/node_id.cpp new file mode 100644 index 000000000..4ed413714 --- /dev/null +++ b/encryption/libtorrent/src/kademlia/node_id.cpp @@ -0,0 +1,99 @@ +/* + +Copyright (c) 2006, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/pch.hpp" + +#include +#include +#include +#include + +#include "libtorrent/kademlia/node_id.hpp" + +using boost::bind; + +namespace libtorrent { namespace dht +{ + +// returns the distance between the two nodes +// using the kademlia XOR-metric +node_id distance(node_id const& n1, node_id const& n2) +{ + node_id ret; + node_id::iterator k = ret.begin(); + for (node_id::const_iterator i = n1.begin(), j = n2.begin() + , end(n1.end()); i != end; ++i, ++j, ++k) + { + *k = *i ^ *j; + } + return ret; +} + +// returns true if: distance(n1, ref) < distance(n2, ref) +bool compare_ref(node_id const& n1, node_id const& n2, node_id const& ref) +{ + for (node_id::const_iterator i = n1.begin(), j = n2.begin() + , k = ref.begin(), end(n1.end()); i != end; ++i, ++j, ++k) + { + boost::uint8_t lhs = (*i ^ *k); + boost::uint8_t rhs = (*j ^ *k); + if (lhs < rhs) return true; + if (lhs > rhs) return false; + } + return false; +} + +// returns n in: 2^n <= distance(n1, n2) < 2^(n+1) +// useful for finding out which bucket a node belongs to +int distance_exp(node_id const& n1, node_id const& n2) +{ + int byte = node_id::size - 1; + for (node_id::const_iterator i = n1.begin(), j = n2.begin() + , end(n1.end()); i != end; ++i, ++j, --byte) + { + assert(byte >= 0); + boost::uint8_t t = *i ^ *j; + if (t == 0) continue; + // we have found the first non-zero byte + // return the bit-number of the first bit + // that differs + int bit = byte * 8; + for (int b = 7; b > 0; --b) + if (t >= (1 << b)) return bit + b; + return bit; + } + + return 0; +} + +} } // namespace libtorrent::dht + diff --git a/encryption/libtorrent/src/kademlia/refresh.cpp b/encryption/libtorrent/src/kademlia/refresh.cpp new file mode 100644 index 000000000..ce94ca93b --- /dev/null +++ b/encryption/libtorrent/src/kademlia/refresh.cpp @@ -0,0 +1,174 @@ +/* + +Copyright (c) 2006, Arvid Norberg & Daniel Wallin +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/pch.hpp" + +#include +#include +#include +#include +#include + +#include + +#include + +using boost::bind; + +namespace libtorrent { namespace dht +{ + +using asio::ip::udp; + +#ifdef TORRENT_DHT_VERBOSE_LOGGING +TORRENT_DEFINE_LOG(refresh) +#endif + +refresh_observer::~refresh_observer() +{ + if (m_algorithm) m_algorithm->failed(m_self, true); +} + +void refresh_observer::reply(msg const& in) +{ + if (!m_algorithm) return; + + if (!in.nodes.empty()) + { + for (msg::nodes_t::const_iterator i = in.nodes.begin() + , end(in.nodes.end()); i != end; ++i) + { + m_algorithm->traverse(i->id, i->addr); + } + } + m_algorithm->finished(m_self); + m_algorithm = 0; +} + +void refresh_observer::timeout() +{ + if (!m_algorithm) return; + m_algorithm->failed(m_self); + m_algorithm = 0; +} + +ping_observer::~ping_observer() +{ + if (m_algorithm) m_algorithm->ping_timeout(m_self, true); +} + +void ping_observer::reply(msg const& m) +{ + if (!m_algorithm) return; + + m_algorithm->ping_reply(m_self); + m_algorithm = 0; +} + +void ping_observer::timeout() +{ + if (!m_algorithm) return; + m_algorithm->ping_timeout(m_self); + m_algorithm = 0; +} + +void refresh::invoke(node_id const& nid, udp::endpoint addr) +{ + observer_ptr o(new (m_rpc.allocator().malloc()) refresh_observer( + this, nid, m_target)); + + m_rpc.invoke(messages::find_node, addr, o); +} + +void refresh::done() +{ + m_leftover_nodes_iterator = (int)m_results.size() > m_max_results ? + m_results.begin() + m_max_results : m_results.end(); + + invoke_pings_or_finish(); +} + +void refresh::ping_reply(node_id nid) +{ + m_active_pings--; + invoke_pings_or_finish(); +} + +void refresh::ping_timeout(node_id nid, bool prevent_request) +{ + m_active_pings--; + invoke_pings_or_finish(prevent_request); +} + +void refresh::invoke_pings_or_finish(bool prevent_request) +{ + if (prevent_request) + { + --m_max_active_pings; + if (m_max_active_pings <= 0) + m_max_active_pings = 1; + } + else + { + while (m_active_pings < m_max_active_pings) + { + if (m_leftover_nodes_iterator == m_results.end()) break; + + result const& node = *m_leftover_nodes_iterator; + + // Skip initial nodes + if (node.flags & result::initial) + { + ++m_leftover_nodes_iterator; + continue; + } + + try + { + observer_ptr o(new (m_rpc.allocator().malloc()) ping_observer( + this, node.id)); + m_rpc.invoke(messages::ping, node.addr, o); + ++m_active_pings; + ++m_leftover_nodes_iterator; + } + catch (std::exception& e) {} + } + } + + if (m_active_pings == 0) + { + m_done_callback(); + } +} + +} } // namespace libtorrent::dht + diff --git a/encryption/libtorrent/src/kademlia/routing_table.cpp b/encryption/libtorrent/src/kademlia/routing_table.cpp new file mode 100644 index 000000000..45091481c --- /dev/null +++ b/encryption/libtorrent/src/kademlia/routing_table.cpp @@ -0,0 +1,448 @@ +/* + +Copyright (c) 2006, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/pch.hpp" + +#include +#include +#include +#include +#include +#include +#include + +#include "libtorrent/kademlia/routing_table.hpp" +#include "libtorrent/kademlia/node_id.hpp" +#include "libtorrent/session_settings.hpp" + +using boost::bind; +using boost::uint8_t; + +namespace libtorrent { namespace dht +{ + +using asio::ip::udp; +typedef asio::ip::address_v4 address; + +routing_table::routing_table(node_id const& id, int bucket_size + , dht_settings const& settings) + : m_bucket_size(bucket_size) + , m_settings(settings) + , m_id(id) + , m_lowest_active_bucket(160) +{ + // distribute the refresh times for the buckets in an + // attempt do even out the network load + for (int i = 0; i < 160; ++i) + m_bucket_activity[i] = time_now() - milliseconds(i*5625); + m_bucket_activity[0] = time_now() - minutes(15); +} + +boost::tuple routing_table::size() const +{ + int nodes = 0; + int replacements = 0; + for (table_t::const_iterator i = m_buckets.begin() + , end(m_buckets.end()); i != end; ++i) + { + nodes += i->first.size(); + replacements += i->second.size(); + } + return boost::make_tuple(nodes, replacements); +} + +size_type routing_table::num_global_nodes() const +{ + int first_full = m_lowest_active_bucket; + int num_nodes = 1; // we are one of the nodes + for (; first_full < 160 + && int(m_buckets[first_full].first.size()) < m_bucket_size; + ++first_full) + { + num_nodes += m_buckets[first_full].first.size(); + } + + return (2 << (160 - first_full)) * num_nodes; +} + +#ifdef TORRENT_DHT_VERBOSE_LOGGING + +void routing_table::print_state(std::ostream& os) const +{ + os << "kademlia routing table state\n" + << "bucket_size: " << m_bucket_size << "\n" + << "global node count: " << num_global_nodes() << "\n" + << "node_id: " << m_id << "\n\n"; + + os << "number of nodes per bucket:\n-- live "; + for (int i = 8; i < 160; ++i) + os << "-"; + os << "\n"; + + for (int k = 0; k < 8; ++k) + { + for (table_t::const_iterator i = m_buckets.begin(), end(m_buckets.end()); + i != end; ++i) + { + os << (int(i->first.size()) > (7 - k) ? "|" : " "); + } + os << "\n"; + } + for (table_t::const_iterator i = m_buckets.begin(), end(m_buckets.end()); + i != end; ++i) + { + os << "+"; + } + os << "\n"; + for (int k = 0; k < 8; ++k) + { + for (table_t::const_iterator i = m_buckets.begin(), end(m_buckets.end()); + i != end; ++i) + { + os << (int(i->second.size()) > k ? "|" : " "); + } + os << "\n"; + } + os << "-- cached "; + for (int i = 10; i < 160; ++i) + os << "-"; + os << "\n\n"; + + os << "nodes:\n"; + for (table_t::const_iterator i = m_buckets.begin(), end(m_buckets.end()); + i != end; ++i) + { + int bucket_index = int(i - m_buckets.begin()); + os << "=== BUCKET = " << bucket_index + << " = " << (bucket_index >= m_lowest_active_bucket?"active":"inactive") + << " = " << total_seconds(time_now() - m_bucket_activity[bucket_index]) + << " s ago ===== \n"; + for (bucket_t::const_iterator j = i->first.begin() + , end(i->first.end()); j != end; ++j) + { + os << "ip: " << j->addr << " fails: " << j->fail_count + << " id: " << j->id << "\n"; + } + } +} + +#endif + +void routing_table::touch_bucket(int bucket) +{ + m_bucket_activity[bucket] = time_now(); +} + +ptime routing_table::next_refresh(int bucket) +{ + assert(bucket < 160); + assert(bucket >= 0); + // lower than or equal to since a refresh of bucket 0 will + // effectively refresh the lowest active bucket as well + if (bucket < m_lowest_active_bucket && bucket > 0) + return time_now() + minutes(15); + return m_bucket_activity[bucket] + minutes(15); +} + +void routing_table::replacement_cache(bucket_t& nodes) const +{ + for (table_t::const_iterator i = m_buckets.begin() + , end(m_buckets.end()); i != end; ++i) + { + std::copy(i->second.begin(), i->second.end() + , std::back_inserter(nodes)); + } +} + +bool routing_table::need_node(node_id const& id) +{ + int bucket_index = distance_exp(m_id, id); + assert(bucket_index < (int)m_buckets.size()); + assert(bucket_index >= 0); + bucket_t& b = m_buckets[bucket_index].first; + bucket_t& rb = m_buckets[bucket_index].second; + + // if the replacement cache is full, we don't + // need another node. The table is fine the + // way it is. + if ((int)rb.size() >= m_bucket_size) return false; + + // if the node already exists, we don't need it + if (std::find_if(b.begin(), b.end(), bind(&node_entry::id, _1) == id) + != b.end()) return false; + + if (std::find_if(rb.begin(), rb.end(), bind(&node_entry::id, _1) == id) + != rb.end()) return false; + + return true; +} + +void routing_table::node_failed(node_id const& id) +{ + int bucket_index = distance_exp(m_id, id); + assert(bucket_index < (int)m_buckets.size()); + assert(bucket_index >= 0); + bucket_t& b = m_buckets[bucket_index].first; + bucket_t& rb = m_buckets[bucket_index].second; + + bucket_t::iterator i = std::find_if(b.begin(), b.end() + , bind(&node_entry::id, _1) == id); + + if (i == b.end()) return; + + // if messages to ourself fails, ignore it + if (bucket_index == 0) return; + + if (rb.empty()) + { + ++i->fail_count; + if (i->fail_count >= m_settings.max_fail_count) + { + b.erase(i); + assert(m_lowest_active_bucket <= bucket_index); + while (m_buckets[m_lowest_active_bucket].first.empty() + && m_lowest_active_bucket < 160) + { + ++m_lowest_active_bucket; + } + } + return; + } + + b.erase(i); + b.push_back(rb.back()); + rb.erase(rb.end() - 1); +} + +void routing_table::add_router_node(udp::endpoint router) +{ + m_router_nodes.insert(router); +} + +// this function is called every time the node sees +// a sign of a node being alive. This node will either +// be inserted in the k-buckets or be moved to the top +// of its bucket. +// the return value indicates if the table needs a refresh. +// if true, the node should refresh the table (i.e. do a find_node +// on its own id) +bool routing_table::node_seen(node_id const& id, udp::endpoint addr) +{ + if (m_router_nodes.find(addr) != m_router_nodes.end()) return false; + int bucket_index = distance_exp(m_id, id); + assert(bucket_index < (int)m_buckets.size()); + assert(bucket_index >= 0); + bucket_t& b = m_buckets[bucket_index].first; + + bucket_t::iterator i = std::find_if(b.begin(), b.end() + , bind(&node_entry::id, _1) == id); + + bool ret = need_bootstrap(); + + //m_bucket_activity[bucket_index] = time_now(); + + if (i != b.end()) + { + // TODO: what do we do if we see a node with + // the same id as a node at a different address? +// assert(i->addr == addr); + + // we already have the node in our bucket + // just move it to the back since it was + // the last node we had any contact with + // in this bucket + b.erase(i); + b.push_back(node_entry(id, addr)); +// TORRENT_LOG(table) << "replacing node: " << id << " " << addr; + return ret; + } + + // if the node was not present in our list + // we will only insert it if there is room + // for it, or if some of our nodes have gone + // offline + if ((int)b.size() < m_bucket_size) + { + if (b.empty()) b.reserve(m_bucket_size); + b.push_back(node_entry(id, addr)); + // if bucket index is 0, the node is ourselves + // don't updated m_lowest_active_bucket + if (bucket_index < m_lowest_active_bucket + && bucket_index > 0) + m_lowest_active_bucket = bucket_index; +// TORRENT_LOG(table) << "inserting node: " << id << " " << addr; + return ret; + } + + // if there is no room, we look for nodes marked as stale + // in the k-bucket. If we find one, we can replace it. + // A node is considered stale if it has failed at least one + // time. Here we choose the node that has failed most times. + // If we don't find one, place this node in the replacement- + // cache and replace any nodes that will fail in the future + // with nodes from that cache. + + i = std::max_element(b.begin(), b.end() + , bind(&node_entry::fail_count, _1) + < bind(&node_entry::fail_count, _2)); + + if (i != b.end() && i->fail_count > 0) + { + // i points to a node that has been marked + // as stale. Replace it with this new one + b.erase(i); + b.push_back(node_entry(id, addr)); +// TORRENT_LOG(table) << "replacing stale node: " << id << " " << addr; + return ret; + } + + // if we don't have any identified stale nodes in + // the bucket, and the bucket is full, we have to + // cache this node and wait until some node fails + // and then replace it. + + bucket_t& rb = m_buckets[bucket_index].second; + + i = std::find_if(rb.begin(), rb.end() + , bind(&node_entry::id, _1) == id); + + // if the node is already in the replacement bucket + // just return. + if (i != rb.end()) return ret; + + if ((int)rb.size() > m_bucket_size) rb.erase(rb.begin()); + if (rb.empty()) rb.reserve(m_bucket_size); + rb.push_back(node_entry(id, addr)); +// TORRENT_LOG(table) << "inserting node in replacement cache: " << id << " " << addr; + return ret; +} + +bool routing_table::need_bootstrap() const +{ + for (const_iterator i = begin(); i != end(); ++i) + { + if (i->fail_count == 0) return false; + } + return true; +} + +// fills the vector with the k nodes from our buckets that +// are nearest to the given id. +void routing_table::find_node(node_id const& target + , std::vector& l, bool include_self, int count) +{ + l.clear(); + if (count == 0) count = m_bucket_size; + l.reserve(count); + + int bucket_index = distance_exp(m_id, target); + bucket_t& b = m_buckets[bucket_index].first; + + // copy all nodes that hasn't failed into the target + // vector. + std::remove_copy_if(b.begin(), b.end(), std::back_inserter(l) + , bind(&node_entry::fail_count, _1)); + assert((int)l.size() <= count); + + if ((int)l.size() == count) + { + assert(std::count_if(l.begin(), l.end() + , boost::bind(&node_entry::fail_count, _1) != 0) == 0); + return; + } + + // if we didn't have enough nodes in that bucket + // we have to reply with nodes from buckets closer + // to us. i.e. all the buckets in the range + // [0, bucket_index) if we are to include ourself + // or [1, bucket_index) if not. + bucket_t tmpb; + for (int i = include_self?0:1; i < count; ++i) + { + bucket_t& b = m_buckets[i].first; + std::remove_copy_if(b.begin(), b.end(), std::back_inserter(tmpb) + , bind(&node_entry::fail_count, _1)); + } + + std::random_shuffle(tmpb.begin(), tmpb.end()); + size_t to_copy = (std::min)(m_bucket_size - l.size() + , tmpb.size()); + std::copy(tmpb.begin(), tmpb.begin() + to_copy + , std::back_inserter(l)); + + assert((int)l.size() <= m_bucket_size); + + // return if we have enough nodes or if the bucket index + // is the biggest index available (there are no more buckets) + // to look in. + if ((int)l.size() == count + || bucket_index == (int)m_buckets.size() - 1) + { + assert(std::count_if(l.begin(), l.end() + , boost::bind(&node_entry::fail_count, _1) != 0) == 0); + return; + } + + for (size_t i = bucket_index + 1; i < m_buckets.size(); ++i) + { + bucket_t& b = m_buckets[i].first; + + std::remove_copy_if(b.begin(), b.end(), std::back_inserter(l) + , bind(&node_entry::fail_count, _1)); + if ((int)l.size() >= count) + { + l.erase(l.begin() + count, l.end()); + assert(std::count_if(l.begin(), l.end() + , boost::bind(&node_entry::fail_count, _1) != 0) == 0); + return; + } + } + assert((int)l.size() == count + || std::distance(l.begin(), l.end()) < m_bucket_size); + assert((int)l.size() <= count); + + assert(std::count_if(l.begin(), l.end() + , boost::bind(&node_entry::fail_count, _1) != 0) == 0); +} + +routing_table::iterator routing_table::begin() const +{ + return iterator(m_buckets.begin(), m_buckets.end()); +} + +routing_table::iterator routing_table::end() const +{ + return iterator(m_buckets.end(), m_buckets.end()); +} + +} } // namespace libtorrent::dht + diff --git a/encryption/libtorrent/src/kademlia/rpc_manager.cpp b/encryption/libtorrent/src/kademlia/rpc_manager.cpp new file mode 100644 index 000000000..93eac8565 --- /dev/null +++ b/encryption/libtorrent/src/kademlia/rpc_manager.cpp @@ -0,0 +1,430 @@ +/* + +Copyright (c) 2006, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/pch.hpp" +#include "libtorrent/socket.hpp" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using boost::shared_ptr; +using boost::bind; + +namespace libtorrent { namespace dht +{ + +namespace io = libtorrent::detail; +namespace mpl = boost::mpl; + +#ifdef TORRENT_DHT_VERBOSE_LOGGING +TORRENT_DEFINE_LOG(rpc) +#endif + +void intrusive_ptr_add_ref(observer const* o) +{ + assert(o->m_refs >= 0); + assert(o != 0); + ++o->m_refs; +} + +void intrusive_ptr_release(observer const* o) +{ + assert(o->m_refs > 0); + assert(o != 0); + if (--o->m_refs == 0) + { + boost::pool<>& p = o->pool_allocator; + o->~observer(); + p.ordered_free(const_cast(o)); + } +} + +node_id generate_id(); + +typedef mpl::vector< + closest_nodes_observer + , find_data_observer + , announce_observer + , get_peers_observer + , refresh_observer + , ping_observer + , null_observer + > observer_types; + +typedef mpl::max_element< + mpl::transform_view > + >::type max_observer_type_iter; + +rpc_manager::rpc_manager(fun const& f, node_id const& our_id + , routing_table& table, send_fun const& sf) + : m_pool_allocator(sizeof(mpl::deref::type)) + , m_next_transaction_id(rand() % max_transactions) + , m_oldest_transaction_id(m_next_transaction_id) + , m_incoming(f) + , m_send(sf) + , m_our_id(our_id) + , m_table(table) + , m_timer(time_now()) + , m_random_number(generate_id()) + , m_destructing(false) +{ + std::srand(time(0)); +} + +rpc_manager::~rpc_manager() +{ + m_destructing = true; +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(rpc) << "Destructing"; +#endif + std::for_each(m_aborted_transactions.begin(), m_aborted_transactions.end() + , bind(&observer::abort, _1)); + + for (transactions_t::iterator i = m_transactions.begin() + , end(m_transactions.end()); i != end; ++i) + { + if (*i) (*i)->abort(); + } +} + +#ifndef NDEBUG +void rpc_manager::check_invariant() const +{ + assert(m_oldest_transaction_id >= 0); + assert(m_oldest_transaction_id < max_transactions); + assert(m_next_transaction_id >= 0); + assert(m_next_transaction_id < max_transactions); + assert(!m_transactions[m_next_transaction_id]); + + for (int i = (m_next_transaction_id + 1) % max_transactions; + i != m_oldest_transaction_id; i = (i + 1) % max_transactions) + { + assert(!m_transactions[i]); + } +} +#endif + +bool rpc_manager::incoming(msg const& m) +{ + INVARIANT_CHECK; + + if (m_destructing) return false; + + if (m.reply) + { + // if we don't have the transaction id in our + // request list, ignore the packet + + if (m.transaction_id.size() < 2) + { +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(rpc) << "Reply with invalid transaction id size: " + << m.transaction_id.size() << " from " << m.addr; +#endif + msg reply; + reply.reply = true; + reply.message_id = messages::error; + reply.error_code = 203; // Protocol error + reply.error_msg = "reply with invalid transaction id, size " + + boost::lexical_cast(m.transaction_id.size()); + reply.addr = m.addr; + reply.transaction_id = ""; + m_send(reply); + return false; + } + + std::string::const_iterator i = m.transaction_id.begin(); + int tid = io::read_uint16(i); + + if (tid >= (int)m_transactions.size() + || tid < 0) + { +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(rpc) << "Reply with invalid transaction id: " + << tid << " from " << m.addr; +#endif + msg reply; + reply.reply = true; + reply.message_id = messages::error; + reply.error_code = 203; // Protocol error + reply.error_msg = "reply with invalid transaction id"; + reply.addr = m.addr; + reply.transaction_id = ""; + m_send(reply); + return false; + } + + observer_ptr o = m_transactions[tid]; + + if (!o) + { +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(rpc) << "Reply with unknown transaction id: " + << tid << " from " << m.addr << " (possibly timed out)"; +#endif + return false; + } + + if (m.addr != o->target_addr) + { +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(rpc) << "Reply with incorrect address and valid transaction id: " + << tid << " from " << m.addr; +#endif + return false; + } + +#ifdef TORRENT_DHT_VERBOSE_LOGGING + std::ofstream reply_stats("libtorrent_logs/round_trip_ms.log", std::ios::app); + reply_stats << m.addr << "\t" << total_milliseconds(time_now() - o->sent) + << std::endl; +#endif + o->reply(m); + m_transactions[tid] = 0; + + if (m.piggy_backed_ping) + { + // there is a ping request piggy + // backed in this reply + msg ph; + ph.message_id = messages::ping; + ph.transaction_id = m.ping_transaction_id; + ph.addr = m.addr; + ph.reply = true; + + reply(ph); + } + return m_table.node_seen(m.id, m.addr); + } + else + { + assert(m.message_id != messages::error); + // this is an incoming request + m_incoming(m); + } + return false; +} + +time_duration rpc_manager::tick() +{ + INVARIANT_CHECK; + + const int timeout_ms = 10 * 1000; + + // look for observers that has timed out + + if (m_next_transaction_id == m_oldest_transaction_id) return milliseconds(timeout_ms); + + std::vector timeouts; + + for (;m_next_transaction_id != m_oldest_transaction_id; + m_oldest_transaction_id = (m_oldest_transaction_id + 1) % max_transactions) + { + assert(m_oldest_transaction_id >= 0); + assert(m_oldest_transaction_id < max_transactions); + + observer_ptr o = m_transactions[m_oldest_transaction_id]; + if (!o) continue; + + time_duration diff = o->sent + milliseconds(timeout_ms) - time_now(); + if (diff > seconds(0)) + { + if (diff < seconds(1)) return seconds(1); + return diff; + } + + try + { + m_transactions[m_oldest_transaction_id] = 0; + timeouts.push_back(o); + } catch (std::exception) {} + } + + std::for_each(timeouts.begin(), timeouts.end(), bind(&observer::timeout, _1)); + timeouts.clear(); + + // clear the aborted transactions, will likely + // generate new requests. We need to swap, since the + // destrutors may add more observers to the m_aborted_transactions + std::vector().swap(m_aborted_transactions); + return milliseconds(timeout_ms); +} + +unsigned int rpc_manager::new_transaction_id(observer_ptr o) +{ + INVARIANT_CHECK; + + unsigned int tid = m_next_transaction_id; + m_next_transaction_id = (m_next_transaction_id + 1) % max_transactions; + if (m_transactions[m_next_transaction_id]) + { + // moving the observer into the set of aborted transactions + // it will prevent it from spawning new requests right now, + // since that would break the invariant + m_aborted_transactions.push_back(m_transactions[m_next_transaction_id]); + m_transactions[m_next_transaction_id] = 0; + assert(m_oldest_transaction_id == m_next_transaction_id); + } + assert(!m_transactions[tid]); + m_transactions[tid] = o; + if (m_oldest_transaction_id == m_next_transaction_id) + { + m_oldest_transaction_id = (m_oldest_transaction_id + 1) % max_transactions; +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(rpc) << "WARNING: transaction limit reached! Too many concurrent" + " messages! limit: " << (int)max_transactions; +#endif + update_oldest_transaction_id(); + } + + return tid; +} + +void rpc_manager::update_oldest_transaction_id() +{ + INVARIANT_CHECK; + + assert(m_oldest_transaction_id != m_next_transaction_id); + while (!m_transactions[m_oldest_transaction_id]) + { + m_oldest_transaction_id = (m_oldest_transaction_id + 1) + % max_transactions; + if (m_oldest_transaction_id == m_next_transaction_id) + break; + } +} + +void rpc_manager::invoke(int message_id, udp::endpoint target_addr + , observer_ptr o) +{ + INVARIANT_CHECK; + + if (m_destructing) + { + o->abort(); + return; + } + + msg m; + m.message_id = message_id; + m.reply = false; + m.id = m_our_id; + m.addr = target_addr; + assert(!m_transactions[m_next_transaction_id]); +#ifndef NDEBUG + int potential_new_id = m_next_transaction_id; +#endif + try + { + m.transaction_id.clear(); + std::back_insert_iterator out(m.transaction_id); + io::write_uint16(m_next_transaction_id, out); + + o->send(m); + + o->sent = time_now(); + o->target_addr = target_addr; + + #ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(rpc) << "Invoking " << messages::ids[message_id] + << " -> " << target_addr; + #endif + m_send(m); + new_transaction_id(o); + } + catch (std::exception& e) + { + // m_send may fail with "no route to host" + assert(potential_new_id == m_next_transaction_id); + o->abort(); + } +} + +void rpc_manager::reply(msg& m) +{ + INVARIANT_CHECK; + + if (m_destructing) return; + + assert(m.reply); + m.piggy_backed_ping = false; + m.id = m_our_id; + + m_send(m); +} + +void rpc_manager::reply_with_ping(msg& m) +{ + INVARIANT_CHECK; + + if (m_destructing) return; + assert(m.reply); + + m.piggy_backed_ping = true; + m.id = m_our_id; + + m.ping_transaction_id.clear(); + std::back_insert_iterator out(m.ping_transaction_id); + io::write_uint16(m_next_transaction_id, out); + + observer_ptr o(new (allocator().malloc()) null_observer(allocator())); + assert(!m_transactions[m_next_transaction_id]); + o->sent = time_now(); + o->target_addr = m.addr; + + m_send(m); + new_transaction_id(o); +} + + + +} } // namespace libtorrent::dht + diff --git a/encryption/libtorrent/src/kademlia/traversal_algorithm.cpp b/encryption/libtorrent/src/kademlia/traversal_algorithm.cpp new file mode 100644 index 000000000..ceb977f19 --- /dev/null +++ b/encryption/libtorrent/src/kademlia/traversal_algorithm.cpp @@ -0,0 +1,184 @@ +/* + +Copyright (c) 2006, Arvid Norberg & Daniel Wallin +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/pch.hpp" + +#include +#include +#include + +#include + +using boost::bind; +using asio::ip::udp; + +namespace libtorrent { namespace dht +{ +#ifdef TORRENT_DHT_VERBOSE_LOGGING +TORRENT_DEFINE_LOG(traversal) +#endif + +void traversal_algorithm::add_entry(node_id const& id, udp::endpoint addr, unsigned char flags) +{ + if (m_failed.find(addr) != m_failed.end()) return; + + result const entry(id, addr, flags); + + std::vector::iterator i = std::lower_bound( + m_results.begin() + , m_results.end() + , entry + , bind( + compare_ref + , bind(&result::id, _1) + , bind(&result::id, _2) + , m_target + ) + ); + + if (i == m_results.end() || i->id != id) + { + assert(std::find_if(m_results.begin(), m_results.end() + , bind(&result::id, _1) == id) == m_results.end()); +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(traversal) << "adding result: " << id << " " << addr; +#endif + m_results.insert(i, entry); + } +} + +boost::pool<>& traversal_algorithm::allocator() const +{ + return m_rpc.allocator(); +} + +void traversal_algorithm::traverse(node_id const& id, udp::endpoint addr) +{ + add_entry(id, addr, 0); +} + +void traversal_algorithm::finished(node_id const& id) +{ + --m_invoke_count; + add_requests(); + if (m_invoke_count == 0) done(); +} + +// 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(node_id const& id, bool prevent_request) +{ + m_invoke_count--; + + std::vector::iterator i = std::find_if( + m_results.begin() + , m_results.end() + , bind( + std::equal_to() + , bind(&result::id, _1) + , id + ) + ); + + assert(i != m_results.end()); + + if (i != m_results.end()) + { + assert(i->flags & result::queried); + m_failed.insert(i->addr); +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(traversal) << "failed: " << i->id << " " << i->addr; +#endif + m_results.erase(i); + } + if (prevent_request) + { + --m_branch_factor; + if (m_branch_factor <= 0) m_branch_factor = 1; + } + else + { + m_table.node_failed(id); + } + add_requests(); + if (m_invoke_count == 0) done(); +} + +namespace +{ + bool bitwise_nand(unsigned char lhs, unsigned char rhs) + { + return (lhs & rhs) == 0; + } +} + +void traversal_algorithm::add_requests() +{ + while (m_invoke_count < m_branch_factor) + { + // Find the first node that hasn't already been queried. + // TODO: Better heuristic + std::vector::iterator i = std::find_if( + m_results.begin() + , last_iterator() + , bind( + &bitwise_nand + , bind(&result::flags, _1) + , (unsigned char)result::queried + ) + ); +#ifdef TORRENT_DHT_VERBOSE_LOGGING + TORRENT_LOG(traversal) << "nodes left (" << this << "): " << (last_iterator() - i); +#endif + + if (i == last_iterator()) break; + + try + { + invoke(i->id, i->addr); + ++m_invoke_count; + i->flags |= result::queried; + } + catch (std::exception& e) {} + } +} + +std::vector::iterator traversal_algorithm::last_iterator() +{ + return (int)m_results.size() >= m_max_results ? + m_results.begin() + m_max_results + : m_results.end(); +} + +} } // namespace libtorrent::dht + diff --git a/encryption/libtorrent/src/logger.cpp b/encryption/libtorrent/src/logger.cpp new file mode 100644 index 000000000..6881c5e7b --- /dev/null +++ b/encryption/libtorrent/src/logger.cpp @@ -0,0 +1,230 @@ +/* + +Copyright (c) 2006, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/pch.hpp" + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include + +#include "libtorrent/extensions/logger.hpp" +#include "libtorrent/extensions.hpp" +#include "libtorrent/entry.hpp" +#include "libtorrent/peer_request.hpp" +#include "libtorrent/peer_connection.hpp" + +namespace libtorrent { namespace +{ + + struct logger_peer_plugin : peer_plugin + { + logger_peer_plugin(std::string const& filename) + { + using namespace boost::filesystem; + path dir(complete("libtorrent_ext_logs")); + if (!exists(dir)) create_directories(dir); + m_file.open(dir / filename, std::ios_base::out | std::ios_base::out); + m_file << "\n\n\n"; + log_timestamp(); + m_file << "*** starting log ***\n"; + } + + void log_timestamp() + { + m_file << time_now_string() << ": "; + } + + // can add entries to the extension handshake + virtual void add_handshake(entry&) {} + + // called when the extension handshake from the other end is received + virtual bool on_extension_handshake(entry const& h) + { + log_timestamp(); + m_file << "<== EXTENSION_HANDSHAKE\n"; + h.print(m_file); + return true; + } + + // returning true from any of the message handlers + // indicates that the plugin has handeled 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() + { + log_timestamp(); + m_file << "<== CHOKE\n"; + m_file.flush(); + return false; + } + + virtual bool on_unchoke() + { + log_timestamp(); + m_file << "<== UNCHOKE\n"; + m_file.flush(); + return false; + } + + virtual bool on_interested() + { + log_timestamp(); + m_file << "<== INTERESTED\n"; + m_file.flush(); + return false; + } + + virtual bool on_not_interested() + { + log_timestamp(); + m_file << "<== NOT_INTERESTED\n"; + m_file.flush(); + return false; + } + + virtual bool on_have(int index) + { + log_timestamp(); + m_file << "<== HAVE [" << index << "]\n"; + m_file.flush(); + return false; + } + + virtual bool on_bitfield(std::vector const& bitfield) + { + log_timestamp(); + m_file << "<== BITFIELD\n"; + m_file.flush(); + return false; + } + + virtual bool on_request(peer_request const& r) + { + log_timestamp(); + m_file << "<== REQUEST [ piece: " << r.piece << " | s: " << r.start + << " | l: " << r.length << " ]\n"; + m_file.flush(); + return false; + } + + virtual bool on_piece(peer_request const& r, char const*) + { + log_timestamp(); + m_file << "<== PIECE [ piece: " << r.piece << " | s: " << r.start + << " | l: " << r.length << " ]\n"; + m_file.flush(); + return false; + } + + virtual bool on_cancel(peer_request const& r) + { + log_timestamp(); + m_file << "<== CANCEL [ piece: " << r.piece << " | s: " << r.start + << " | l: " << r.length << " ]\n"; + m_file.flush(); + return false; + } + + // 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 + virtual bool on_extended(int length + , int msg, buffer::const_interval body) + { return false; } + + virtual bool on_unknown_message(int length, int msg + , buffer::const_interval body) + { + if (body.left() < length) return false; + log_timestamp(); + m_file << "<== UNKNOWN [ msg: " << msg + << " | l: " << length << " ]\n"; + m_file.flush(); + return false; + } + + virtual void on_piece_pass(int index) + { + log_timestamp(); + m_file << "*** HASH PASSED *** [ piece: " << index << " ]\n"; + m_file.flush(); + } + + virtual void on_piece_failed(int index) + { + log_timestamp(); + m_file << "*** HASH FAILED *** [ piece: " << index << " ]\n"; + m_file.flush(); + } + + private: + boost::filesystem::ofstream m_file; + }; + + struct logger_plugin : torrent_plugin + { + virtual boost::shared_ptr new_connection( + peer_connection* pc) + { + return boost::shared_ptr(new logger_peer_plugin( + pc->remote().address().to_string() + "_" + + boost::lexical_cast(pc->remote().port()) + ".log")); + } + }; + +} } + +namespace libtorrent +{ + + boost::shared_ptr create_logger_plugin(torrent*) + { + return boost::shared_ptr(new logger_plugin()); + } + +} + + diff --git a/encryption/libtorrent/src/lsd.cpp b/encryption/libtorrent/src/lsd.cpp new file mode 100644 index 000000000..b73e407bc --- /dev/null +++ b/encryption/libtorrent/src/lsd.cpp @@ -0,0 +1,250 @@ +/* + +Copyright (c) 2007, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/pch.hpp" + +#include "libtorrent/lsd.hpp" +#include "libtorrent/io.hpp" +#include "libtorrent/http_tracker_connection.hpp" +#include "libtorrent/xml_parse.hpp" +#include +#include +#include +#include +#include +#include +#include + +using boost::bind; +using namespace libtorrent; + +namespace libtorrent +{ + // defined in upnp.cpp + address_v4 guess_local_address(asio::io_service&); +} + +address_v4 lsd::lsd_multicast_address; +udp::endpoint lsd::lsd_multicast_endpoint; + +lsd::lsd(io_service& ios, address const& listen_interface + , peer_callback_t const& cb) + : m_callback(cb) + , m_retry_count(0) + , m_socket(ios) + , m_broadcast_timer(ios) + , m_disabled(false) +{ + // Bittorrent Local discovery multicast address and port + lsd_multicast_address = address_v4::from_string("239.192.152.143"); + lsd_multicast_endpoint = udp::endpoint(lsd_multicast_address, 6771); + +#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING) + m_log.open("lsd.log", std::ios::in | std::ios::out | std::ios::trunc); +#endif + assert(lsd_multicast_address.is_multicast()); + rebind(listen_interface); +} + +lsd::~lsd() {} + +void lsd::rebind(address const& listen_interface) +{ + address_v4 local_ip = address_v4::any(); + if (listen_interface.is_v4() && listen_interface != address_v4::any()) + { + local_ip = listen_interface.to_v4(); + } + + try + { + // the local interface hasn't changed + if (m_socket.is_open() + && m_socket.local_endpoint().address() == local_ip) + return; + + m_socket.close(); + + using namespace asio::ip::multicast; + + m_socket.open(udp::v4()); + m_socket.set_option(datagram_socket::reuse_address(true)); + m_socket.bind(udp::endpoint(local_ip, 6771)); + +#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING) + m_log << "local ip: " << local_ip << std::endl; +#endif + + m_socket.set_option(join_group(lsd_multicast_address)); + m_socket.set_option(outbound_interface(local_ip)); + m_socket.set_option(enable_loopback(true)); + m_socket.set_option(hops(255)); + } + catch (std::exception& e) + { +#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING) + m_log << "socket multicast error " << e.what() + << ". disabling local service discovery" << std::endl; +#endif + m_disabled = true; + return; + } + m_disabled = false; + + setup_receive(); +} + +void lsd::announce(sha1_hash const& ih, int listen_port) +{ + if (m_disabled) return; + + std::stringstream btsearch; + btsearch << "BT-SEARCH * HTTP/1.1\r\n" + "Host: 239.192.152.143:6771\r\n" + "Port: " << listen_port << "\r\n" + "Infohash: " << ih << "\r\n" + "\r\n\r\n"; + std::string const& msg = btsearch.str(); + + m_retry_count = 0; + asio::error_code ec; + m_socket.send_to(asio::buffer(msg.c_str(), msg.size() - 1) + , lsd_multicast_endpoint, 0, ec); + if (ec) + { + m_disabled = true; + return; + } + +#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING) + m_log << time_now_string() + << " ==> announce: ih: " << ih << " port: " << listen_port << std::endl; +#endif + + m_broadcast_timer.expires_from_now(milliseconds(250 * m_retry_count)); + m_broadcast_timer.async_wait(bind(&lsd::resend_announce, this, _1, msg)); +} + +void lsd::resend_announce(asio::error_code const& e, std::string msg) try +{ + if (e) return; + + m_socket.send_to(asio::buffer(msg, msg.size() - 1) + , lsd_multicast_endpoint); + + ++m_retry_count; + if (m_retry_count >= 5) + return; + + m_broadcast_timer.expires_from_now(milliseconds(250 * m_retry_count)); + m_broadcast_timer.async_wait(bind(&lsd::resend_announce, this, _1, msg)); +} +catch (std::exception&) +{} + +void lsd::on_announce(asio::error_code const& e + , std::size_t bytes_transferred) +{ + using namespace libtorrent::detail; + if (e) return; + + char* p = m_receive_buffer; + char* end = m_receive_buffer + bytes_transferred; + char* line = std::find(p, end, '\n'); + for (char* i = p; i < line; ++i) *i = std::tolower(*i); +#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING) + m_log << time_now_string() + << " <== announce: " << std::string(p, line) << std::endl; +#endif + if (line == end || (line - p >= 9 && std::memcmp("bt-search", p, 9))) + { +#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING) + m_log << time_now_string() + << " *** assumed 'bt-search', ignoring" << std::endl; +#endif + setup_receive(); + return; + } + p = line + 1; + int port = 0; + sha1_hash ih(0); + while (p != end) + { + line = std::find(p, end, '\n'); + if (line == end) break; + *line = 0; + for (char* i = p; i < line; ++i) *i = std::tolower(*i); + if (line - p >= 5 && memcmp(p, "port:", 5) == 0) + { + p += 5; + while (*p == ' ') ++p; + port = atoi(p); + } + else if (line - p >= 9 && memcmp(p, "infohash:", 9) == 0) + { + p += 9; + while (*p == ' ') ++p; + if (line - p > 40) p[40] = 0; + try { ih = boost::lexical_cast(p); } + catch (std::exception&) {} + } + p = line + 1; + } + + if (!ih.is_all_zeros() && port != 0) + { +#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING) + m_log << time_now_string() + << " *** incoming local announce " << m_remote.address() + << ":" << port << " ih: " << ih << std::endl; +#endif + // we got an announce, pass it on through the callback + try { m_callback(tcp::endpoint(m_remote.address(), port), ih); } + catch (std::exception&) {} + } + setup_receive(); +} + +void lsd::setup_receive() try +{ + assert(m_socket.is_open()); + m_socket.async_receive_from(asio::buffer(m_receive_buffer + , sizeof(m_receive_buffer)), m_remote, bind(&lsd::on_announce, this, _1, _2)); +} +catch (std::exception&) +{} + +void lsd::close() +{ + m_socket.close(); +} + diff --git a/encryption/libtorrent/src/metadata_transfer.cpp b/encryption/libtorrent/src/metadata_transfer.cpp new file mode 100644 index 000000000..fe308f926 --- /dev/null +++ b/encryption/libtorrent/src/metadata_transfer.cpp @@ -0,0 +1,568 @@ +/* + +Copyright (c) 2006, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/pch.hpp" + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include +#include +#include + +#include "libtorrent/peer_connection.hpp" +#include "libtorrent/bt_peer_connection.hpp" +#include "libtorrent/hasher.hpp" +#include "libtorrent/bencode.hpp" +#include "libtorrent/torrent.hpp" +#include "libtorrent/extensions.hpp" +#include "libtorrent/extensions/metadata_transfer.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) + { + assert(req.first >= 0); + assert(req.second > 0); + assert(req.second <= 256); + assert(req.first + req.second <= 256); + + int start = div_round_up(req.first * total_size, 256); + int size = div_round_up((req.first + req.second) * total_size, 256) - start; + return std::make_pair(start, size); + } + + std::pair 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); + + assert(start >= 0); + assert(size > 0); + assert(start <= 256); + assert(start + size <= 256); + + // assert the identity of this function +#ifndef NDEBUG + std::pair identity = req_to_offset(ret, total_size); + assert(offset == identity); +#endif + return ret; + } + + + struct metadata_plugin : torrent_plugin + { + metadata_plugin(torrent& t) + : m_torrent(t) + , m_metadata_progress(0) + , m_metadata_size(0) + { + m_requested_metadata.resize(256, 0); + } + + virtual void on_files_checked() + { + // if the torrent is a seed, copy the metadata from + // the torrent before it is deallocated + if (m_torrent.is_seed()) + metadata(); + } + + virtual boost::shared_ptr new_connection( + peer_connection* pc); + + std::vector const& metadata() const + { + if (m_metadata.empty()) + { + bencode(std::back_inserter(m_metadata) + , m_torrent.torrent_file().create_info_metadata()); + + assert(hasher(&m_metadata[0], m_metadata.size()).final() + == m_torrent.torrent_file().info_hash()); + } + assert(!m_metadata.empty()); + return m_metadata; + } + + bool received_metadata(char const* buf, int size, int offset, int total_size) + { + if (m_torrent.valid_metadata()) return false; + + if ((int)m_metadata.size() < total_size) + m_metadata.resize(total_size); + + std::copy( + buf + , buf + size + , &m_metadata[offset]); + + if (m_have_metadata.empty()) + m_have_metadata.resize(256, false); + + std::pair req = offset_to_req(std::make_pair(offset, size) + , total_size); + + assert(req.first + req.second <= (int)m_have_metadata.size()); + + std::fill( + m_have_metadata.begin() + req.first + , m_have_metadata.begin() + req.first + req.second + , true); + + bool have_all = std::count( + m_have_metadata.begin() + , m_have_metadata.end() + , true) == 256; + + if (!have_all) return false; + + hasher h; + h.update(&m_metadata[0], (int)m_metadata.size()); + sha1_hash info_hash = h.final(); + + if (info_hash != m_torrent.torrent_file().info_hash()) + { + std::fill( + m_have_metadata.begin() + , m_have_metadata.begin() + req.first + req.second + , false); + m_metadata_progress = 0; + m_metadata_size = 0; + // TODO: allow plugins to post alerts +/* + if (m_ses.m_alerts.should_post(alert::info)) + { + m_ses.m_alerts.post_alert(metadata_failed_alert( + get_handle(), "invalid metadata received from swarm")); + } +*/ + return false; + } + + entry metadata = bdecode(m_metadata.begin(), m_metadata.end()); + m_torrent.set_metadata(metadata); + + // 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) + { + 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; + } + + void piece_pass(int) + { + // if we became a seed, copy the metadata from + // the torrent before it is deallocated + if (m_torrent.is_seed()) + metadata(); + } + + 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 std::vector m_metadata; + + int m_metadata_progress; + 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; + }; + + + struct metadata_peer_plugin : 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) + {} + + // can add entries to the extension handshake + virtual void add_handshake(entry& h) + { + entry& messages = h["m"]; + messages["LT_metadata"] = 14; + } + + // called when the extension handshake from the other end is received + virtual bool on_extension_handshake(entry const& h) + { + entry const& messages = h["m"]; + if (entry const* index = messages.find_key("LT_metadata")) + { + m_message_index = index->integer(); + return true; + } + else + { + m_message_index = 0; + return false; + } + } + + void write_metadata_request(std::pair req) + { + assert(req.first >= 0); + assert(req.second > 0); + assert(req.first + req.second <= 256); + assert(!m_pc.associated_torrent().expired()); + 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; + + buffer::interval i = m_pc.allocate_send_buffer(9); + + detail::write_uint32(1 + 1 + 3, i.begin); + detail::write_uint8(bt_peer_connection::msg_extended, i.begin); + detail::write_uint8(m_message_index, i.begin); + // means 'request data' + detail::write_uint8(0, i.begin); + detail::write_uint8(start, i.begin); + detail::write_uint8(size - 1, i.begin); + assert(i.begin == i.end); + m_pc.setup_send(); + } + + void write_metadata(std::pair req) + { + assert(req.first >= 0); + assert(req.second > 0); + assert(req.second <= 256); + assert(req.first + req.second <= 256); + assert(!m_pc.associated_torrent().expired()); + + // abort if the peer doesn't support the metadata extension + if (m_message_index == 0) return; + + // only send metadata if the torrent is non-private + if (m_torrent.valid_metadata() && !m_torrent.torrent_file().priv()) + { + std::pair offset + = req_to_offset(req, (int)m_tp.metadata().size()); + + buffer::interval i = m_pc.allocate_send_buffer(15 + offset.second); + + // yes, we have metadata, send it + detail::write_uint32(11 + offset.second, i.begin); + detail::write_uint8(bt_peer_connection::msg_extended, i.begin); + detail::write_uint8(m_message_index, i.begin); + // means 'data packet' + detail::write_uint8(1, i.begin); + detail::write_uint32((int)m_tp.metadata().size(), i.begin); + detail::write_uint32(offset.first, i.begin); + std::vector const& metadata = m_tp.metadata(); + std::copy(metadata.begin() + offset.first + , metadata.begin() + offset.first + offset.second, i.begin); + i.begin += offset.second; + assert(i.begin == i.end); + } + else + { + buffer::interval i = m_pc.allocate_send_buffer(4 + 3); + // we don't have the metadata, reply with + // don't have-message + detail::write_uint32(1 + 2, i.begin); + detail::write_uint8(bt_peer_connection::msg_extended, i.begin); + detail::write_uint8(m_message_index, i.begin); + // means 'have no data' + detail::write_uint8(2, i.begin); + assert(i.begin == i.end); + } + m_pc.setup_send(); + } + + virtual bool on_extended(int length + , int msg, buffer::const_interval body) + { + if (msg != 14) return false; + if (m_message_index == 0) return false; + + if (length > 500 * 1024) + throw protocol_error("LT_metadata message larger than 500 kB"); + + 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; + + if (length != 3) + { + // invalid metadata request + throw protocol_error("invalid metadata request"); + } + + 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; + + if (total_size > 500 * 1024) + throw protocol_error("metadata size larger than 500 kB"); + if (total_size <= 0) + throw protocol_error("invalid metadata size"); + if (offset > total_size || offset < 0) + throw protocol_error("invalid metadata offset"); + if (offset + data_size > total_size) + throw protocol_error("invalid metadata message"); + + 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 = time_now(); + if (m_waiting_metadata_request) + m_tp.cancel_metadata_request(m_last_metadata_request); + m_waiting_metadata_request = false; + break; + default: + throw protocol_error("unknown metadata extension message: " + + boost::lexical_cast(type)); + } + return true; + } + + virtual void tick() + { + // 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 = time_now(); + } + } + + bool has_metadata() const + { + return time_now() - m_no_metadata > minutes(5); + } + + 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. + ptime m_no_metadata; + + // this is set to the time when we last sent + // a request for metadata to this peer + ptime 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; + }; + + boost::shared_ptr metadata_plugin::new_connection( + peer_connection* pc) + { + bt_peer_connection* c = dynamic_cast(pc); + if (!c) return boost::shared_ptr(); + return boost::shared_ptr(new metadata_peer_plugin(m_torrent, *pc, *this)); + } + + std::pair metadata_plugin::metadata_request() + { + // count the number of peers that supports the + // extension and that has metadata + int peers = 0; +#ifndef TORRENT_DISABLE_EXTENSIONS + typedef std::map conn_map; + for (conn_map::iterator i = m_torrent.begin() + , end(m_torrent.end()); i != end; ++i) + { + bt_peer_connection* c = dynamic_cast(i->second); + if (c == 0) continue; + metadata_peer_plugin* p + = c->supports_extension(); + if (p == 0) continue; + if (!p->has_metadata()) continue; + ++peers; + } +#endif + + // the number of blocks to request + int num_blocks = 256 / (peers + 1); + if (num_blocks < 1) num_blocks = 1; + assert(num_blocks <= 128); + + int min_element = std::numeric_limits::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]++; + + assert(ret.first >= 0); + assert(ret.second > 0); + assert(ret.second <= 256); + assert(ret.first + ret.second <= 256); + + return ret; + } + +} } + +namespace libtorrent +{ + + boost::shared_ptr create_metadata_plugin(torrent* t) + { + return boost::shared_ptr(new metadata_plugin(*t)); + } + +} + + diff --git a/encryption/libtorrent/src/natpmp.cpp b/encryption/libtorrent/src/natpmp.cpp new file mode 100644 index 000000000..0a5932a56 --- /dev/null +++ b/encryption/libtorrent/src/natpmp.cpp @@ -0,0 +1,393 @@ +/* + +Copyright (c) 2007, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/pch.hpp" + +#include +#include +#include +#include + +using boost::bind; +using namespace libtorrent; + +enum { num_mappings = 2 }; + +namespace libtorrent +{ + // defined in upnp.cpp + bool is_local(address const& a); + address_v4 guess_local_address(asio::io_service&); +} + +natpmp::natpmp(io_service& ios, address const& listen_interface, portmap_callback_t const& cb) + : m_callback(cb) + , m_currently_mapping(-1) + , m_retry_count(0) + , m_socket(ios) + , m_send_timer(ios) + , m_refresh_timer(ios) + , m_disabled(false) +{ + m_mappings[0].protocol = 2; // tcp + m_mappings[1].protocol = 1; // udp + +#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING) + m_log.open("natpmp.log", std::ios::in | std::ios::out | std::ios::trunc); +#endif + rebind(listen_interface); +} + +void natpmp::rebind(address const& listen_interface) try +{ + address_v4 local = address_v4::any(); + if (listen_interface.is_v4() && listen_interface != address_v4::any()) + { + local = listen_interface.to_v4(); + } + else + { + local = guess_local_address(m_socket.io_service()); + + if (local == address_v4::any()) + { + throw std::runtime_error("local host is probably not on a NATed " + "network. disabling NAT-PMP"); + } + } + +#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING) + m_log << time_now_string() + << " local ip: " << local.to_string() << std::endl; +#endif + + if (!is_local(local)) + { + // the local address seems to be an external + // internet address. Assume it is not behind a NAT + throw std::runtime_error("local IP is not on a local network"); + } + + m_disabled = false; + + // assume the router is located on the local + // network as x.x.x.1 + udp::endpoint nat_endpoint( + address_v4((local.to_ulong() & 0xffffff00) | 1), 5351); + + if (nat_endpoint == m_nat_endpoint) return; + + // TODO: find a better way to figure out the router IP + m_nat_endpoint = nat_endpoint; + +#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING) + m_log << "assuming router is at: " << m_nat_endpoint.address().to_string() << std::endl; +#endif + + m_socket.open(udp::v4()); + m_socket.bind(udp::endpoint(address_v4::any(), 0)); + + for (int i = 0; i < num_mappings; ++i) + { + if (m_mappings[i].local_port == 0) + continue; + refresh_mapping(i); + } +} +catch (std::exception& e) +{ + m_disabled = true; + std::stringstream msg; + msg << "NAT-PMP disabled: " << e.what(); +#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING) + m_log << msg.str() << std::endl; +#endif + m_callback(0, 0, msg.str()); +}; + +void natpmp::set_mappings(int tcp, int udp) +{ + if (m_disabled) return; + update_mapping(0, tcp); + update_mapping(1, udp); +} + +void natpmp::update_mapping(int i, int port) +{ + natpmp::mapping& m = m_mappings[i]; + if (port <= 0) return; + if (m.local_port != port) + m.need_update = true; + + m.local_port = port; + // prefer the same external port as the local port + if (m.external_port == 0) m.external_port = port; + + 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); + m_socket.async_receive_from(asio::buffer(&m_response_buffer, 16) + , m_remote, bind(&natpmp::on_reply, this, _1, _2)); + } +} + +void natpmp::send_map_request(int i) try +{ + using namespace libtorrent::detail; + + assert(m_currently_mapping == -1 + || m_currently_mapping == i); + m_currently_mapping = i; + mapping& m = m_mappings[i]; + 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.external_port == 0 ? 0 : 3600; + write_uint32(ttl, out); // port mapping lifetime + +#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING) + m_log << time_now_string() + << " ==> port map request: " << (m.protocol == 1 ? "udp" : "tcp") + << " local: " << m.local_port << " external: " << m.external_port + << " ttl: " << ttl << std::endl; +#endif + + m_socket.send_to(asio::buffer(buf, 12), m_nat_endpoint); + // linear back-off instead of exponential + ++m_retry_count; + m_send_timer.expires_from_now(milliseconds(250 * m_retry_count)); + m_send_timer.async_wait(bind(&natpmp::resend_request, this, i, _1)); +} +catch (std::exception& e) +{ + std::string err = e.what(); +}; + +void natpmp::resend_request(int i, asio::error_code const& e) +{ + if (e) return; + if (m_currently_mapping != i) return; + if (m_retry_count >= 9) + { + m_mappings[i].need_update = false; + // try again in two hours + m_mappings[i].expires = time_now() + hours(2); + return; + } + send_map_request(i); +} + +void natpmp::on_reply(asio::error_code const& e + , std::size_t bytes_transferred) +{ + using namespace libtorrent::detail; + if (e) return; + + try + { + + if (m_remote != m_nat_endpoint) + { + m_socket.async_receive_from(asio::buffer(&m_response_buffer, 16) + , m_remote, bind(&natpmp::on_reply, this, _1, _2)); + return; + } + + m_send_timer.cancel(); + + assert(m_currently_mapping >= 0); + int i = m_currently_mapping; + mapping& m = m_mappings[i]; + + char* in = m_response_buffer; + int version = read_uint8(in); + int cmd = read_uint8(in); + int result = read_uint16(in); + int time = read_uint32(in); + int private_port = read_uint16(in); + int public_port = read_uint16(in); + int lifetime = read_uint32(in); + + (void)time; // to remove warning + +#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING) + m_log << time_now_string() + << " <== port map response: " << (cmd - 128 == 1 ? "udp" : "tcp") + << " local: " << private_port << " external: " << public_port + << " ttl: " << lifetime << std::endl; +#endif + +#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING) + if (version != 0) + { + m_log << "*** unexpected version: " << version << std::endl; + } +#endif + +#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING) + if (private_port != m.local_port) + { + m_log << "*** unexpected local port: " << private_port << std::endl; + } +#endif + +#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING) + if (cmd != 128 + m.protocol) + { + m_log << "*** unexpected protocol: " << (cmd - 128) << std::endl; + } +#endif + + if (public_port == 0 || lifetime == 0) + { + // this means the mapping was + // successfully closed + m.local_port = 0; + } + else + { + m.expires = time_now() + seconds(int(lifetime * 0.7f)); + m.external_port = public_port; + } + + if (result != 0) + { +#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING) + m_log << "*** ERROR: " << result << std::endl; +#endif + std::stringstream errmsg; + errmsg << "NAT router reports error (" << result << ") "; + switch (result) + { + case 1: errmsg << "Unsupported protocol version"; break; + case 2: errmsg << "Not authorized to create port map (enable NAT-PMP on your router)"; break; + case 3: errmsg << "Network failure"; break; + case 4: errmsg << "Out of resources"; break; + case 5: errmsg << "Unsupported opcode"; break; + } + throw std::runtime_error(errmsg.str()); + } + + // don't report when we remove mappings + if (m.local_port != 0) + { + int tcp_port = 0; + int udp_port = 0; + if (m.protocol == 1) udp_port = m.external_port; + else tcp_port = public_port; + m_callback(tcp_port, udp_port, ""); + } + } + catch (std::exception& e) + { + // try again in two hours + m_mappings[m_currently_mapping].expires = time_now() + hours(2); + m_callback(0, 0, e.what()); + } + int i = m_currently_mapping; + m_currently_mapping = -1; + m_mappings[i].need_update = false; + m_send_timer.cancel(); + update_expiration_timer(); + try_next_mapping(i); +} + +void natpmp::update_expiration_timer() +{ + ptime now = time_now(); + ptime min_expire = now + seconds(3600); + int min_index = -1; + for (int i = 0; i < num_mappings; ++i) + if (m_mappings[i].expires < min_expire + && m_mappings[i].local_port != 0) + { + min_expire = m_mappings[i].expires; + min_index = i; + } + + if (min_index >= 0) + { + m_refresh_timer.expires_from_now(min_expire - now); + m_refresh_timer.async_wait(bind(&natpmp::mapping_expired, this, _1, min_index)); + } +} + +void natpmp::mapping_expired(asio::error_code const& e, int i) +{ + if (e) return; +#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING) + m_log << "*** mapping " << i << " expired, updating" << std::endl; +#endif + refresh_mapping(i); +} + +void natpmp::refresh_mapping(int i) +{ + m_mappings[i].need_update = true; + 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); + m_socket.async_receive_from(asio::buffer(&m_response_buffer, 16) + , m_remote, bind(&natpmp::on_reply, this, _1, _2)); + } +} + +void natpmp::try_next_mapping(int i) +{ + ++i; + if (i >= num_mappings) i = 0; + if (m_mappings[i].need_update) + refresh_mapping(i); +} + +void natpmp::close() +{ + if (m_disabled) return; + for (int i = 0; i < num_mappings; ++i) + { + if (m_mappings[i].local_port == 0) + continue; + m_mappings[i].external_port = 0; + refresh_mapping(i); + } +} + diff --git a/encryption/libtorrent/src/pe_crypto.cpp b/encryption/libtorrent/src/pe_crypto.cpp new file mode 100644 index 000000000..a763e5458 --- /dev/null +++ b/encryption/libtorrent/src/pe_crypto.cpp @@ -0,0 +1,122 @@ +/* + +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. + +*/ + +#ifndef TORRENT_DISABLE_ENCRYPTION + +#include +#include + +#include +#include + +#include "libtorrent/pe_crypto.hpp" + +namespace libtorrent { + + + // Set the prime P and the generator, generate local public key + DH_key_exchange::DH_key_exchange () + { + m_DH = DH_new (); + + m_DH->p = BN_bin2bn (m_dh_prime, sizeof(m_dh_prime), NULL); + m_DH->g = BN_bin2bn (m_dh_generator, sizeof(m_dh_generator), NULL); + + assert (sizeof(m_dh_prime) == DH_size(m_DH)); + + DH_generate_key (m_DH); // TODO Check != 0 + + assert (m_DH->pub_key); + + // DH can generate key sizes that are smaller than the size of + // P with exponentially decreasing probability, in which case + // the msb's of m_dh_local_key need to be zeroed + // appropriately. + int key_size = get_local_key_size(); + int len_dh = sizeof(m_dh_prime); // must equal DH_size(m_DH) + if (key_size != len_dh) + { + assert(key_size > 0 && key_size < len_dh); + + int pad_zero_size = len_dh - key_size; + std::fill(m_dh_local_key, m_dh_local_key + pad_zero_size, 0); + BN_bn2bin(m_DH->pub_key, (unsigned char*)m_dh_local_key + pad_zero_size); + } + else + BN_bn2bin(m_DH->pub_key, (unsigned char*)m_dh_local_key); // TODO Check return value + } + + DH_key_exchange::~DH_key_exchange () + { + assert (m_DH); + DH_free (m_DH); + } + + char const* DH_key_exchange::get_local_key () const + { + return m_dh_local_key; + } + + + // compute shared secret given remote public key + void DH_key_exchange::compute_secret (char const* remote_pubkey) + { + assert (remote_pubkey); + BIGNUM* bn_remote_pubkey = BN_bin2bn ((unsigned char*)remote_pubkey, 96, NULL); + + int ret = + DH_compute_key ( (unsigned char*)m_dh_secret, bn_remote_pubkey, m_DH); // TODO Check for errors + + BN_free (bn_remote_pubkey); + } + + char const* DH_key_exchange::get_secret () const + { + return m_dh_secret; + } + + const unsigned char DH_key_exchange::m_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 + }; + + const unsigned char DH_key_exchange::m_dh_generator[1] = { 2 }; + +} // namespace libtorrent + +#endif // #ifndef TORRENT_DISABLE_ENCRYPTION diff --git a/encryption/libtorrent/src/peer_connection.cpp b/encryption/libtorrent/src/peer_connection.cpp new file mode 100755 index 000000000..db70d2e1c --- /dev/null +++ b/encryption/libtorrent/src/peer_connection.cpp @@ -0,0 +1,2665 @@ +/* + +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 "libtorrent/pch.hpp" + +#include +#include +#include +#include +#include + +#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_impl.hpp" +#include "libtorrent/policy.hpp" +#include "libtorrent/socket_type.hpp" + +using boost::bind; +using boost::shared_ptr; +using libtorrent::aux::session_impl; + +namespace libtorrent +{ + + void intrusive_ptr_add_ref(peer_connection const* c) + { + assert(c->m_refs >= 0); + assert(c != 0); + ++c->m_refs; + } + + void intrusive_ptr_release(peer_connection const* c) + { + assert(c->m_refs > 0); + assert(c != 0); + if (--c->m_refs == 0) + delete c; + } + + // outbound connection + peer_connection::peer_connection( + session_impl& ses + , boost::weak_ptr tor + , shared_ptr s + , tcp::endpoint const& remote + , policy::peer* peerinfo) + : +#ifndef NDEBUG + m_last_choke(time_now() - hours(1)) + , +#endif + m_ses(ses) + , m_max_out_request_queue(m_ses.settings().max_out_request_queue) + , m_timeout(m_ses.settings().peer_timeout) + , m_last_piece(time_now()) + , m_last_request(time_now()) + , m_packet_size(0) + , m_recv_pos(0) + , m_current_send_buffer(0) + , m_write_pos(0) + , m_last_receive(time_now()) + , m_last_sent(time_now()) + , m_socket(s) + , m_remote(remote) + , m_torrent(tor) + , m_active(true) + , m_peer_interested(false) + , m_peer_choked(true) + , m_interesting(false) + , m_choked(true) + , m_failed(false) + , m_ignore_bandwidth_limits(false) + , m_num_pieces(0) + , m_desired_queue_size(2) + , m_free_upload(0) + , m_assume_fifo(false) + , m_num_invalid_requests(0) + , m_disconnecting(false) + , m_became_uninterested(time_now()) + , m_became_uninteresting(time_now()) + , m_connecting(true) + , m_queued(true) + , m_writing(false) + , m_reading(false) + , m_prefer_whole_pieces(false) + , m_request_large_blocks(false) + , m_non_prioritized(false) + , m_refs(0) + , m_upload_limit(resource_request::inf) + , m_download_limit(resource_request::inf) + , m_peer_info(peerinfo) + , m_speed(slow) + , m_connection_ticket(-1) +#ifndef NDEBUG + , m_in_constructor(true) +#endif + { +#ifndef TORRENT_DISABLE_RESOLVE_COUNTRIES + std::fill(m_country, m_country + 2, 0); +#endif +#ifdef TORRENT_VERBOSE_LOGGING + m_logger = m_ses.create_log(m_remote.address().to_string() + "_" + + boost::lexical_cast(m_remote.port()), m_ses.listen_port()); + (*m_logger) << "*** OUTGOING CONNECTION\n"; +#endif + + boost::shared_ptr t = m_torrent.lock(); + assert(t); + std::fill(m_peer_id.begin(), m_peer_id.end(), 0); + + if (t->ready_for_connections()) + init(); + } + + // incoming connection + peer_connection::peer_connection( + session_impl& ses + , boost::shared_ptr s + , policy::peer* peerinfo) + : +#ifndef NDEBUG + m_last_choke(time_now() - hours(1)) + , +#endif + m_ses(ses) + , m_max_out_request_queue(m_ses.settings().max_out_request_queue) + , m_timeout(m_ses.settings().peer_timeout) + , m_last_piece(time_now()) + , m_last_request(time_now()) + , m_packet_size(0) + , m_recv_pos(0) + , m_current_send_buffer(0) + , m_write_pos(0) + , m_last_receive(time_now()) + , m_last_sent(time_now()) + , m_socket(s) + , m_active(false) + , m_peer_interested(false) + , m_peer_choked(true) + , m_interesting(false) + , m_choked(true) + , m_failed(false) + , m_ignore_bandwidth_limits(false) + , m_num_pieces(0) + , m_desired_queue_size(2) + , m_free_upload(0) + , m_assume_fifo(false) + , m_num_invalid_requests(0) + , m_disconnecting(false) + , m_became_uninterested(time_now()) + , m_became_uninteresting(time_now()) + , m_connecting(false) + , m_queued(false) + , m_writing(false) + , m_reading(false) + , m_prefer_whole_pieces(false) + , m_request_large_blocks(false) + , m_non_prioritized(false) + , m_refs(0) + , m_upload_limit(resource_request::inf) + , m_download_limit(resource_request::inf) + , m_peer_info(peerinfo) + , m_speed(slow) +#ifndef NDEBUG + , m_in_constructor(true) +#endif + { + tcp::socket::non_blocking_io ioc(true); + m_socket->io_control(ioc); +#ifndef TORRENT_DISABLE_RESOLVE_COUNTRIES + std::fill(m_country, m_country + 2, 0); +#endif + m_remote = m_socket->remote_endpoint(); + +#ifdef TORRENT_VERBOSE_LOGGING + assert(m_socket->remote_endpoint() == remote()); + m_logger = m_ses.create_log(remote().address().to_string() + "_" + + boost::lexical_cast(remote().port()), m_ses.listen_port()); + (*m_logger) << "*** INCOMING CONNECTION\n"; +#endif + + std::fill(m_peer_id.begin(), m_peer_id.end(), 0); + } + + void peer_connection::update_interest() + { + INVARIANT_CHECK; + + boost::shared_ptr t = m_torrent.lock(); + assert(t); + + bool interested = false; + const std::vector& we_have = t->pieces(); + for (int j = 0; j != (int)we_have.size(); ++j) + { + if (!we_have[j] + && t->piece_priority(j) > 0 + && m_have_piece[j]) + { + interested = true; + break; + } + } + try + { + if (!interested) + send_not_interested(); + else + t->get_policy().peer_is_interesting(*this); + } + // may throw an asio error if socket has disconnected + catch (std::exception& e) {} + + assert(is_interesting() == interested); + } + +#ifndef TORRENT_DISABLE_EXTENSIONS + void peer_connection::add_extension(boost::shared_ptr ext) + { + m_extensions.push_back(ext); + } +#endif + + void peer_connection::init() + { + INVARIANT_CHECK; + + boost::shared_ptr t = m_torrent.lock(); + assert(t); + assert(t->valid_metadata()); + assert(t->ready_for_connections()); + + m_have_piece.resize(t->torrent_file().num_pieces(), false); + + // now that we have a piece_picker, + // update it with this peers pieces + + int num_pieces = std::count(m_have_piece.begin(), m_have_piece.end(), true); + if (num_pieces == int(m_have_piece.size())) + { +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << " *** THIS IS A SEED ***\n"; +#endif + // if this is a web seed. we don't have a peer_info struct + if (m_peer_info) m_peer_info->seed = true; + // if we're a seed too, disconnect + if (t->is_seed()) + { + throw std::runtime_error("seed to seed connection redundant, disconnecting"); + } + m_num_pieces = num_pieces; + t->peer_has_all(); + if (!t->is_finished()) + t->get_policy().peer_is_interesting(*this); + return; + } + + m_num_pieces = num_pieces; + // if we're a seed, we don't keep track of piece availability + if (!t->is_seed()) + { + bool interesting = false; + for (int i = 0; i < int(m_have_piece.size()); ++i) + { + if (m_have_piece[i]) + { + t->peer_has(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->get_policy().peer_is_interesting(*this); + } + } + + peer_connection::~peer_connection() + { +// INVARIANT_CHECK; + assert(m_disconnecting); + +#ifdef TORRENT_VERBOSE_LOGGING + if (m_logger) + { + (*m_logger) << time_now_string() + << " *** CONNECTION CLOSED\n"; + } +#endif +#ifndef NDEBUG + if (m_peer_info) + assert(m_peer_info->connection == 0); + + boost::shared_ptr t = m_torrent.lock(); + if (t) assert(t->connection_for(remote()) != this); +#endif + } + + void peer_connection::announce_piece(int index) + { + // dont announce during handshake + if (in_handshake()) return; + + // optimization, don't send have messages + // to peers that already have the piece + if (!m_ses.settings().send_redundant_have + && has_piece(index)) return; + +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << time_now_string() + << " ==> HAVE [ piece: " << index << "]\n"; +#endif + write_have(index); +#ifndef NDEBUG + boost::shared_ptr t = m_torrent.lock(); + assert(t); + assert(t->have_piece(index)); +#endif + } + + bool peer_connection::has_piece(int i) const + { + INVARIANT_CHECK; + + boost::shared_ptr t = m_torrent.lock(); + assert(t); + assert(t->valid_metadata()); + assert(i >= 0); + assert(i < t->torrent_file().num_pieces()); + return m_have_piece[i]; + } + + std::deque const& peer_connection::request_queue() const + { + return m_request_queue; + } + + std::deque const& peer_connection::download_queue() const + { + return m_download_queue; + } + + std::deque const& peer_connection::upload_queue() const + { + return m_requests; + } + + void peer_connection::add_stat(size_type downloaded, size_type uploaded) + { + INVARIANT_CHECK; + + m_statistics.add_stat(downloaded, uploaded); + } + + std::vector const& peer_connection::get_bitfield() const + { + return m_have_piece; + } + + void peer_connection::received_valid_data(int index) + { + INVARIANT_CHECK; + +#ifndef TORRENT_DISABLE_EXTENSIONS + for (extension_list_t::iterator i = m_extensions.begin() + , end(m_extensions.end()); i != end; ++i) + { + try { (*i)->on_piece_pass(index); } catch (std::exception&) {} + } +#endif + + if (peer_info_struct()) + { + peer_info_struct()->on_parole = false; + int& trust_points = peer_info_struct()->trust_points; + trust_points++; + // TODO: make this limit user settable + if (trust_points > 20) trust_points = 20; + } + } + + void peer_connection::received_invalid_data(int index) + { + INVARIANT_CHECK; + +#ifndef TORRENT_DISABLE_EXTENSIONS + for (extension_list_t::iterator i = m_extensions.begin() + , end(m_extensions.end()); i != end; ++i) + { + try { (*i)->on_piece_failed(index); } catch (std::exception&) {} + } +#endif + + if (peer_info_struct()) + { + peer_info_struct()->on_parole = true; + ++peer_info_struct()->hashfails; + int& trust_points = peer_info_struct()->trust_points; + + // we decrease more than we increase, to keep the + // allowed failed/passed ratio low. + // TODO: make this limit user settable + trust_points -= 2; + if (trust_points < -7) trust_points = -7; + } + } + + size_type peer_connection::total_free_upload() const + { + return m_free_upload; + } + + void peer_connection::add_free_upload(size_type free_upload) + { + INVARIANT_CHECK; + + m_free_upload += free_upload; + } + + // verifies a piece to see if it is valid (is within a valid range) + // and if it can correspond to a request generated by libtorrent. + bool peer_connection::verify_piece(const peer_request& p) const + { + INVARIANT_CHECK; + + boost::shared_ptr t = m_torrent.lock(); + assert(t); + + assert(t->valid_metadata()); + + return p.piece >= 0 + && p.piece < t->torrent_file().num_pieces() + && p.length > 0 + && p.start >= 0 + && (p.length == t->block_size() + || (p.length < t->block_size() + && p.piece == t->torrent_file().num_pieces()-1 + && p.start + p.length == t->torrent_file().piece_size(p.piece)) + || (m_request_large_blocks + && p.length <= t->torrent_file().piece_size(p.piece))) + && p.start + p.length <= t->torrent_file().piece_size(p.piece) + && (p.start % t->block_size() == 0); + } + + struct disconnect_torrent + { + disconnect_torrent(boost::weak_ptr& t): m_t(&t) {} + ~disconnect_torrent() { if (m_t) m_t->reset(); } + void cancel() { m_t = 0; } + private: + boost::weak_ptr* m_t; + }; + + void peer_connection::attach_to_torrent(sha1_hash const& ih) + { + INVARIANT_CHECK; + + assert(!m_disconnecting); + m_torrent = m_ses.find_torrent(ih); + + boost::shared_ptr t = m_torrent.lock(); + + if (t && t->is_aborted()) + { + m_torrent.reset(); + t.reset(); + } + + if (!t) + { + // we couldn't find the torrent! +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << " couldn't find a torrent with the given info_hash\n"; +#endif + throw std::runtime_error("got info-hash that is not in our session"); + } + + disconnect_torrent disconnect(m_torrent); + if (t->is_paused()) + { + // paused torrents will not accept + // incoming connections +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << " rejected connection to paused torrent\n"; +#endif + throw std::runtime_error("connection rejected by paused torrent"); + } + + // check to make sure we don't have another connection with the same + // info_hash and peer_id. If we do. close this connection. + t->attach_peer(this); + + // if the torrent isn't ready to accept + // connections yet, we'll have to wait with + // our initialization + if (t->ready_for_connections()) init(); + + // assume the other end has no pieces + // if we don't have valid metadata yet, + // leave the vector unallocated + assert(m_num_pieces == 0); + std::fill(m_have_piece.begin(), m_have_piece.end(), false); + disconnect.cancel(); + } + + // message handlers + + // ----------------------------- + // --------- KEEPALIVE --------- + // ----------------------------- + + void peer_connection::incoming_keepalive() + { + INVARIANT_CHECK; + +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << time_now_string() << " <== KEEPALIVE\n"; +#endif + } + + // ----------------------------- + // ----------- CHOKE ----------- + // ----------------------------- + + void peer_connection::incoming_choke() + { + INVARIANT_CHECK; + + boost::shared_ptr t = m_torrent.lock(); + 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_choke()) return; + } +#endif + +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << time_now_string() << " <== CHOKE\n"; +#endif + m_peer_choked = true; + t->get_policy().choked(*this); + + if (!t->is_seed()) + { + piece_picker& p = t->picker(); + // remove all pieces from this peers download queue and + // remove the 'downloading' flag from piece_picker. + for (std::deque::iterator i = m_download_queue.begin(); + i != m_download_queue.end(); ++i) + { + p.abort_download(*i); + } + for (std::deque::const_iterator i = m_request_queue.begin() + , end(m_request_queue.end()); i != end; ++i) + { + // since this piece was skipped, clear it and allow it to + // be requested from other peers + p.abort_download(*i); + } + } + + m_download_queue.clear(); + m_request_queue.clear(); + } + + // ----------------------------- + // ---------- UNCHOKE ---------- + // ----------------------------- + + void peer_connection::incoming_unchoke() + { + INVARIANT_CHECK; + + boost::shared_ptr t = m_torrent.lock(); + 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_unchoke()) return; + } +#endif + +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << time_now_string() << " <== UNCHOKE\n"; +#endif + m_peer_choked = false; + t->get_policy().unchoked(*this); + } + + // ----------------------------- + // -------- INTERESTED --------- + // ----------------------------- + + void peer_connection::incoming_interested() + { + INVARIANT_CHECK; + + boost::shared_ptr t = m_torrent.lock(); + 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 + +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << time_now_string() << " <== INTERESTED\n"; +#endif + m_peer_interested = true; + t->get_policy().interested(*this); + } + + // ----------------------------- + // ------ NOT INTERESTED ------- + // ----------------------------- + + void peer_connection::incoming_not_interested() + { + 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 = time_now(); + + // clear the request queue if the client isn't interested + m_requests.clear(); +// setup_send(); + +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << time_now_string() << " <== NOT_INTERESTED\n"; +#endif + + boost::shared_ptr t = m_torrent.lock(); + assert(t); + + m_peer_interested = false; + t->get_policy().not_interested(*this); + } + + // ----------------------------- + // ----------- HAVE ------------ + // ----------------------------- + + void peer_connection::incoming_have(int index) + { + INVARIANT_CHECK; + + boost::shared_ptr t = m_torrent.lock(); + 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 + +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << time_now_string() + << " <== HAVE [ piece: " << index << "]\n"; +#endif + + // if we got an invalid message, abort + if (index >= (int)m_have_piece.size() || index < 0) + throw protocol_error("got 'have'-message with higher index " + "than the number of pieces"); + + if (m_have_piece[index]) + { +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << " got redundant HAVE message for index: " << index << "\n"; +#endif + } + else + { + m_have_piece[index] = true; + + // only update the piece_picker if + // we have the metadata and if + // we're not a seed (in which case + // we won't have a piece picker) + if (t->valid_metadata()) + { + ++m_num_pieces; + t->peer_has(index); + + if (!t->have_piece(index) + && !t->is_seed() + && !is_interesting() + && t->picker().piece_priority(index) != 0) + t->get_policy().peer_is_interesting(*this); + } + + if (is_seed()) + { + assert(m_peer_info); + m_peer_info->seed = true; + if (t->is_seed()) + { + throw protocol_error("seed to seed connection redundant, disconnecting"); + } + } + } + } + + // ----------------------------- + // --------- BITFIELD ---------- + // ----------------------------- + + void peer_connection::incoming_bitfield(std::vector const& bitfield) + { + INVARIANT_CHECK; + + boost::shared_ptr t = m_torrent.lock(); + 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(bitfield)) return; + } +#endif + +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << time_now_string() << " <== BITFIELD "; + + for (int i = 0; i < int(bitfield.size()); ++i) + { + if (bitfield[i]) (*m_logger) << "1"; + else (*m_logger) << "0"; + } + (*m_logger) << "\n"; +#endif + + // if we don't have the metedata, we cannot + // verify the bitfield size + if (t->valid_metadata() + && (bitfield.size() / 8) != (m_have_piece.size() / 8)) + throw protocol_error("got bitfield with invalid size: " + + boost::lexical_cast(bitfield.size() / 8) + + "bytes. expected: " + + boost::lexical_cast(m_have_piece.size() / 8) + + "bytes"); + + // 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()) + { + m_have_piece = bitfield; + m_num_pieces = std::count(bitfield.begin(), bitfield.end(), true); + + if (m_peer_info) m_peer_info->seed = true; + return; + } + + int num_pieces = std::count(bitfield.begin(), bitfield.end(), true); + if (num_pieces == int(m_have_piece.size())) + { +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << " *** THIS IS A SEED ***\n"; +#endif + // if this is a web seed. we don't have a peer_info struct + if (m_peer_info) m_peer_info->seed = true; + // if we're a seed too, disconnect + if (t->is_seed()) + { + throw protocol_error("seed to seed connection redundant, disconnecting"); + } + + std::fill(m_have_piece.begin(), m_have_piece.end(), true); + m_num_pieces = num_pieces; + t->peer_has_all(); + if (!t->is_finished()) + t->get_policy().peer_is_interesting(*this); + return; + } + + // let the torrent know which pieces the + // peer has + // if we're a seed, we don't keep track of piece availability + if (!t->is_seed()) + { + bool interesting = false; + for (int i = 0; i < (int)m_have_piece.size(); ++i) + { + bool have = bitfield[i]; + if (have && !m_have_piece[i]) + { + m_have_piece[i] = true; + ++m_num_pieces; + t->peer_has(i); + if (!t->have_piece(i) && t->picker().piece_priority(i) != 0) + interesting = true; + } + else if (!have && m_have_piece[i]) + { + // this should probably not be allowed + m_have_piece[i] = false; + --m_num_pieces; + t->peer_lost(i); + } + } + + if (interesting) t->get_policy().peer_is_interesting(*this); + } + else + { + for (int i = 0; i < (int)m_have_piece.size(); ++i) + { + bool have = bitfield[i]; + if (have && !m_have_piece[i]) + { + m_have_piece[i] = true; + ++m_num_pieces; + } + else if (!have && m_have_piece[i]) + { + // this should probably not be allowed + m_have_piece[i] = false; + --m_num_pieces; + } + } + } + } + + // ----------------------------- + // ---------- REQUEST ---------- + // ----------------------------- + + void peer_connection::incoming_request(peer_request const& r) + { + INVARIANT_CHECK; + + boost::shared_ptr t = m_torrent.lock(); + 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_request(r)) return; + } +#endif + + if (!t->valid_metadata()) + { + // if we don't have valid metadata yet, + // we shouldn't get a request +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << time_now_string() + << " <== UNEXPECTED_REQUEST [ " + "piece: " << r.piece << " | " + "s: " << r.start << " | " + "l: " << r.length << " | " + "i: " << m_peer_interested << " | " + "t: " << (int)t->torrent_file().piece_size(r.piece) << " | " + "n: " << t->torrent_file().num_pieces() << " ]\n"; +#endif + return; + } + + if (int(m_requests.size()) > m_ses.settings().max_allowed_in_request_queue) + { + // don't allow clients to abuse our + // memory consumption. + // ignore requests if the client + // is making too many of them. +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << time_now_string() + << " <== TOO MANY REQUESTS [ " + "piece: " << r.piece << " | " + "s: " << r.start << " | " + "l: " << r.length << " | " + "i: " << m_peer_interested << " | " + "t: " << (int)t->torrent_file().piece_size(r.piece) << " | " + "n: " << t->torrent_file().num_pieces() << " ]\n"; +#endif + return; + } + + // make sure this request + // is legal and that the peer + // is not choked + if (r.piece >= 0 + && r.piece < t->torrent_file().num_pieces() + && t->have_piece(r.piece) + && r.start >= 0 + && r.start < t->torrent_file().piece_size(r.piece) + && r.length > 0 + && r.length + r.start <= t->torrent_file().piece_size(r.piece) + && m_peer_interested) + { +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << time_now_string() + << " <== REQUEST [ piece: " << r.piece << " | s: " << r.start << " | l: " << r.length << " ]\n"; +#endif + // if we have choked the client + // ignore the request + if (m_choked) + return; + + m_requests.push_back(r); + fill_send_buffer(); + } + else + { +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << time_now_string() + << " <== INVALID_REQUEST [ " + "piece: " << r.piece << " | " + "s: " << r.start << " | " + "l: " << r.length << " | " + "i: " << m_peer_interested << " | " + "t: " << (int)t->torrent_file().piece_size(r.piece) << " | " + "n: " << t->torrent_file().num_pieces() << " | " + "h: " << t->have_piece(r.piece) << " ]\n"; +#endif + + ++m_num_invalid_requests; + + if (t->alerts().should_post(alert::debug)) + { + t->alerts().post_alert(invalid_request_alert( + r + , t->get_handle() + , m_remote + , m_peer_id + , "peer sent an illegal piece request, ignoring")); + } + } + } + + void peer_connection::incoming_piece_fragment() + { + m_last_piece = time_now(); + } + +#ifndef NDEBUG + 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()); + + std::vector const& dl_queue + = t->picker().get_download_queue(); + + for (std::vector::const_iterator i = + dl_queue.begin(); i != dl_queue.end(); ++i) + { + assert(i->finished < blocks_per_piece); + } + } + } + + shared_ptr t; + }; +#endif + + + // ----------------------------- + // ----------- PIECE ----------- + // ----------------------------- + + void peer_connection::incoming_piece(peer_request const& p, char const* data) + { + INVARIANT_CHECK; + + boost::shared_ptr t = m_torrent.lock(); + 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_piece(p, data)) return; + } +#endif + +#ifndef NDEBUG + check_postcondition post_checker_(t); + t->check_invariant(); +#endif + +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << time_now_string() + << " <== PIECE [ piece: " << p.piece << " | " + "s: " << p.start << " | " + "l: " << p.length << " | " + "ds: " << statistics().download_rate() << " | " + "qs: " << m_desired_queue_size << " ]\n"; +#endif + + if (!verify_piece(p)) + { +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << time_now_string() + << " <== INVALID_PIECE [ piece: " << p.piece << " | " + "start: " << p.start << " | " + "length: " << p.length << " ]\n"; +#endif + throw protocol_error("got invalid piece packet"); + } + + // if we're already seeding, don't bother, + // just ignore it + if (t->is_seed()) + { + t->received_redundant_data(p.length); + return; + } + + piece_picker& picker = t->picker(); + piece_manager& fs = t->filesystem(); + policy& pol = t->get_policy(); + + std::vector finished_blocks; + piece_block block_finished(p.piece, p.start / t->block_size()); + assert(p.start % t->block_size() == 0); + assert(p.length == t->block_size() + || p.length == t->torrent_file().total_size() % t->block_size()); + + std::deque::iterator b + = std::find( + m_download_queue.begin() + , m_download_queue.end() + , block_finished); + + // if there's another peer that needs to do another + // piece request, this will point to it + peer_connection* request_peer = 0; + + if (b != m_download_queue.end()) + { + if (m_assume_fifo) + { + for (std::deque::iterator i = m_download_queue.begin(); + i != b; ++i) + { +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << time_now_string() + << " *** SKIPPED_PIECE [ piece: " << i->piece_index << " | " + "b: " << i->block_index << " ] ***\n"; +#endif + // since this piece was skipped, clear it and allow it to + // be requested from other peers + // TODO: send cancel? + picker.abort_download(*i); + } + + // remove the request that just finished + // from the download queue plus the + // skipped blocks. + m_download_queue.erase(m_download_queue.begin() + , boost::next(b)); + } + else + { + m_download_queue.erase(b); + } + } + else + { + // cancel the block from the + // peer that has taken over it. + boost::optional peer + = t->picker().get_downloader(block_finished); + if (peer) + { + assert(!t->picker().is_finished(block_finished)); + peer_connection* pc = t->connection_for(*peer); + if (pc && pc != this) + { + pc->cancel_request(block_finished); + request_peer = pc; + } + } + else + { + if (t->alerts().should_post(alert::debug)) + { + t->alerts().post_alert( + peer_error_alert( + m_remote + , m_peer_id + , "got a block that was not requested")); + } +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << " *** The block we just got was not in the " + "request queue ***\n"; +#endif + } + } + + // if the block we got is already finished, then ignore it + if (picker.is_finished(block_finished)) + { + t->received_redundant_data(t->block_size()); + pol.block_finished(*this, block_finished); + send_block_requests(); + + if (request_peer && !request_peer->has_peer_choked() && !t->is_seed()) + { + request_a_block(*t, *request_peer); + request_peer->send_block_requests(); + } + return; + } + + fs.write(data, p.piece, p.start, p.length); + + picker.mark_as_finished(block_finished, m_remote); + + try + { + pol.block_finished(*this, block_finished); + send_block_requests(); + } + catch (std::exception const&) {} + + if (request_peer && !request_peer->has_peer_choked() && !t->is_seed()) + { + request_a_block(*t, *request_peer); + request_peer->send_block_requests(); + } + +#ifndef NDEBUG + try + { +#endif + + bool was_seed = t->is_seed(); + bool was_finished = picker.num_filtered() + t->num_pieces() + == t->torrent_file().num_pieces(); + + // did we just finish the piece? + if (picker.is_piece_finished(p.piece)) + { +#ifndef NDEBUG + check_postcondition post_checker2_(t, false); +#endif + bool verified = t->verify_piece(p.piece); + if (verified) + { + // the following call may cause picker to become invalid + // in case we just became a seed + t->announce_piece(p.piece); + assert(t->valid_metadata()); + // if we just became a seed, picker is now invalid, since it + // is deallocated by the torrent once it starts seeding + if (!was_finished + && (t->is_seed() + || picker.num_filtered() + t->num_pieces() + == t->torrent_file().num_pieces())) + { + // torrent finished + // i.e. all the pieces we're interested in have + // been downloaded. Release the files (they will open + // in read only mode if needed) + try { t->finished(); } + catch (std::exception& e) + { +#ifndef NDEBUG + std::cerr << e.what() << std::endl; + assert(false); +#endif + } + } + } + else + { + t->piece_failed(p.piece); + } + +#ifndef NDEBUG + try + { +#endif + + pol.piece_finished(p.piece, verified); + +#ifndef NDEBUG + } + catch (std::exception const& e) + { + std::cerr << e.what() << std::endl; + assert(false); + } +#endif + +#ifndef NDEBUG + try + { +#endif + + if (!was_seed && t->is_seed()) + { + assert(verified); + t->completed(); + } + +#ifndef NDEBUG + } + catch (std::exception const& e) + { + std::cerr << e.what() << std::endl; + assert(false); + } +#endif + + } + +#ifndef NDEBUG + } + catch (std::exception const& e) + { + std::cerr << e.what() << std::endl; + assert(false); + } +#endif + } + + // ----------------------------- + // ---------- CANCEL ----------- + // ----------------------------- + + void peer_connection::incoming_cancel(peer_request const& r) + { + 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 + +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << time_now_string() + << " <== CANCEL [ piece: " << r.piece << " | s: " << r.start << " | l: " << r.length << " ]\n"; +#endif + + std::deque::iterator i + = std::find(m_requests.begin(), m_requests.end(), r); + + if (i != m_requests.end()) + { + m_requests.erase(i); + } + else + { +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << time_now_string() << " *** GOT CANCEL NOT IN THE QUEUE\n"; +#endif + } + } + + // ----------------------------- + // --------- DHT PORT ---------- + // ----------------------------- + + void peer_connection::incoming_dht_port(int listen_port) + { + INVARIANT_CHECK; + +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << time_now_string() + << " <== DHT_PORT [ p: " << listen_port << " ]\n"; +#endif +#ifndef TORRENT_DISABLE_DHT + m_ses.add_dht_node(udp::endpoint( + m_remote.address(), listen_port)); +#endif + } + + void peer_connection::add_request(piece_block const& block) + { + INVARIANT_CHECK; + + boost::shared_ptr t = m_torrent.lock(); + assert(t); + + assert(t->valid_metadata()); + assert(block.piece_index >= 0); + assert(block.piece_index < t->torrent_file().num_pieces()); + assert(block.block_index >= 0); + assert(block.block_index < t->torrent_file().piece_size(block.piece_index)); + assert(!t->picker().is_downloading(block)); + + piece_picker::piece_state_t state; + peer_speed_t speed = peer_speed(); + if (speed == fast) state = piece_picker::fast; + else if (speed == medium) state = piece_picker::medium; + else state = piece_picker::slow; + + t->picker().mark_as_downloading(block, m_remote, state); + + m_request_queue.push_back(block); + } + + void peer_connection::cancel_request(piece_block const& block) + { + INVARIANT_CHECK; + + boost::shared_ptr t = m_torrent.lock(); + assert(t); + + assert(t->valid_metadata()); + + assert(block.piece_index >= 0); + assert(block.piece_index < t->torrent_file().num_pieces()); + assert(block.block_index >= 0); + assert(block.block_index < t->torrent_file().piece_size(block.piece_index)); + assert(t->picker().is_downloading(block)); + + t->picker().abort_download(block); + + std::deque::iterator it + = std::find(m_download_queue.begin(), m_download_queue.end(), block); + if (it == m_download_queue.end()) + { + it = std::find(m_request_queue.begin(), m_request_queue.end(), block); + assert(it != m_request_queue.end()); + if (it == m_request_queue.end()) return; + m_request_queue.erase(it); + // since we found it in the request queue, it means it hasn't been + // sent yet, so we don't have to send a cancel. + return; + } + else + { + m_download_queue.erase(it); + } + + int block_offset = block.block_index * t->block_size(); + int block_size + = std::min((int)t->torrent_file().piece_size(block.piece_index)-block_offset, + t->block_size()); + assert(block_size > 0); + assert(block_size <= t->block_size()); + + peer_request r; + r.piece = block.piece_index; + r.start = block_offset; + r.length = block_size; + + write_cancel(r); + +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << time_now_string() + << " ==> CANCEL [ piece: " << block.piece_index << " | s: " + << block_offset << " | l: " << block_size << " | " << block.block_index << " ]\n"; +#endif + } + + void peer_connection::send_choke() + { + INVARIANT_CHECK; + + if (m_choked) return; + write_choke(); + m_choked = true; + +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << time_now_string() << " ==> CHOKE\n"; +#endif +#ifndef NDEBUG + m_last_choke = time_now(); +#endif + m_num_invalid_requests = 0; + m_requests.clear(); + } + + void peer_connection::send_unchoke() + { + INVARIANT_CHECK; + +#ifndef NDEBUG + // TODO: once the policy lowers the interval for optimistic + // unchoke, increase this value that interval + // this condition cannot be guaranteed since if peers disconnect + // a new one will be unchoked ignoring when it was last choked + //assert(time_now() - m_last_choke > seconds(9)); +#endif + + if (!m_choked) return; + write_unchoke(); + m_choked = false; + +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << time_now_string() << " ==> UNCHOKE\n"; +#endif + } + + void peer_connection::send_interested() + { + INVARIANT_CHECK; + + if (m_interesting) return; + write_interested(); + m_interesting = true; + +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << time_now_string() << " ==> INTERESTED\n"; +#endif + } + + void peer_connection::send_not_interested() + { + INVARIANT_CHECK; + + if (!m_interesting) return; + write_not_interested(); + m_interesting = false; + + m_became_uninteresting = time_now(); + +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << time_now_string() << " ==> NOT_INTERESTED\n"; +#endif + } + + void peer_connection::send_block_requests() + { + INVARIANT_CHECK; + + if (has_peer_choked()) return; + + boost::shared_ptr t = m_torrent.lock(); + assert(t); + + assert(!has_peer_choked()); + + if ((int)m_download_queue.size() >= m_desired_queue_size) return; + + while (!m_request_queue.empty() + && (int)m_download_queue.size() < m_desired_queue_size) + { + piece_block block = m_request_queue.front(); + + int block_offset = block.block_index * t->block_size(); + int block_size = std::min((int)t->torrent_file().piece_size( + block.piece_index) - block_offset, t->block_size()); + assert(block_size > 0); + assert(block_size <= t->block_size()); + + peer_request r; + r.piece = block.piece_index; + r.start = block_offset; + r.length = block_size; + + m_request_queue.pop_front(); + m_download_queue.push_back(block); +/* +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << time_now_string() + << " *** REQUEST-QUEUE** [ " + "piece: " << block.piece_index << " | " + "block: " << block.block_index << " ]\n"; +#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) + { + while (!m_request_queue.empty() + && m_request_queue.front().piece_index == r.piece + && m_request_queue.front().block_index == block.block_index + 1) + { + block = m_request_queue.front(); + m_request_queue.pop_front(); + m_download_queue.push_back(block); +/* +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << time_now_string() + << " *** REQUEST-QUEUE** [ " + "piece: " << block.piece_index << " | " + "block: " << block.block_index << " ]\n"; +#endif +*/ + block_offset = block.block_index * t->block_size(); + block_size = std::min((int)t->torrent_file().piece_size( + block.piece_index) - block_offset, t->block_size()); + assert(block_size > 0); + assert(block_size <= t->block_size()); + + r.length += block_size; + } + } + + assert(verify_piece(r)); + +#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 (!handled) + { + write_request(r); + m_last_request = time_now(); + } +#else + write_request(r); + m_last_request = time_now(); +#endif + +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << time_now_string() + << " ==> REQUEST [ " + "piece: " << r.piece << " | " + "s: " << r.start << " | " + "l: " << r.length << " | " + "ds: " << statistics().download_rate() << " B/s | " + "qs: " << m_desired_queue_size << " ]\n"; +#endif + } + m_last_piece = time_now(); + } + + + void close_socket_ignore_error(boost::shared_ptr s) + { + try { s->close(); } catch (std::exception& e) {} + } + + void peer_connection::timed_out() + { + if (m_peer_info) ++m_peer_info->failcount; +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + (*m_ses.m_logger) << "CONNECTION TIMED OUT: " << m_remote.address().to_string() + << "\n"; +#endif + m_ses.connection_failed(m_socket, m_remote, "timed out"); + } + + void peer_connection::disconnect() + { + boost::intrusive_ptr me(this); + + INVARIANT_CHECK; + + if (m_disconnecting) return; + m_disconnecting = true; + if (m_connecting) + m_ses.m_half_open.done(m_connection_ticket); + + m_ses.m_io_service.post(boost::bind(&close_socket_ignore_error, m_socket)); + + boost::shared_ptr t = m_torrent.lock(); + + if (t) + { + if (t->has_picker()) + { + piece_picker& picker = t->picker(); + + while (!m_download_queue.empty()) + { + picker.abort_download(m_download_queue.back()); + m_download_queue.pop_back(); + } + while (!m_request_queue.empty()) + { + picker.abort_download(m_request_queue.back()); + m_request_queue.pop_back(); + } + } + + t->remove_peer(this); + + m_torrent.reset(); + } + + m_ses.close_connection(me); + } + + void peer_connection::set_upload_limit(int limit) + { + assert(limit >= -1); + if (limit == -1) limit = resource_request::inf; + if (limit < 10) limit = 10; + m_upload_limit = limit; + m_bandwidth_limit[upload_channel].throttle(m_upload_limit); + } + + void peer_connection::set_download_limit(int limit) + { + assert(limit >= -1); + if (limit == -1) limit = resource_request::inf; + if (limit < 10) limit = 10; + m_download_limit = limit; + m_bandwidth_limit[download_channel].throttle(m_download_limit); + } + + size_type peer_connection::share_diff() const + { + INVARIANT_CHECK; + + boost::shared_ptr t = m_torrent.lock(); + assert(t); + + float ratio = t->ratio(); + + // if we have an infinite ratio, just say we have downloaded + // much more than we have uploaded. And we'll keep uploading. + if (ratio == 0.f) + return std::numeric_limits::max(); + + return m_free_upload + + static_cast(m_statistics.total_payload_download() * ratio) + - m_statistics.total_payload_upload(); + } + + // defined in upnp.cpp + bool is_local(address const& a); + + bool peer_connection::on_local_network() const + { + if (libtorrent::is_local(m_remote.address())) return true; + return false; + } + + void peer_connection::get_peer_info(peer_info& p) const + { + assert(!associated_torrent().expired()); + + p.down_speed = statistics().download_rate(); + p.up_speed = statistics().upload_rate(); + p.payload_down_speed = statistics().download_payload_rate(); + p.payload_up_speed = statistics().upload_payload_rate(); + p.pid = pid(); + p.ip = remote(); + +#ifndef TORRENT_DISABLE_RESOLVE_COUNTRIES + p.country[0] = m_country[0]; + p.country[1] = m_country[1]; +#endif + + p.total_download = statistics().total_payload_download(); + p.total_upload = statistics().total_payload_upload(); + + if (m_bandwidth_limit[upload_channel].throttle() == bandwidth_limit::inf) + p.upload_limit = -1; + else + p.upload_limit = m_bandwidth_limit[upload_channel].throttle(); + + if (m_bandwidth_limit[download_channel].throttle() == bandwidth_limit::inf) + p.download_limit = -1; + else + p.download_limit = m_bandwidth_limit[download_channel].throttle(); + + p.load_balancing = total_free_upload(); + + p.download_queue_length = (int)download_queue().size(); + p.upload_queue_length = (int)upload_queue().size(); + + if (boost::optional 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(); + ptime now = time_now(); + 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; + if (peer_info_struct()) + { + p.source = peer_info_struct()->source; + p.failcount = peer_info_struct()->failcount; + p.num_hashfails = peer_info_struct()->hashfails; + p.flags |= peer_info_struct()->on_parole ? peer_info::on_parole : 0; + } + else + { + p.source = 0; + p.failcount = 0; + p.num_hashfails = 0; + } + + p.send_buffer_size = send_buffer_size(); + } + + void peer_connection::cut_receive_buffer(int size, int packet_size) + { + INVARIANT_CHECK; + + assert(packet_size > 0); + assert(int(m_recv_buffer.size()) >= size); + assert(int(m_recv_buffer.size()) >= m_recv_pos); + assert(m_recv_pos >= size); + + if (size > 0) + std::memmove(&m_recv_buffer[0], &m_recv_buffer[0] + size, m_recv_pos - size); + + m_recv_pos -= size; + +#ifndef NDEBUG + std::fill(m_recv_buffer.begin() + m_recv_pos, m_recv_buffer.end(), 0); +#endif + + m_packet_size = packet_size; + if (m_packet_size >= m_recv_pos) m_recv_buffer.resize(m_packet_size); + } + + void peer_connection::second_tick(float tick_interval) + { + INVARIANT_CHECK; + + ptime now(time_now()); + + boost::shared_ptr t = m_torrent.lock(); + assert(t); + + on_tick(); + +#ifndef TORRENT_DISABLE_EXTENSIONS + for (extension_list_t::iterator i = m_extensions.begin() + , end(m_extensions.end()); i != end; ++i) + { + (*i)->tick(); + } +#endif + + m_ignore_bandwidth_limits = m_ses.settings().ignore_limits_on_local_network + && on_local_network(); + + m_statistics.second_tick(tick_interval); + + if (!t->valid_metadata()) return; + + // calculate the desired download queue size + const float queue_time = m_ses.settings().request_queue_time; + // (if the latency is more than this, the download will stall) + // so, the queue size is queue_time * down_rate / 16 kiB + // (16 kB is the size of each request) + // the minimum number of requests is 2 and the maximum is 48 + // the block size doesn't have to be 16. So we first query the + // torrent for it + const int block_size = m_request_large_blocks + ? t->torrent_file().piece_length() : t->block_size(); + assert(block_size > 0); + + m_desired_queue_size = static_cast(queue_time + * statistics().download_rate() / block_size); + if (m_desired_queue_size > m_max_out_request_queue) + m_desired_queue_size = m_max_out_request_queue; + if (m_desired_queue_size < min_request_queue) + m_desired_queue_size = min_request_queue; + + if (!m_download_queue.empty() + && now - m_last_piece > seconds(m_ses.settings().piece_timeout)) + { + // this peer isn't sending the pieces we've + // requested (this has been observed by BitComet) + // in this case we'll clear our download queue and + // re-request the blocks. +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << time_now_string() + << " *** PIECE_REQUESTS TIMED OUT [ " << (int)m_download_queue.size() + << " " << total_seconds(now - m_last_piece) << "] ***\n"; +#endif + + if (t->is_seed()) + { + m_download_queue.clear(); + m_request_queue.clear(); + } + else + { + piece_picker& picker = t->picker(); + while (!m_download_queue.empty()) + { + picker.abort_download(m_download_queue.back()); + m_download_queue.pop_back(); + } + while (!m_request_queue.empty()) + { + picker.abort_download(m_request_queue.back()); + m_request_queue.pop_back(); + } + + // TODO: If we have a limited number of upload + // slots, choke this peer + + m_assume_fifo = true; + + request_a_block(*t, *this); + send_block_requests(); + } + } + + m_statistics.second_tick(tick_interval); + + // If the client sends more data + // we send it data faster, otherwise, slower. + // It will also depend on how much data the + // client has sent us. This is the mean to + // maintain the share ratio given by m_ratio + // with all peers. + + if (t->is_seed() || is_choked() || t->ratio() == 0.0f) + { + // if we have downloaded more than one piece more + // than we have uploaded OR if we are a seed + // have an unlimited upload rate + m_bandwidth_limit[upload_channel].throttle(m_upload_limit); + } + else + { + size_type bias = 0x10000 + 2 * t->block_size() + m_free_upload; + + double break_even_time = 15; // seconds. + size_type have_uploaded = m_statistics.total_payload_upload(); + size_type have_downloaded = m_statistics.total_payload_download(); + double download_speed = m_statistics.download_rate(); + + size_type soon_downloaded = + have_downloaded + (size_type)(download_speed * break_even_time*1.5); + + if (t->ratio() != 1.f) + soon_downloaded = (size_type)(soon_downloaded*(double)t->ratio()); + + double upload_speed_limit = std::min((soon_downloaded - have_uploaded + + bias) / break_even_time, double(m_upload_limit)); + + upload_speed_limit = std::min(upload_speed_limit, + (double)std::numeric_limits::max()); + + m_bandwidth_limit[upload_channel].throttle( + std::min(std::max((int)upload_speed_limit, 20) + , m_upload_limit)); + } + + fill_send_buffer(); +/* + size_type diff = share_diff(); + + enum { block_limit = 2 }; // how many blocks difference is considered unfair + + // if the peer has been choked, send the current piece + // as fast as possible + if (diff > block_limit*m_torrent->block_size() || m_torrent->is_seed() || is_choked()) + { + // if we have downloaded more than one piece more + // than we have uploaded OR if we are a seed + // have an unlimited upload rate + m_ul_bandwidth_quota.wanted = std::numeric_limits::max(); + } + else + { + float ratio = m_torrent->ratio(); + // if we have downloaded too much, response with an + // upload rate of 10 kB/s more than we dowlload + // if we have uploaded too much, send with a rate of + // 10 kB/s less than we receive + int bias = 0; + if (diff > -block_limit*m_torrent->block_size()) + { + bias = static_cast(m_statistics.download_rate() * ratio) / 2; + if (bias < 10*1024) bias = 10*1024; + } + else + { + bias = -static_cast(m_statistics.download_rate() * ratio) / 2; + } + m_ul_bandwidth_quota.wanted = static_cast(m_statistics.download_rate()) + bias; + + // the maximum send_quota given our download rate from this peer + if (m_ul_bandwidth_quota.wanted < 256) m_ul_bandwidth_quota.wanted = 256; + } +*/ + } + + void peer_connection::fill_send_buffer() + { + INVARIANT_CHECK; + + boost::shared_ptr t = m_torrent.lock(); + if (!t) return; + + // only add new piece-chunks if the send buffer is small enough + // otherwise there will be no end to how large it will be! + + int buffer_size_watermark = int(m_statistics.upload_rate()) / 2; + if (buffer_size_watermark < 1024) buffer_size_watermark = 1024; + else if (buffer_size_watermark > 80 * 1024) buffer_size_watermark = 80 * 1024; + + while (!m_requests.empty() + && (send_buffer_size() < buffer_size_watermark) + && !m_choked) + { + assert(t->valid_metadata()); + peer_request& r = m_requests.front(); + + assert(r.piece >= 0); + assert(r.piece < (int)m_have_piece.size()); + assert(t->have_piece(r.piece)); + assert(r.start + r.length <= t->torrent_file().piece_size(r.piece)); + assert(r.length > 0 && r.start >= 0); + + write_piece(r); + +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << time_now_string() + << " ==> PIECE [ piece: " << r.piece << " | s: " << r.start + << " | l: " << r.length << " ]\n"; +#endif + + m_requests.erase(m_requests.begin()); + + if (m_requests.empty() + && m_num_invalid_requests > 0 + && is_peer_interested() + && !is_seed()) + { + // this will make the peer clear + // its download queue and re-request + // pieces. Hopefully it will not + // send invalid requests then + send_choke(); + send_unchoke(); + } + } + } + + void peer_connection::assign_bandwidth(int channel, int amount) + { + session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); + +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << "bandwidth [ " << channel << " ] + " << amount << "\n"; +#endif + + m_bandwidth_limit[channel].assign(amount); + if (channel == upload_channel) + { + m_writing = false; + setup_send(); + } + else if (channel == download_channel) + { + m_reading = false; + setup_receive(); + } + } + + void peer_connection::expire_bandwidth(int channel, int amount) + { + session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); + + m_bandwidth_limit[channel].expire(amount); + if (channel == upload_channel) + { + setup_send(); + } + else if (channel == download_channel) + { + setup_receive(); + } + } + + void peer_connection::setup_send() + { + session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); + + INVARIANT_CHECK; + + if (m_writing) return; + + shared_ptr t = m_torrent.lock(); + + if (m_bandwidth_limit[upload_channel].quota_left() == 0 + && (!m_send_buffer[m_current_send_buffer].empty() + || !m_send_buffer[(m_current_send_buffer + 1) & 1].empty()) + && !m_connecting + && t + && !m_ignore_bandwidth_limits) + { + // in this case, we have data to send, but no + // bandwidth. So, we simply request bandwidth + // from the torrent + assert(t); + if (m_bandwidth_limit[upload_channel].max_assignable() > 0) + { +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << "req bandwidth [ " << upload_channel << " ]\n"; +#endif + + // peers that we are not interested in are non-prioritized + t->request_bandwidth(upload_channel, self() + , !(is_interesting() && !has_peer_choked())); + m_writing = true; + } + return; + } + + if (!can_write()) return; + + assert(!m_writing); + + int sending_buffer = (m_current_send_buffer + 1) & 1; + if (m_send_buffer[sending_buffer].empty()) + { + // this means we have to swap buffer, because there's no + // previous buffer we're still waiting for. + std::swap(m_current_send_buffer, sending_buffer); + m_write_pos = 0; + } + + // send the actual buffer + if (!m_send_buffer[sending_buffer].empty()) + { + int amount_to_send = (int)m_send_buffer[sending_buffer].size() - m_write_pos; + int quota_left = m_bandwidth_limit[upload_channel].quota_left(); + if (!m_ignore_bandwidth_limits && amount_to_send > quota_left) + amount_to_send = quota_left; + + assert(amount_to_send > 0); + + assert(m_write_pos < (int)m_send_buffer[sending_buffer].size()); +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << "async_write " << amount_to_send << " bytes\n"; +#endif + m_socket->async_write_some(asio::buffer( + &m_send_buffer[sending_buffer][m_write_pos], amount_to_send) + , bind(&peer_connection::on_send_data, self(), _1, _2)); + + m_writing = true; + } + } + + void peer_connection::setup_receive() + { + session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); + + INVARIANT_CHECK; + + if (m_reading) return; + + shared_ptr t = m_torrent.lock(); + + if (m_bandwidth_limit[download_channel].quota_left() == 0 + && !m_connecting + && t + && !m_ignore_bandwidth_limits) + { + if (m_bandwidth_limit[download_channel].max_assignable() > 0) + { +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << "req bandwidth [ " << download_channel << " ]\n"; +#endif + t->request_bandwidth(download_channel, self(), m_non_prioritized); + m_reading = true; + } + return; + } + + if (!can_read()) return; + + assert(m_packet_size > 0); + int max_receive = m_packet_size - m_recv_pos; + int quota_left = m_bandwidth_limit[download_channel].quota_left(); + if (!m_ignore_bandwidth_limits && max_receive > quota_left) + max_receive = quota_left; + + assert(max_receive > 0); + + assert(m_recv_pos >= 0); + assert(m_packet_size > 0); + + assert(can_read()); +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << "async_read " << max_receive << " bytes\n"; +#endif + m_socket->async_read_some(asio::buffer(&m_recv_buffer[m_recv_pos] + , max_receive), bind(&peer_connection::on_receive_data, self(), _1, _2)); + m_reading = true; + } + + void peer_connection::reset_recv_buffer(int packet_size) + { + assert(packet_size > 0); + if (m_recv_pos > m_packet_size) + { + cut_receive_buffer(m_packet_size, packet_size); + return; + } + m_recv_pos = 0; + m_packet_size = packet_size; + if (int(m_recv_buffer.size()) < m_packet_size) + m_recv_buffer.resize(m_packet_size); + } + + void peer_connection::send_buffer(char const* begin, char const* end) + { + std::vector& buf = m_send_buffer[m_current_send_buffer]; + buf.insert(buf.end(), begin, end); + setup_send(); + } + +// TODO: change this interface to automatically call setup_send() when the +// return value is destructed + buffer::interval peer_connection::allocate_send_buffer(int size) + { + std::vector& buf = m_send_buffer[m_current_send_buffer]; + buf.resize(buf.size() + size); + buffer::interval ret(&buf[0] + buf.size() - size, &buf[0] + buf.size()); + return ret; + } + + template + struct set_to_zero + { + set_to_zero(T& v, bool cond): m_val(v), m_cond(cond) {} + void fire() { if (!m_cond) return; m_cond = false; m_val = 0; } + ~set_to_zero() { if (m_cond) m_val = 0; } + private: + T& m_val; + bool m_cond; + }; + + // -------------------------- + // RECEIVE DATA + // -------------------------- + + // throws exception when the client should be disconnected + void peer_connection::on_receive_data(const asio::error_code& error + , std::size_t bytes_transferred) try + { + session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); + + INVARIANT_CHECK; + + assert(m_reading); + m_reading = false; + + + if (error) + { +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << "**ERROR**: " << error.message() << "[in peer_connection::on_receive_data]\n"; +#endif + on_receive(error, bytes_transferred); + throw std::runtime_error(error.message()); + } + + do + { +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << "read " << bytes_transferred << " bytes\n"; +#endif + // correct the dl quota usage, if not all of the buffer was actually read + if (!m_ignore_bandwidth_limits) + m_bandwidth_limit[download_channel].use_quota(bytes_transferred); + + if (m_disconnecting) return; + + assert(m_packet_size > 0); + assert(bytes_transferred > 0); + + m_last_receive = time_now(); + m_recv_pos += bytes_transferred; + assert(m_recv_pos <= int(m_recv_buffer.size())); + + { + INVARIANT_CHECK; + on_receive(error, bytes_transferred); + } + + assert(m_packet_size > 0); + + if (m_peer_choked + && m_recv_pos == 0 + && (m_recv_buffer.capacity() - m_packet_size) > 128) + { + std::vector(m_packet_size).swap(m_recv_buffer); + } + + int max_receive = m_packet_size - m_recv_pos; + int quota_left = m_bandwidth_limit[download_channel].quota_left(); + if (!m_ignore_bandwidth_limits && max_receive > quota_left) + max_receive = quota_left; + + if (max_receive == 0) break; + + asio::error_code ec; + bytes_transferred = m_socket->read_some(asio::buffer(&m_recv_buffer[m_recv_pos] + , max_receive), ec); + if (ec && ec != asio::error::would_block) + throw asio::system_error(ec); + } + while (bytes_transferred > 0); + + setup_receive(); + } + catch (file_error& e) + { + session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); + + boost::shared_ptr t = m_torrent.lock(); + if (!t) + { + m_ses.connection_failed(m_socket, remote(), e.what()); + return; + } + + if (t->alerts().should_post(alert::fatal)) + { + t->alerts().post_alert( + file_error_alert(t->get_handle() + , std::string("torrent paused: ") + e.what())); + } + t->pause(); + } + catch (std::exception& e) + { + session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); + m_ses.connection_failed(m_socket, remote(), e.what()); + } + catch (...) + { + // all exceptions should derive from std::exception + assert(false); + session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); + m_ses.connection_failed(m_socket, remote(), "connection failed for unknown reason"); + } + + bool peer_connection::can_write() const + { + INVARIANT_CHECK; + + // if we have requests or pending data to be sent or announcements to be made + // we want to send data + return (!m_send_buffer[m_current_send_buffer].empty() + || !m_send_buffer[(m_current_send_buffer + 1) & 1].empty()) + && (m_bandwidth_limit[upload_channel].quota_left() > 0 + || m_ignore_bandwidth_limits) + && !m_connecting; + } + + bool peer_connection::can_read() const + { + INVARIANT_CHECK; + + return (m_bandwidth_limit[download_channel].quota_left() > 0 + || m_ignore_bandwidth_limits) + && !m_connecting; + } + + void peer_connection::connect(int ticket) + { + INVARIANT_CHECK; + +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + (*m_ses.m_logger) << "CONNECTING: " << m_remote.address().to_string() + << ":" << m_remote.port() << "\n"; +#endif + + m_connection_ticket = ticket; + boost::shared_ptr t = m_torrent.lock(); + assert(t); + + m_queued = false; + assert(m_connecting); + m_socket->open(t->get_interface().protocol()); + + // set the socket to non-blocking, so that we can + // read the entire buffer on each read event we get + tcp::socket::non_blocking_io ioc(true); + m_socket->io_control(ioc); + m_socket->bind(t->get_interface()); + m_socket->async_connect(m_remote + , bind(&peer_connection::on_connection_complete, self(), _1)); + + if (t->alerts().should_post(alert::debug)) + { + t->alerts().post_alert(peer_error_alert( + m_remote, m_peer_id, "connecting to peer")); + } + } + + void peer_connection::on_connection_complete(asio::error_code const& e) try + { + session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); + + INVARIANT_CHECK; + + if (m_disconnecting) return; + + m_connecting = false; + m_ses.m_half_open.done(m_connection_ticket); + + if (e) + { +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + (*m_ses.m_logger) << "CONNECTION FAILED: " << m_remote.address().to_string() + << ": " << e.message() << "\n"; +#endif + m_ses.connection_failed(m_socket, m_remote, e.message().c_str()); + return; + } + + if (m_disconnecting) return; + m_last_receive = time_now(); + + // this means the connection just succeeded + +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + (*m_ses.m_logger) << "COMPLETED: " << m_remote.address().to_string() << "\n"; +#endif + + on_connected(); + setup_send(); + setup_receive(); + } + catch (std::exception& ex) + { + session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); + m_ses.connection_failed(m_socket, remote(), ex.what()); + } + catch (...) + { + // all exceptions should derive from std::exception + assert(false); + session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); + m_ses.connection_failed(m_socket, remote(), "connection failed for unkown reason"); + } + + // -------------------------- + // SEND DATA + // -------------------------- + + // throws exception when the client should be disconnected + void peer_connection::on_send_data(asio::error_code const& error + , std::size_t bytes_transferred) try + { + session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); + + INVARIANT_CHECK; + + assert(m_writing); + m_writing = false; + + if (!m_ignore_bandwidth_limits) + m_bandwidth_limit[upload_channel].use_quota(bytes_transferred); + +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << "wrote " << bytes_transferred << " bytes\n"; +#endif + + m_write_pos += bytes_transferred; + + + if (error) + { +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << "**ERROR**: " << error.message() << " [in peer_connection::on_send_data]\n"; +#endif + throw std::runtime_error(error.message()); + } + if (m_disconnecting) return; + + assert(!m_connecting); + assert(bytes_transferred > 0); + + int sending_buffer = (m_current_send_buffer + 1) & 1; + + assert(int(m_send_buffer[sending_buffer].size()) >= m_write_pos); + if (int(m_send_buffer[sending_buffer].size()) == m_write_pos) + { + m_send_buffer[sending_buffer].clear(); + m_write_pos = 0; + } + + m_last_sent = time_now(); + + on_sent(error, bytes_transferred); + fill_send_buffer(); + + if (m_choked) + { + for (int i = 0; i < 2; ++i) + { + if (int(m_send_buffer[i].size()) < 64 + && int(m_send_buffer[i].capacity()) > 128) + { + std::vector tmp(m_send_buffer[i]); + tmp.swap(m_send_buffer[i]); + assert(m_send_buffer[i].capacity() == m_send_buffer[i].size()); + } + } + } + + setup_send(); + } + catch (std::exception& e) + { + session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); + m_ses.connection_failed(m_socket, remote(), e.what()); + } + catch (...) + { + // all exceptions should derive from std::exception + assert(false); + session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); + m_ses.connection_failed(m_socket, remote(), "connection failed for unknown reason"); + } + + +#ifndef NDEBUG + void peer_connection::check_invariant() const + { + if (m_peer_info) + assert(m_peer_info->connection == this + || m_peer_info->connection == 0); + + boost::shared_ptr t = m_torrent.lock(); + if (!t) + { + typedef session_impl::torrent_map torrent_map; + torrent_map& m = m_ses.m_torrents; + for (torrent_map::iterator i = m.begin(), end(m.end()); i != end; ++i) + { + torrent& t = *i->second; + assert(t.connection_for(m_remote) != this); + } + return; + } + + if (!m_in_constructor && t->connection_for(remote()) != this + && !m_ses.settings().allow_multiple_connections_per_ip) + { + assert(false); + } + +// expensive when using checked iterators +/* + if (t->valid_metadata()) + { + int piece_count = std::count(m_have_piece.begin() + , m_have_piece.end(), true); + if (m_num_pieces != piece_count) + { + assert(false); + } + } +*/ + assert(m_write_pos <= int(m_send_buffer[ + (m_current_send_buffer + 1) & 1].size())); + +// 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()) + { + assert(i->info[j].peer == m_remote); + } + else + { + assert(i->info[j].peer != m_remote || i->info[j].finished); + } + } + } + } +*/ + } +#endif + + bool peer_connection::has_timed_out() const + { + // TODO: the timeout should be called by an event + INVARIANT_CHECK; + +#ifndef NDEBUG + // allow step debugging without timing out + return false; +#endif + + ptime now(time_now()); + + // if the socket is still connecting, don't + // consider it timed out. Because Windows XP SP2 + // may delay connection attempts. + if (m_connecting) return false; + + // if the peer hasn't said a thing for a certain + // time, it is considered to have timed out + time_duration d; + d = time_now() - m_last_receive; + if (d > seconds(m_timeout)) return true; + + // TODO: as long as we have less than 95% of the + // global (or local) connection limit, connections should + // never time out for another reason + + // if the peer hasn't become interested and we haven't + // become interested in the peer for 10 minutes, it + // has also timed out. + time_duration d1; + time_duration d2; + d1 = now - m_became_uninterested; + d2 = now - m_became_uninteresting; + // TODO: these timeouts should be user settable + if (!m_interesting + && !m_peer_interested + && d1 > minutes(10) + && d2 > minutes(10)) + { + return true; + } + + return false; + } + + peer_connection::peer_speed_t peer_connection::peer_speed() + { + shared_ptr t = m_torrent.lock(); + assert(t); + + int download_rate = int(statistics().download_payload_rate()); + int torrent_download_rate = int(t->statistics().download_payload_rate()); + + if (download_rate > 512 && download_rate > torrent_download_rate / 16) + m_speed = fast; + else if (download_rate > 4096 && download_rate > torrent_download_rate / 64) + m_speed = medium; + else if (download_rate < torrent_download_rate / 15 && m_speed == fast) + m_speed = medium; + else if (download_rate < torrent_download_rate / 63 && m_speed == medium) + m_speed = slow; + + return m_speed; + } + + void peer_connection::keep_alive() + { + INVARIANT_CHECK; + + time_duration d; + d = time_now() - m_last_sent; + if (total_seconds(d) < m_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_writing) return; + +#ifdef TORRENT_VERBOSE_LOGGING + using namespace boost::posix_time; + (*m_logger) << time_now_string() << " ==> KEEPALIVE\n"; +#endif + + write_keepalive(); + } + + bool peer_connection::is_seed() const + { + INVARIANT_CHECK; + // if m_num_pieces == 0, we probably don't have the + // metadata yet. + return m_num_pieces == (int)m_have_piece.size() && m_num_pieces > 0; + } +} + diff --git a/encryption/libtorrent/src/piece_picker.cpp b/encryption/libtorrent/src/piece_picker.cpp new file mode 100755 index 000000000..cf6eb4a0e --- /dev/null +++ b/encryption/libtorrent/src/piece_picker.cpp @@ -0,0 +1,1446 @@ +/* + +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 "libtorrent/pch.hpp" + +#include +#include +#include +#include + +// non-standard header, is_sorted() +//#include + +#include "libtorrent/piece_picker.hpp" +#include "libtorrent/aux_/session_impl.hpp" + +#ifndef NDEBUG +#include "libtorrent/peer_connection.hpp" +#include "libtorrent/torrent.hpp" +#endif + +#define TORRENT_PIECE_PICKER_INVARIANT_CHECK INVARIANT_CHECK +//#define TORRENT_PIECE_PICKER_INVARIANT_CHECK + +namespace libtorrent +{ + + piece_picker::piece_picker(int blocks_per_piece, int total_num_blocks) + : m_piece_info(2) + , m_piece_map((total_num_blocks + blocks_per_piece-1) / blocks_per_piece) + , m_num_filtered(0) + , m_num_have_filtered(0) + , m_num_have(0) + , m_sequenced_download_threshold(100) + { + assert(blocks_per_piece > 0); + assert(total_num_blocks >= 0); +#ifndef NDEBUG + m_files_checked_called = false; +#endif + + // the piece index is stored in 20 bits, which limits the allowed + // number of pieces somewhat + if (m_piece_map.size() >= piece_pos::we_have_index) + throw std::runtime_error("too many pieces in torrent"); + + m_blocks_per_piece = blocks_per_piece; + m_blocks_in_last_piece = total_num_blocks % blocks_per_piece; + if (m_blocks_in_last_piece == 0) m_blocks_in_last_piece = blocks_per_piece; + + assert(m_blocks_in_last_piece <= m_blocks_per_piece); + + // allocate the piece_map to cover all pieces + // and make them invalid (as if though we already had every piece) + std::fill(m_piece_map.begin(), m_piece_map.end() + , piece_pos(0, piece_pos::we_have_index)); + m_num_have = m_piece_map.size(); + } + + // pieces is a bitmask with the pieces we have + void piece_picker::files_checked( + const std::vector& pieces + , const std::vector& unfinished) + { +#ifndef NDEBUG + m_files_checked_called = true; +#endif + for (std::vector::const_iterator i = pieces.begin(); + i != pieces.end(); ++i) + { + if (*i) continue; + int index = static_cast(i - pieces.begin()); + m_piece_map[index].index = 0; + --m_num_have; + if (m_piece_map[index].filtered()) + { + ++m_num_filtered; + --m_num_have_filtered; + } + } + + // if we have fast resume info + // use it + if (!unfinished.empty()) + { + for (std::vector::const_iterator i + = unfinished.begin(); i != unfinished.end(); ++i) + { + tcp::endpoint peer; + for (int j = 0; j < m_blocks_per_piece; ++j) + { + if (i->info[j].finished) + mark_as_finished(piece_block(i->index, j), peer); + } + if (is_piece_finished(i->index)) + { + // TODO: handle this case by verifying the + // piece and either accept it or discard it + assert(false); + } + } + } + } + + void piece_picker::set_sequenced_download_threshold( + int sequenced_download_threshold) + { + TORRENT_PIECE_PICKER_INVARIANT_CHECK; + + if (sequenced_download_threshold == m_sequenced_download_threshold) + return; + + assert(sequenced_download_threshold > 0); + + int old_limit = m_sequenced_download_threshold; + m_sequenced_download_threshold = sequenced_download_threshold; + + for (std::vector::iterator i = m_piece_map.begin() + , end(m_piece_map.end()); i != end; ++i) + { + if (i->priority(old_limit) != i->priority(m_sequenced_download_threshold)) + { + piece_pos& p = *i; + int prev_priority = p.priority(old_limit); + if (prev_priority == 0) continue; + move(prev_priority, p.index); + } + } + + typedef std::vector info_t; + + if (old_limit < sequenced_download_threshold) + { + // the threshold was incremented, in case + // the previous max availability was reached + // we need to shuffle that bucket, if not, we + // don't have to do anything + if (int(m_piece_info.size()) > old_limit) + { + info_t& in = m_piece_info[old_limit]; + std::random_shuffle(in.begin(), in.end()); + int c = 0; + for (info_t::iterator i = in.begin() + , end(in.end()); i != end; ++i) + { + m_piece_map[*i].index = c++; + assert(m_piece_map[*i].priority(old_limit) == old_limit); + } + } + } + else if (int(m_piece_info.size()) > sequenced_download_threshold) + { + info_t& in = m_piece_info[sequenced_download_threshold]; + std::sort(in.begin(), in.end()); + int c = 0; + for (info_t::iterator i = in.begin() + , end(in.end()); i != end; ++i) + { + m_piece_map[*i].index = c++; + assert(m_piece_map[*i].priority( + sequenced_download_threshold) == sequenced_download_threshold); + } + } + } + + piece_picker::downloading_piece& piece_picker::add_download_piece() + { + int num_downloads = m_downloads.size(); + int block_index = num_downloads * m_blocks_per_piece; + if (int(m_block_info.size()) < block_index + m_blocks_per_piece) + { + m_block_info.resize(block_index + m_blocks_per_piece); + if (!m_downloads.empty() && &m_block_info[0] != m_downloads.front().info) + { + // this means the memory was reallocated, update the pointers + for (int i = 0; i < int(m_downloads.size()); ++i) + m_downloads[i].info = &m_block_info[i * m_blocks_per_piece]; + } + } + m_downloads.push_back(downloading_piece()); + downloading_piece& ret = m_downloads.back(); + ret.info = &m_block_info[block_index]; + for (int i = 0; i < m_blocks_per_piece; ++i) + { + ret.info[i].num_downloads = 0; + ret.info[i].requested = 0; + ret.info[i].finished = 0; + ret.info[i].peer = tcp::endpoint(); + } + return ret; + } + + void piece_picker::erase_download_piece(std::vector::iterator i) + { + if (i != m_downloads.end() - 1) + { + int remove_index = i - m_downloads.begin(); + int last_index = m_downloads.size() - 1; + assert(remove_index < last_index); + + assert(int(m_block_info.size()) >= last_index * m_blocks_per_piece + m_blocks_per_piece); + std::copy(m_block_info.begin() + (last_index * m_blocks_per_piece) + , m_block_info.begin() + (last_index * m_blocks_per_piece + m_blocks_per_piece) + , m_block_info.begin() + (remove_index * m_blocks_per_piece)); + m_downloads[remove_index] = m_downloads[last_index]; + m_downloads[remove_index].info = &m_block_info[remove_index * m_blocks_per_piece]; + } + + m_downloads.pop_back(); + } +#ifndef NDEBUG + + void piece_picker::check_invariant(const torrent* t) const + { + assert(sizeof(piece_pos) == 4); + + assert(m_piece_info.empty() || m_piece_info[0].empty()); + + if (t != 0) + assert((int)m_piece_map.size() == t->torrent_file().num_pieces()); + + for (int i = m_sequenced_download_threshold * 2 + 1; i < int(m_piece_info.size()); ++i) + assert(m_piece_info[i].empty()); + + for (std::vector::const_iterator i = m_downloads.begin() + , end(m_downloads.end()); i != end; ++i) + { + bool blocks_requested = false; + int num_blocks = blocks_in_piece(i->index); + int num_requested = 0; + int num_finished = 0; + for (int k = 0; k < num_blocks; ++k) + { + if (i->info[k].finished) + { + ++num_finished; + assert(i->info[k].requested); + ++num_requested; + continue; + } + if (i->info[k].requested) + { + ++num_requested; + blocks_requested = true; + } + } + assert(blocks_requested == (i->state != none)); + assert(num_requested == i->requested); + assert(num_finished == i->finished); + assert(num_finished <= num_requested); + } + + + 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()); + if (i->filtered()) + { + if (i->index != piece_pos::we_have_index) + ++num_filtered; + else + ++num_have_filtered; + } + if (i->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++; + } + + 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) + { + assert(num_downloaders == 1); + } + else + { + assert(num_downloaders == 0); + } +*/ + } +#endif + + if (i->index == piece_pos::we_have_index) + { + assert(t == 0 || t->have_piece(index)); + assert(i->downloading == 0); +/* + // make sure there's no entry + // with this index. (there shouldn't + // be since the piece_map is piece_pos::we_have_index) + for (int i = 0; i < int(m_piece_info.size()); ++i) + { + for (int j = 0; j < int(m_piece_info[i].size()); ++j) + { + assert(m_piece_info[i][j] != index); + } + } +*/ + } + else + { + if (t != 0) + assert(!t->have_piece(index)); + + int prio = i->priority(m_sequenced_download_threshold); + if (prio > 0) + { + const std::vector& vec = m_piece_info[prio]; + assert (i->index < vec.size()); + assert(vec[i->index] == index); + } +/* + for (int k = 0; k < int(m_piece_info.size()); ++k) + { + for (int j = 0; j < int(m_piece_info[k].size()); ++j) + { + assert(int(m_piece_info[k][j]) != index + || (prio > 0 && prio == k && int(i->index) == j)); + } + } +*/ + } + + int count = std::count_if(m_downloads.begin(), m_downloads.end() + , has_index(index)); + if (i->downloading == 1) + { + assert(count == 1); + } + else + { + assert(count == 0); + } + } + assert(num_have == m_num_have); + assert(num_filtered == m_num_filtered); + assert(num_have_filtered == m_num_have_filtered); + } +#endif + + float piece_picker::distributed_copies() const + { + const float num_pieces = static_cast(m_piece_map.size()); + + 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 = i->peer_count; + fraction_part += integer_part; + integer_part = 1; + } + else if (peer_count == min_availability) + { + ++integer_part; + } + else + { + assert(peer_count > min_availability); + ++fraction_part; + } + } + assert(integer_part + fraction_part == num_pieces); + return float(min_availability) + (fraction_part / num_pieces); + } + + void piece_picker::add(int index) + { + assert(index >= 0); + assert(index < int(m_piece_map.size())); + piece_pos& p = m_piece_map[index]; + assert(!p.filtered()); + assert(!p.have()); + + int priority = p.priority(m_sequenced_download_threshold); + assert(priority > 0); + if (int(m_piece_info.size()) <= priority) + m_piece_info.resize(priority + 1); + + assert(int(m_piece_info.size()) > priority); + + if (is_ordered(priority)) + { + // the piece should be inserted ordered, not randomly + std::vector& v = m_piece_info[priority]; +// assert(is_sorted(v.begin(), v.end()/*, std::greater()*/)); + std::vector::iterator i = std::lower_bound(v.begin(), v.end() + , index/*, std::greater()*/); + p.index = i - v.begin(); + v.insert(i, index); + i = v.begin() + p.index + 1; + for (;i != v.end(); ++i) + { + ++m_piece_map[*i].index; + assert(v[m_piece_map[*i].index] == *i); + } +// assert(is_sorted(v.begin(), v.end()/*, std::greater()*/)); + } + else if (m_piece_info[priority].size() < 2) + { + p.index = m_piece_info[priority].size(); + m_piece_info[priority].push_back(index); + } + else + { + // find a random position in the destination vector where we will place + // this entry. + int dst_index = rand() % m_piece_info[priority].size(); + + // copy the entry at that position to the back + m_piece_map[m_piece_info[priority][dst_index]].index + = m_piece_info[priority].size(); + m_piece_info[priority].push_back(m_piece_info[priority][dst_index]); + + // and then replace the one at dst_index with the one we're moving. + // this procedure is to make sure there's no ordering when pieces + // are moved in sequenced order. + p.index = dst_index; + m_piece_info[priority][p.index] = index; + } + } + + // will update the piece with the given properties (priority, elem_index) + // to place it at the correct position in the vectors. + void piece_picker::move(int priority, int elem_index) + { + assert(priority > 0); + assert(elem_index >= 0); + assert(m_files_checked_called); + + assert(int(m_piece_info.size()) > priority); + assert(int(m_piece_info[priority].size()) > elem_index); + + int index = m_piece_info[priority][elem_index]; + // update the piece_map + piece_pos& p = m_piece_map[index]; + assert(int(p.index) == elem_index || p.have()); + + int new_priority = p.priority(m_sequenced_download_threshold); + + if (new_priority == priority) return; + + if (int(m_piece_info.size()) <= new_priority + && new_priority > 0) + { + m_piece_info.resize(new_priority + 1); + assert(int(m_piece_info.size()) > new_priority); + } + + if (new_priority == 0) + { + // this means the piece should not have an entry + } + else if (is_ordered(new_priority)) + { + // the piece should be inserted ordered, not randomly + std::vector& v = m_piece_info[new_priority]; +// assert(is_sorted(v.begin(), v.end()/*, std::greater()*/)); + std::vector::iterator i = std::lower_bound(v.begin(), v.end() + , index/*, std::greater()*/); + p.index = i - v.begin(); + v.insert(i, index); + i = v.begin() + p.index + 1; + for (;i != v.end(); ++i) + { + ++m_piece_map[*i].index; + assert(v[m_piece_map[*i].index] == *i); + } +// assert(is_sorted(v.begin(), v.end()/*, std::greater()*/)); + } + else if (m_piece_info[new_priority].size() < 2) + { + p.index = m_piece_info[new_priority].size(); + m_piece_info[new_priority].push_back(index); + } + else + { + // find a random position in the destination vector where we will place + // this entry. + int dst_index = rand() % m_piece_info[new_priority].size(); + + // copy the entry at that position to the back + m_piece_map[m_piece_info[new_priority][dst_index]].index + = m_piece_info[new_priority].size(); + m_piece_info[new_priority].push_back(m_piece_info[new_priority][dst_index]); + + // and then replace the one at dst_index with the one we're moving. + // this procedure is to make sure there's no ordering when pieces + // are moved in sequenced order. + p.index = dst_index; + m_piece_info[new_priority][p.index] = index; + } + assert(new_priority == 0 || p.index < m_piece_info[p.priority(m_sequenced_download_threshold)].size()); + assert(new_priority == 0 || m_piece_info[p.priority(m_sequenced_download_threshold)][p.index] == index); + + if (is_ordered(priority)) + { + // remove the element from the source vector and preserve the order + std::vector& v = m_piece_info[priority]; + v.erase(v.begin() + elem_index); + for (std::vector::iterator i = v.begin() + elem_index; + i != v.end(); ++i) + { + --m_piece_map[*i].index; + assert(v[m_piece_map[*i].index] == *i); + } + } + else + { + // this will remove elem from the source vector without + // preserving order, but the order is random anyway + int replace_index = m_piece_info[priority][elem_index] = m_piece_info[priority].back(); + if (index != replace_index) + { + // update the entry we moved from the back + m_piece_map[replace_index].index = elem_index; + + assert(int(m_piece_info[priority].size()) > elem_index); + // this may not necessarily be the case. If we've just updated the threshold and are updating + // the piece map +// assert((int)m_piece_map[replace_index].priority(m_sequenced_download_threshold) == priority); + assert(int(m_piece_map[replace_index].index) == elem_index); + assert(m_piece_info[priority][elem_index] == replace_index); + } + else + { + assert(int(m_piece_info[priority].size()) == elem_index+1); + } + + m_piece_info[priority].pop_back(); + } + } + + void piece_picker::restore_piece(int index) + { + TORRENT_PIECE_PICKER_INVARIANT_CHECK; + + assert(index >= 0); + assert(index < (int)m_piece_map.size()); + assert(m_files_checked_called); + + assert(m_piece_map[index].downloading == 1); + + std::vector::iterator i + = std::find_if(m_downloads.begin(), + m_downloads.end(), + has_index(index)); + assert(i != m_downloads.end()); + erase_download_piece(i); + + piece_pos& p = m_piece_map[index]; + int prev_priority = p.priority(m_sequenced_download_threshold); + p.downloading = 0; + int new_priority = p.priority(m_sequenced_download_threshold); + + if (new_priority == prev_priority) return; + + if (prev_priority == 0) + { + add(index); + } + else + { + move(prev_priority, p.index); + } + } + + void piece_picker::inc_refcount_all() + { + TORRENT_PIECE_PICKER_INVARIANT_CHECK; + assert(m_files_checked_called); + + // in general priority = availability * 2 + // see piece_block::priority() + + // this will insert two empty vectors at the start of the + // piece_info vector. It is done like this as an optimization, + // to swap vectors instead of copying them + while (m_piece_info.size() < 3 + || (!m_piece_info.rbegin()->empty()) + || (!(m_piece_info.rbegin()+1)->empty())) + { + m_piece_info.push_back(std::vector()); + } + assert(m_piece_info.rbegin()->empty()); + assert((m_piece_info.rbegin()+1)->empty()); + typedef std::vector > piece_info_t; + for (piece_info_t::reverse_iterator i = m_piece_info.rbegin(), j(i+1) + , k(j+1), end(m_piece_info.rend()); k != end; ++i, ++j, ++k) + { + k->swap(*i); + } + assert(m_piece_info.begin()->empty()); + assert((m_piece_info.begin()+1)->empty()); + + // if we have some priorities that are clamped to the + // sequenced download, move that vector back down + int last_index = m_piece_info.size() - 1; + int cap_index = m_sequenced_download_threshold * 2; + if (last_index == cap_index) + { + // this is the case when the top bucket + // was moved up into the sequenced download bucket. + m_piece_info.push_back(std::vector()); + m_piece_info[cap_index].swap(m_piece_info[cap_index+1]); + ++last_index; + } + else if (last_index > cap_index) + { + if (last_index - cap_index == 1) + { + m_piece_info.push_back(std::vector()); + ++last_index; + } + m_piece_info[cap_index+1].swap(m_piece_info[cap_index+2]); + m_piece_info[cap_index].swap(m_piece_info[cap_index+1]); + } + + // now, increase the peer count of all the pieces. + // because of different priorities, some pieces may have + // ended up in the wrong priority bucket. Adjust that. + for (std::vector::iterator i = m_piece_map.begin() + , end(m_piece_map.end()); i != end; ++i) + { + int prev_prio = i->priority(m_sequenced_download_threshold); + ++i->peer_count; + // if the assumption that the priority would + // increase by 2 when increasing the availability + // by one isn't true for this particular piece, correct it. + // that assumption is true for all pieces with priority 0 or 1 + int new_prio = i->priority(m_sequenced_download_threshold); + assert(new_prio <= cap_index); + if (prev_prio == 0 && new_prio > 0) + { + add(i - m_piece_map.begin()); + continue; + } + if (new_prio == 0) + { + assert(prev_prio == 0); + continue; + } + if (prev_prio == cap_index) + { + assert(new_prio == cap_index); + continue; + } + if (new_prio == prev_prio + 2 && new_prio != cap_index) + { + assert(new_prio != cap_index); + continue; + } + if (prev_prio + 2 >= cap_index) + { + // these two vectors will have moved one extra step + // passed the sequenced download threshold + ++prev_prio; + } + assert(prev_prio + 2 != cap_index); + assert(prev_prio + 2 != new_prio); + move(prev_prio + 2, i->index); + } + } + + void piece_picker::dec_refcount_all() + { + TORRENT_PIECE_PICKER_INVARIANT_CHECK; + assert(m_files_checked_called); + assert(m_piece_info.size() >= 2); + assert(m_piece_info.front().empty()); + // swap all vectors two steps down + if (m_piece_info.size() > 2) + { + typedef std::vector > piece_info_t; + for (piece_info_t::iterator i = m_piece_info.begin(), j(i+1) + , k(j+1), end(m_piece_info.end()); k != end; ++i, ++j, ++k) + { + k->swap(*i); + } + } + else + { + m_piece_info.resize(3); + } + int last_index = m_piece_info.size() - 1; + if ((m_piece_info.size() & 1) == 0) + { + // if there's an even number of vectors, swap + // the last two to get the same layout in both cases + m_piece_info[last_index].swap(m_piece_info[last_index-1]); + } + assert(m_piece_info.back().empty()); + int pushed_out_index = m_piece_info.size() - 2; + + int cap_index = m_sequenced_download_threshold * 2; + assert(m_piece_info[last_index].empty()); + if (last_index >= cap_index) + { + assert(pushed_out_index == cap_index - 1 + || m_piece_info[cap_index - 1].empty()); + m_piece_info[cap_index].swap(m_piece_info[cap_index - 2]); + if (cap_index == pushed_out_index) + pushed_out_index = cap_index - 2; + } + + // first is the vector that were + // bumped down to 0. The should always be moved + // since they have to be removed or reinserted + std::vector().swap(m_piece_info.front()); + + for (std::vector::iterator i = m_piece_map.begin() + , end(m_piece_map.end()); i != end; ++i) + { + int prev_prio = i->priority(m_sequenced_download_threshold); + assert(i->peer_count > 0); + --i->peer_count; + // if the assumption that the priority would + // decrease by 2 when decreasing the availability + // by one isn't true for this particular piece, correct it. + // that assumption is true for all pieces with priority 0 or 1 + if (prev_prio == 0) + { + assert(i->priority(m_sequenced_download_threshold) == 0); + continue; + } + + int new_prio = i->priority(m_sequenced_download_threshold); + if (prev_prio == cap_index) + { + if (new_prio == cap_index) continue; + prev_prio += 2; + } + else if (new_prio == prev_prio - 2) + { + continue; + } + else if (prev_prio == 2) + { + // if this piece was pushed down to priority 0, it was + // removed + assert(new_prio > 0); + add(i - m_piece_map.begin()); + continue; + } + else if (prev_prio == 1) + { + // if this piece was one of the vectors that was pushed to the + // top, adjust the prev_prio to point to that vector, so that + // the pieces are moved from there + prev_prio = pushed_out_index + 2; + } + move(prev_prio - 2, i->index); + } + } + + void piece_picker::inc_refcount(int i) + { +// TORRENT_PIECE_PICKER_INVARIANT_CHECK; + assert(i >= 0); + assert(i < (int)m_piece_map.size()); + assert(m_files_checked_called); + + piece_pos& p = m_piece_map[i]; + int index = p.index; + int prev_priority = p.priority(m_sequenced_download_threshold); + + assert(p.peer_count < piece_pos::max_peer_count); + p.peer_count++; + assert(p.peer_count != 0); + + // if we have the piece or if it's filtered + // we don't have to move any entries in the piece_info vector + if (p.priority(m_sequenced_download_threshold) == prev_priority) return; + + if (prev_priority == 0) + { + add(i); + } + else + { + move(prev_priority, index); + } + +#ifndef NDEBUG +// integrity_check(); +#endif + return; + } + + void piece_picker::dec_refcount(int i) + { +// TORRENT_PIECE_PICKER_INVARIANT_CHECK; + + assert(m_files_checked_called); + assert(i >= 0); + assert(i < (int)m_piece_map.size()); + + piece_pos& p = m_piece_map[i]; + int prev_priority = p.priority(m_sequenced_download_threshold); + int index = p.index; + assert(p.peer_count > 0); + + if (p.peer_count > 0) + p.peer_count--; + + if (p.priority(m_sequenced_download_threshold) == prev_priority) return; + + move(prev_priority, index); + } + + // this is used to indicate that we succesfully have + // downloaded a piece, and that no further attempts + // to pick that piece should be made. The piece will + // be removed from the available piece list. + void piece_picker::we_have(int index) + { + TORRENT_PIECE_PICKER_INVARIANT_CHECK; + assert(index >= 0); + assert(index < (int)m_piece_map.size()); + + piece_pos& p = m_piece_map[index]; + int info_index = p.index; + int priority = p.priority(m_sequenced_download_threshold); + + assert(p.downloading == 1); + assert(!p.have()); + + std::vector::iterator i + = std::find_if(m_downloads.begin() + , m_downloads.end() + , has_index(index)); + assert(i != m_downloads.end()); + erase_download_piece(i); + p.downloading = 0; + + assert(std::find_if(m_downloads.begin(), m_downloads.end() + , has_index(index)) == m_downloads.end()); + + if (p.have()) return; + if (p.filtered()) + { + --m_num_filtered; + ++m_num_have_filtered; + } + ++m_num_have; + p.set_have(); + if (priority == 0) return; + assert(p.priority(m_sequenced_download_threshold) == 0); + move(priority, info_index); + } + + + void piece_picker::set_piece_priority(int index, int new_piece_priority) + { + TORRENT_PIECE_PICKER_INVARIANT_CHECK; + assert(new_piece_priority >= 0); + assert(new_piece_priority <= 7); + assert(index >= 0); + 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; + + int prev_priority = p.priority(m_sequenced_download_threshold); + + 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; + } + 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; + } + assert(m_num_filtered >= 0); + assert(m_num_have_filtered >= 0); + + p.piece_priority = new_piece_priority; + int new_priority = p.priority(m_sequenced_download_threshold); + + if (new_priority == prev_priority) return; + + if (prev_priority == 0) + { + add(index); + } + else + { + move(prev_priority, p.index); + } + } + + int piece_picker::piece_priority(int index) const + { + assert(index >= 0); + 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 ============== + + // 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_whole_pieces can be set if this peer should download + // whole pieces rather than trying to download blocks from the + // same piece as other peers. + // the endpoint is the address 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. + // state is supposed to be set to fast if the peer is downloading + // relatively fast, by some notion. Slow peers will prefer not + // to pick blocks from the same pieces as fast peers, and vice + // versa. Downloading pieces are marked as being fast, medium + // or slow once they're started. + void piece_picker::pick_pieces(const std::vector& pieces + , std::vector& interesting_blocks + , int num_blocks, bool prefer_whole_pieces + , tcp::endpoint peer, piece_state_t speed) const + { + TORRENT_PIECE_PICKER_INVARIANT_CHECK; + assert(num_blocks > 0); + assert(pieces.size() == m_piece_map.size()); + assert(m_files_checked_called); + + assert(m_piece_info.begin() != m_piece_info.end()); + + // this will be filled with blocks that we should not request + // unless we can't find num_blocks among the other ones. + // blocks that belong to pieces with a mismatching speed + // category for instance, or if we prefer whole pieces, + // blocks belonging to a piece that others have + // downloaded to + std::vector backup_blocks; + + // this loop will loop from pieces with priority 1 and up + // until we either reach the end of the piece list or + // has filled the interesting_blocks with num_blocks + // blocks. + + // When prefer_whole_pieces is set (usually set when downloading from + // fast peers) the partial pieces will not be prioritized, but actually + // ignored as long as possible. + + // +1 is to ignore pieces that no peer has. The bucket with index 0 contains + // pieces that 0 other peers have. bucket will point to a bucket with + // pieces with the same priority. It will be iterated in priority + // order (high priority/rare pices first). The content of each + // bucket is randomized + for (std::vector >::const_iterator bucket + = m_piece_info.begin() + 1; bucket != m_piece_info.end(); + ++bucket) + { + if (bucket->empty()) continue; + num_blocks = add_interesting_blocks(*bucket, pieces + , interesting_blocks, backup_blocks, num_blocks + , prefer_whole_pieces, peer, speed); + assert(num_blocks >= 0); + if (num_blocks == 0) return; + } + + assert(num_blocks > 0); + + if (!backup_blocks.empty()) + interesting_blocks.insert(interesting_blocks.end() + , backup_blocks.begin(), backup_blocks.begin() + + (std::min)(num_blocks, (int)backup_blocks.size())); + } + + namespace + { + bool exclusively_requested_from(piece_picker::downloading_piece const& p + , int num_blocks_in_piece, tcp::endpoint peer) + { + for (int j = 0; j < num_blocks_in_piece; ++j) + { + piece_picker::block_info const& info = p.info[j]; + if ((info.finished == 1 + || info.requested == 1) + && info.peer != peer + && info.peer != tcp::endpoint()) + { + return false; + } + } + return true; + } + } + + int piece_picker::add_interesting_blocks(std::vector const& piece_list + , std::vector const& pieces + , std::vector& interesting_blocks + , std::vector& backup_blocks + , int num_blocks, bool prefer_whole_pieces + , tcp::endpoint peer, piece_state_t speed) const + { + for (std::vector::const_iterator i = piece_list.begin(); + i != piece_list.end(); ++i) + { + assert(*i >= 0); + assert(*i < (int)m_piece_map.size()); + + // if the peer doesn't have the piece + // skip it + if (!pieces[*i]) continue; + + // if we have less than 1% of the pieces, ignore speed priorities and just try + // to finish any downloading piece + bool ignore_speed_categories = (m_num_have * 100 / m_piece_map.size()) < 1; + + int num_blocks_in_piece = blocks_in_piece(*i); + + if (m_piece_map[*i].downloading == 1) + { + std::vector::const_iterator p + = std::find_if(m_downloads.begin(), m_downloads.end(), has_index(*i)); + assert(p != m_downloads.end()); + + // is true if all the other pieces that are currently + // requested from this piece are from the same + // peer as 'peer'. + bool only_same_peer = exclusively_requested_from(*p + , num_blocks_in_piece, peer); + + // this means that this partial piece has + // been downloaded/requested partially from + // another peer that isn't us. And since + // we prefer whole pieces, add this piece's + // blocks to the backup list. If the prioritized + // blocks aren't enough, blocks from this list + // will be picked. + if (prefer_whole_pieces && !only_same_peer) + { + if (int(backup_blocks.size()) >= num_blocks) continue; + for (int j = 0; j < num_blocks_in_piece; ++j) + { + block_info const& info = p->info[j]; + if (info.finished) continue; + if (info.requested + && info.peer == peer) continue; + backup_blocks.push_back(piece_block(*i, j)); + } + continue; + } + + for (int j = 0; j < num_blocks_in_piece; ++j) + { + // ignore completed blocks + block_info const& info = p->info[j]; + if (info.finished) continue; + // ignore blocks requested from this peer already + if (info.requested && info.peer == peer) continue; + // if the piece is fast and the peer is slow, or vice versa, + // add the block as a backup. + // override this behavior if all the other blocks + // have been requested from the same peer or + // if the state of the piece is none (the + // piece will in that case change state). + if (p->state != none && p->state != speed + && !only_same_peer + && !ignore_speed_categories) + { + if (int(backup_blocks.size()) >= num_blocks) continue; + backup_blocks.push_back(piece_block(*i, j)); + continue; + } + // this block is interesting (we don't have it + // yet). But it may already have been requested + // from another peer. We have to add it anyway + // to allow the requester to determine if the + // block should be requested from more than one + // peer. If it is being downloaded, we continue + // to look for blocks until we have num_blocks + // blocks that have not been requested from any + // other peer. + interesting_blocks.push_back(piece_block(*i, j)); + if (p->info[j].requested == 0) + { + // we have found a block that's free to download + num_blocks--; + // if we prefer whole pieces, continue picking from this + // piece even though we have num_blocks + if (prefer_whole_pieces) continue; + assert(num_blocks >= 0); + if (num_blocks == 0) return num_blocks; + } + } + assert(num_blocks >= 0 || prefer_whole_pieces); + if (num_blocks < 0) num_blocks = 0; + } + else + { + if (!prefer_whole_pieces && num_blocks_in_piece > num_blocks) + num_blocks_in_piece = num_blocks; + for (int j = 0; j < num_blocks_in_piece; ++j) + { + interesting_blocks.push_back(piece_block(*i, j)); + } + num_blocks -= (std::min)(num_blocks_in_piece, num_blocks); + } + assert(num_blocks >= 0); + if (num_blocks == 0) return num_blocks; + } + return num_blocks; + } + + bool piece_picker::is_piece_finished(int index) const + { + assert(index < (int)m_piece_map.size()); + assert(index >= 0); + + if (m_piece_map[index].downloading == 0) + { + assert(std::find_if(m_downloads.begin(), m_downloads.end() + , has_index(index)) == m_downloads.end()); + return false; + } + std::vector::const_iterator i + = std::find_if(m_downloads.begin(), m_downloads.end(), has_index(index)); + assert(i != m_downloads.end()); + assert((int)i->finished <= m_blocks_per_piece); + int max_blocks = blocks_in_piece(index); + if ((int)i->finished < max_blocks) return false; + + assert((int)i->requested == max_blocks); + return true; + } + + bool piece_picker::is_downloading(piece_block block) const + { + assert(block.piece_index >= 0); + assert(block.block_index >= 0); + assert(block.piece_index < (int)m_piece_map.size()); + + if (m_piece_map[block.piece_index].downloading == 0) return false; + std::vector::const_iterator i + = std::find_if( + m_downloads.begin() + , m_downloads.end() + , has_index(block.piece_index)); + + assert(i != m_downloads.end()); + return i->info[block.block_index].requested; + } + + bool piece_picker::is_finished(piece_block block) const + { + assert(block.piece_index >= 0); + assert(block.block_index >= 0); + assert(block.piece_index < (int)m_piece_map.size()); + + if (m_piece_map[block.piece_index].index == piece_pos::we_have_index) return true; + if (m_piece_map[block.piece_index].downloading == 0) return false; + std::vector::const_iterator i + = std::find_if(m_downloads.begin(), m_downloads.end(), has_index(block.piece_index)); + assert(i != m_downloads.end()); + return i->info[block.block_index].finished; + } + + + void piece_picker::mark_as_downloading(piece_block block + , const tcp::endpoint& peer, piece_state_t state) + { + TORRENT_PIECE_PICKER_INVARIANT_CHECK; + + assert(block.piece_index >= 0); + assert(block.block_index >= 0); + assert(block.piece_index < (int)m_piece_map.size()); + assert(block.block_index < blocks_in_piece(block.piece_index)); + + piece_pos& p = m_piece_map[block.piece_index]; + if (p.downloading == 0) + { + int prio = p.priority(m_sequenced_download_threshold); + p.downloading = 1; + move(prio, p.index); + + downloading_piece& dp = add_download_piece(); + dp.state = state; + dp.index = block.piece_index; + block_info& info = dp.info[block.block_index]; + info.requested = 1; + info.peer = peer; + ++dp.requested; + } + else + { + std::vector::iterator i + = std::find_if(m_downloads.begin(), m_downloads.end(), has_index(block.piece_index)); + assert(i != m_downloads.end()); + block_info& info = i->info[block.block_index]; + assert(info.requested == 0); + info.peer = peer; + info.requested = 1; + ++i->requested; + if (i->state == none) i->state = state; + } + } + + void piece_picker::mark_as_finished(piece_block block, const tcp::endpoint& peer) + { + TORRENT_PIECE_PICKER_INVARIANT_CHECK; + + assert(block.piece_index >= 0); + assert(block.block_index >= 0); + assert(block.piece_index < (int)m_piece_map.size()); + assert(block.block_index < blocks_in_piece(block.piece_index)); + + piece_pos& p = m_piece_map[block.piece_index]; + int prio = p.priority(m_sequenced_download_threshold); + + if (p.downloading == 0) + { + p.downloading = 1; + if (prio > 0) move(prio, p.index); + else assert(p.priority(m_sequenced_download_threshold) == 0); + + downloading_piece& dp = add_download_piece(); + dp.state = none; + dp.index = block.piece_index; + block_info& info = dp.info[block.block_index]; + info.requested = 1; + info.finished = 1; + ++dp.requested; + ++dp.finished; + dp.info[block.block_index].peer = peer; + } + else + { + std::vector::iterator i + = std::find_if(m_downloads.begin(), m_downloads.end(), has_index(block.piece_index)); + assert(i != m_downloads.end()); + block_info& info = i->info[block.block_index]; + info.peer = peer; + if (!info.requested) ++i->requested; + info.requested = 1; + if (!info.finished) ++i->finished; + info.finished = 1; + + if (i->requested == i->finished) + { + // there are no blocks requested in this piece. + // remove the fast/slow state from it + i->state = none; + } + } + } + + void piece_picker::get_downloaders(std::vector& d, int index) const + { + assert(index >= 0 && index <= (int)m_piece_map.size()); + std::vector::const_iterator i + = std::find_if(m_downloads.begin(), m_downloads.end(), has_index(index)); + assert(i != m_downloads.end()); + + d.clear(); + for (int j = 0; j < blocks_in_piece(index); ++j) + { + d.push_back(i->info[j].peer); + } + } + + boost::optional piece_picker::get_downloader(piece_block block) const + { + std::vector::const_iterator i = std::find_if( + m_downloads.begin() + , m_downloads.end() + , has_index(block.piece_index)); + + if (i == m_downloads.end()) + return boost::optional(); + + assert(block.block_index >= 0); + + if (i->info[block.block_index].requested == false + || i->info[block.block_index].requested == true) + return boost::optional(); + + return boost::optional(i->info[block.block_index].peer); + } + + void piece_picker::abort_download(piece_block block) + { + TORRENT_PIECE_PICKER_INVARIANT_CHECK; + + assert(block.piece_index >= 0); + assert(block.block_index >= 0); + assert(block.piece_index < (int)m_piece_map.size()); + assert(block.block_index < blocks_in_piece(block.piece_index)); + + if (m_piece_map[block.piece_index].downloading == 0) + { + assert(std::find_if(m_downloads.begin(), m_downloads.end() + , has_index(block.piece_index)) == m_downloads.end()); + return; + } + + std::vector::iterator i = std::find_if(m_downloads.begin() + , m_downloads.end(), has_index(block.piece_index)); + assert(i != m_downloads.end()); + + if (i->info[block.block_index].finished) + { + assert(i->info[block.block_index].requested); + return; + } + + assert(block.block_index < blocks_in_piece(block.piece_index)); + assert(i->info[block.block_index].requested); + + // clear this block as being downloaded + i->info[block.block_index].requested = false; + --i->requested; + + // clear the downloader of this block + i->info[block.block_index].peer = tcp::endpoint(); + + // if there are no other blocks in this piece + // that's being downloaded, remove it from the list + if (i->requested == 0) + { + erase_download_piece(i); + piece_pos& p = m_piece_map[block.piece_index]; + int prio = p.priority(m_sequenced_download_threshold); + p.downloading = 0; + if (prio > 0) move(prio, p.index); + + assert(std::find_if(m_downloads.begin(), m_downloads.end() + , has_index(block.piece_index)) == m_downloads.end()); + } + else if (i->requested == i->finished) + { + // there are no blocks requested in this piece. + // remove the fast/slow state from it + i->state = none; + } + } + + int piece_picker::unverified_blocks() const + { + int counter = 0; + for (std::vector::const_iterator i = m_downloads.begin(); + i != m_downloads.end(); ++i) + { + counter += (int)i->finished; + } + return counter; + } + +} + diff --git a/encryption/libtorrent/src/policy.cpp b/encryption/libtorrent/src/policy.cpp new file mode 100755 index 000000000..1cb1ed65d --- /dev/null +++ b/encryption/libtorrent/src/policy.cpp @@ -0,0 +1,1444 @@ +/* + +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 "libtorrent/pch.hpp" + +#include + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/peer_connection.hpp" +#include "libtorrent/web_peer_connection.hpp" +#include "libtorrent/policy.hpp" +#include "libtorrent/torrent.hpp" +#include "libtorrent/socket.hpp" +#include "libtorrent/alert_types.hpp" +#include "libtorrent/invariant_check.hpp" +#include "libtorrent/time.hpp" +#include "libtorrent/aux_/session_impl.hpp" + +namespace libtorrent +{ + class peer_connection; +} + +using boost::bind; + +namespace +{ + using namespace libtorrent; + + size_type collect_free_download( + torrent::peer_iterator start + , torrent::peer_iterator end) + { + size_type accumulator = 0; + for (torrent::peer_iterator i = start; i != end; ++i) + { + // if the peer is interested in us, it means it may + // want to trade it's surplus uploads for downloads itself + // (and we should not consider it free). If the share diff is + // negative, there's no free download to get from this peer. + size_type diff = i->second->share_diff(); + assert(diff < std::numeric_limits::max()); + if (i->second->is_peer_interested() || diff <= 0) + continue; + + assert(diff > 0); + i->second->add_free_upload(-diff); + accumulator += diff; + assert(accumulator > 0); + } + assert(accumulator >= 0); + return accumulator; + } + + + // returns the amount of free upload left after + // it has been distributed to the peers + size_type distribute_free_upload( + torrent::peer_iterator start + , torrent::peer_iterator end + , size_type free_upload) + { + if (free_upload <= 0) return free_upload; + int num_peers = 0; + size_type total_diff = 0; + for (torrent::peer_iterator i = start; i != end; ++i) + { + size_type d = i->second->share_diff(); + assert(d < std::numeric_limits::max()); + total_diff += d; + if (!i->second->is_peer_interested() || i->second->share_diff() >= 0) continue; + ++num_peers; + } + + if (num_peers == 0) return free_upload; + size_type upload_share; + if (total_diff >= 0) + { + upload_share = std::min(free_upload, total_diff) / num_peers; + } + else + { + upload_share = (free_upload + total_diff) / num_peers; + } + if (upload_share < 0) return free_upload; + + for (torrent::peer_iterator i = start; i != end; ++i) + { + peer_connection* p = i->second; + if (!p->is_peer_interested() || p->share_diff() >= 0) continue; + p->add_free_upload(upload_share); + free_upload -= upload_share; + } + return free_upload; + } + + struct match_peer_ip + { + match_peer_ip(tcp::endpoint const& ip) + : m_ip(ip) + {} + + bool operator()(policy::peer const& p) const + { return p.ip.address() == m_ip.address(); } + + tcp::endpoint m_ip; + }; + + struct match_peer_connection + { + match_peer_connection(peer_connection const& c) + : m_conn(c) + {} + + bool operator()(policy::peer const& p) const + { return p.connection == &m_conn; } + + const peer_connection& m_conn; + }; + + +} + +namespace libtorrent +{ + // the case where ignore_peer is motivated is if two peers + // have only one piece that we don't have, and it's the + // same piece for both peers. Then they might get into an + // infinite loop, fighting to request the same blocks. + void request_a_block( + torrent& t + , peer_connection& c + , std::vector ignore) + { + assert(!t.is_seed()); + assert(!c.has_peer_choked()); + int num_requests = c.desired_queue_size() + - (int)c.download_queue().size() + - (int)c.request_queue().size(); + + 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; + + piece_picker& p = t.picker(); + std::vector interesting_pieces; + interesting_pieces.reserve(100); + + bool prefer_whole_pieces = c.prefer_whole_pieces() + || (c.peer_info_struct() && c.peer_info_struct()->on_parole); + + if (!prefer_whole_pieces) + { + prefer_whole_pieces = c.statistics().download_payload_rate() + * t.settings().whole_pieces_threshold + > t.torrent_file().piece_length(); + } + + // if we prefer whole pieces, the piece picker will pick at least + // the number of blocks we want, but it will try to make the picked + // blocks be from whole pieces, possibly by returning more blocks + // than we requested. + assert(c.remote() == c.get_socket()->remote_endpoint()); + + piece_picker::piece_state_t state; + peer_connection::peer_speed_t speed = c.peer_speed(); + if (speed == peer_connection::fast) state = piece_picker::fast; + else if (speed == peer_connection::medium) state = piece_picker::medium; + else state = piece_picker::slow; + + // 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. + p.pick_pieces(c.get_bitfield(), interesting_pieces + , num_requests, prefer_whole_pieces, c.remote(), state); + + // this vector is filled with the interesting pieces + // that some other peer is currently downloading + // we should then compare this peer's download speed + // with the other's, to see if we should abort another + // peer_connection in favour of this one + std::vector busy_pieces; + busy_pieces.reserve(10); + + for (std::vector::iterator i = interesting_pieces.begin(); + i != interesting_pieces.end(); ++i) + { + if (p.is_downloading(*i)) + { + busy_pieces.push_back(*i); + continue; + } + + // ok, we found a piece that's not being downloaded + // by somebody else. request it from this peer + // and return + c.add_request(*i); + num_requests--; + } + + c.send_block_requests(); + + // in this case, we could not find any blocks + // that was free. If we couldn't find any busy + // blocks as well, we cannot download anything + // more from this peer. + + if (busy_pieces.empty()) return; + + // first look for blocks that are just queued + // and not actually sent to us yet + // (then we can cancel those and request them + // from this peer instead) + + while (num_requests > 0) + { + peer_connection* peer = 0; + + const int initial_queue_size = (int)c.download_queue().size() + + (int)c.request_queue().size(); + + // This peer's weight will be the minimum, to prevent + // cancelling requests from a faster peer. + float min_weight = initial_queue_size == 0 + ? std::numeric_limits::max() + : c.statistics().download_payload_rate() / initial_queue_size; + + // find the peer with the lowest download + // speed that also has a piece that this + // peer could send us + for (torrent::peer_iterator i = t.begin(); + i != t.end(); ++i) + { + // don't try to take over blocks from ourself + if (i->second == &c) + continue; + + // ignore all peers in the ignore list + if (std::find(ignore.begin(), ignore.end(), i->second) != ignore.end()) + continue; + + const std::deque& download_queue = i->second->download_queue(); + const std::deque& request_queue = i->second->request_queue(); + const int queue_size = (int)i->second->download_queue().size() + + (int)i->second->request_queue().size(); + + bool in_request_queue = std::find_first_of( + busy_pieces.begin() + , busy_pieces.end() + , request_queue.begin() + , request_queue.end()) != busy_pieces.end(); + + bool in_download_queue = std::find_first_of( + busy_pieces.begin() + , busy_pieces.end() + , download_queue.begin() + , download_queue.end()) != busy_pieces.end(); + + // if the block is in the request queue rather than the download queue + // (i.e. the request message hasn't been sent yet) lower the weight in + // order to prioritize it. Taking over a block in the request queue is + // free in terms of redundant download. A block that already has been + // requested is likely to be in transit already, and would in that case + // mean redundant data to receive. + const float weight = (queue_size == 0) + ? std::numeric_limits::max() + : i->second->statistics().download_payload_rate() / queue_size + * in_request_queue ? .1f : 1.f; + + // if the peer's (i) weight is less than the lowest we've found so + // far (weight == priority) and it has blocks in its request- + // or download queue that we could request from this peer (c), + // replace the currently lowest ranking peer. + if (weight < min_weight && (in_request_queue || in_download_queue)) + { + peer = i->second; + min_weight = weight; + } + } + + if (peer == 0) + { + // we probably couldn't request the block because + // we are ignoring some peers + break; + } + + // find a suitable block to take over from this peer + + std::deque::const_reverse_iterator common_block = + std::find_first_of( + peer->request_queue().rbegin() + , peer->request_queue().rend() + , busy_pieces.begin() + , busy_pieces.end()); + + if (common_block == peer->request_queue().rend()) + { + common_block = std::find_first_of( + peer->download_queue().rbegin() + , peer->download_queue().rend() + , busy_pieces.begin() + , busy_pieces.end()); + assert(common_block != peer->download_queue().rend()); + } + + piece_block block = *common_block; + + // the one we interrupted may need to request a new piece. + // make sure it doesn't take over a block from the peer + // that just took over its block (that would cause an + // infinite recursion) + peer->cancel_request(block); + c.add_request(block); + ignore.push_back(&c); + if (!peer->has_peer_choked() && !t.is_seed()) + { + request_a_block(t, *peer, ignore); + peer->send_block_requests(); + } + + num_requests--; + + const int queue_size = (int)c.download_queue().size() + + (int)c.request_queue().size(); + const float weight = queue_size == 0 + ? std::numeric_limits::max() + : c.statistics().download_payload_rate() / queue_size; + + // this peer doesn't have a faster connection than the + // slowest peer. Don't take over any blocks + if (weight <= min_weight) break; + } + c.send_block_requests(); + } + + policy::policy(torrent* t) + : m_torrent(t) + , m_num_unchoked(0) + , m_available_free_upload(0) + , m_last_optimistic_disconnect(min_time()) + { assert(t); } + // finds the peer that has the worst download rate + // and returns it. May return 0 if all peers are + // choked. + policy::iterator policy::find_choke_candidate() + { + INVARIANT_CHECK; + + iterator worst_peer = m_peers.end(); + size_type min_weight = std::numeric_limits::min(); + +#ifndef NDEBUG + int unchoked_counter = m_num_unchoked; +#endif + + // TODO: make this selection better + + for (iterator i = m_peers.begin(); + i != m_peers.end(); ++i) + { + peer_connection* c = i->connection; + + if (c == 0) continue; + if (c->is_choked()) continue; +#ifndef NDEBUG + unchoked_counter--; +#endif + if (c->is_disconnecting()) continue; + // if the peer isn't interested, just choke it + if (!c->is_peer_interested()) + return i; + + size_type diff = i->total_download() + - i->total_upload(); + + size_type weight = static_cast(c->statistics().download_rate() * 10.f) + + diff + + ((c->is_interesting() && c->has_peer_choked())?-10:10)*1024; + + if (weight >= min_weight && worst_peer != m_peers.end()) continue; + + min_weight = weight; + worst_peer = i; + continue; + } + assert(unchoked_counter == 0); + return worst_peer; + } + + policy::iterator policy::find_unchoke_candidate() + { + INVARIANT_CHECK; + + // if all of our peers are unchoked, there's + // no left to unchoke + if (m_num_unchoked == m_torrent->num_peers()) + return m_peers.end(); + + iterator unchoke_peer = m_peers.end(); + ptime min_time = libtorrent::min_time(); + float max_down_speed = 0.f; + + // TODO: make this selection better + + for (iterator i = m_peers.begin(); + i != m_peers.end(); ++i) + { + peer_connection* c = i->connection; + if (c == 0) continue; + if (c->is_disconnecting()) continue; + if (!c->is_choked()) continue; + if (!c->is_peer_interested()) continue; + if (c->share_diff() < -free_upload_amount + && m_torrent->ratio() != 0) continue; + if (c->statistics().download_rate() < max_down_speed) continue; + + min_time = i->last_optimistically_unchoked; + max_down_speed = c->statistics().download_rate(); + unchoke_peer = i; + } + return unchoke_peer; + } + + policy::iterator policy::find_disconnect_candidate() + { + INVARIANT_CHECK; + + iterator disconnect_peer = m_peers.end(); + double slowest_transfer_rate = std::numeric_limits::max(); + + ptime now = time_now(); + + for (iterator i = m_peers.begin(); + i != m_peers.end(); ++i) + { + peer_connection* c = i->connection; + if (c == 0) continue; + if (c->is_disconnecting()) continue; + + // never disconnect an interesting peer if we have a candidate that + // isn't interesting + if (disconnect_peer != m_peers.end() + && c->is_interesting() + && !disconnect_peer->connection->is_interesting()) + continue; + + double transferred_amount + = (double)c->statistics().total_payload_download(); + + time_duration connected_time = now - i->connected; + + double connected_time_in_seconds = total_seconds(connected_time); + + double transfer_rate + = transferred_amount / (connected_time_in_seconds + 1); + + // prefer to disconnect uninteresting peers, and secondly slow peers + if (transfer_rate <= slowest_transfer_rate + || (disconnect_peer != m_peers.end() + && disconnect_peer->connection->is_interesting() + && !c->is_interesting())) + { + slowest_transfer_rate = transfer_rate; + disconnect_peer = i; + } + } + return disconnect_peer; + } + + policy::iterator policy::find_connect_candidate() + { + INVARIANT_CHECK; + + ptime now = time_now(); + ptime min_connect_time(now); + iterator candidate = m_peers.end(); + + int max_failcount = m_torrent->settings().max_failcount; + int min_reconnect_time = m_torrent->settings().min_reconnect_time; + + for (iterator i = m_peers.begin(); i != m_peers.end(); ++i) + { + if (i->connection) continue; + if (i->banned) continue; + if (i->type == peer::not_connectable) continue; + if (i->seed && m_torrent->is_seed()) continue; + if (i->failcount >= max_failcount) continue; + if (now - i->connected < seconds(i->failcount * min_reconnect_time)) + continue; + + assert(i->connected <= now); + + if (i->connected <= min_connect_time) + { + min_connect_time = i->connected; + candidate = i; + } + } + + assert(min_connect_time <= now); + + return candidate; + } + + policy::iterator policy::find_seed_choke_candidate() + { + INVARIANT_CHECK; + + assert(m_num_unchoked > 0); + // first choice candidate. + // it is a candidate we owe nothing to and which has been unchoked + // the longest. + iterator candidate = m_peers.end(); + + // not valid when candidate == 0 + ptime last_unchoke = min_time(); + + // second choice candidate. + // if there is no first choice candidate, this candidate will be chosen. + // it is the candidate that we owe the least to. + iterator second_candidate = m_peers.end(); + size_type lowest_share_diff = 0; // not valid when secondCandidate==0 + + for (iterator i = m_peers.begin(); + i != m_peers.end(); ++i) + { + peer_connection* c = i->connection; + // ignore peers that are choked or + // whose connection is closed + if (c == 0) continue; + + if (c->is_choked()) continue; + if (c->is_disconnecting()) continue; + + size_type share_diff = c->share_diff(); + + // select as second candidate the one that we owe the least + // to + if (second_candidate == m_peers.end() + || share_diff <= lowest_share_diff) + { + lowest_share_diff = share_diff; + second_candidate = i; + } + + // select as first candidate the one that we don't owe anything to + // and has been waiting for an unchoke the longest + if (share_diff > 0) continue; + if (candidate == m_peers.end() + || last_unchoke > i->last_optimistically_unchoked) + { + last_unchoke = i->last_optimistically_unchoked; + candidate = i; + } + } + if (candidate != m_peers.end()) return candidate; + assert(second_candidate != m_peers.end()); + return second_candidate; + } + + policy::iterator policy::find_seed_unchoke_candidate() + { + INVARIANT_CHECK; + + iterator candidate = m_peers.end(); + ptime last_unchoke = time_now(); + + for (iterator i = m_peers.begin(); + i != m_peers.end(); ++i) + { + peer_connection* c = i->connection; + if (c == 0) continue; + if (!c->is_choked()) continue; + if (!c->is_peer_interested()) continue; + if (c->is_disconnecting()) continue; + if (last_unchoke < i->last_optimistically_unchoked) continue; + last_unchoke = i->last_optimistically_unchoked; + candidate = i; + } + return candidate; + } + + bool policy::seed_unchoke_one_peer() + { + INVARIANT_CHECK; + + iterator p = find_seed_unchoke_candidate(); + if (p != m_peers.end()) + { + assert(p->connection->is_choked()); + p->connection->send_unchoke(); + p->last_optimistically_unchoked = time_now(); + ++m_num_unchoked; + } + return p != m_peers.end(); + } + + void policy::seed_choke_one_peer() + { + INVARIANT_CHECK; + + iterator p = find_seed_choke_candidate(); + if (p != m_peers.end()) + { + assert(!p->connection->is_choked()); + p->connection->send_choke(); + --m_num_unchoked; + } + } + + void policy::pulse() + { + INVARIANT_CHECK; + + if (m_torrent->is_paused()) return; + + ptime now = time_now(); + // remove old disconnected peers from the list + for (iterator i = m_peers.begin(); i != m_peers.end();) + { + // this timeout has to be customizable! + if (i->connection == 0 + && i->connected != min_time() + && now - i->connected > minutes(120)) + { + m_peers.erase(i++); + } + else + { + ++i; + } + } + + // ------------------------------------- + // maintain the number of connections + // ------------------------------------- + + // count the number of connected peers except for peers + // that are currently in the process of disconnecting + int num_connected_peers = 0; + + for (iterator i = m_peers.begin(); + i != m_peers.end(); ++i) + { + if (i->connection && !i->connection->is_disconnecting()) + ++num_connected_peers; + } + + if (m_torrent->m_connections_quota.given != std::numeric_limits::max()) + { + + int max_connections = m_torrent->m_connections_quota.given; + + if (num_connected_peers >= max_connections) + { + // every minute, disconnect the worst peer in hope of finding a better peer + + ptime local_time = time_now(); + if (m_last_optimistic_disconnect + seconds(120) <= local_time + && find_connect_candidate() != m_peers.end()) + { + m_last_optimistic_disconnect = local_time; + --max_connections; // this will have the effect of disconnecting the worst peer + } + } + else + { + // don't do a disconnect earlier than 1 minute after some peer was connected + m_last_optimistic_disconnect = time_now(); + } + + while (num_connected_peers > max_connections) + { + bool ret = disconnect_one_peer(); + (void)ret; + assert(ret); + --num_connected_peers; + } + } + + // ------------------------ + // upload shift + // ------------------------ + + // this part will shift downloads + // from peers that are seeds and peers + // that don't want to download from us + // to peers that cannot upload anything + // to us. The shifting will make sure + // that the torrent's share ratio + // will be maintained + + // if the share ratio is 0 (infinite) + // m_available_free_upload isn't used + // because it isn't necessary + if (m_torrent->ratio() != 0.f) + { + // accumulate all the free download we get + // and add it to the available free upload + m_available_free_upload + += collect_free_download( + m_torrent->begin() + , m_torrent->end()); + + // distribute the free upload among the peers + m_available_free_upload = distribute_free_upload( + m_torrent->begin() + , m_torrent->end() + , m_available_free_upload); + } + + // ------------------------ + // seed choking policy + // ------------------------ + if (m_torrent->is_seed()) + { + if (m_num_unchoked > m_torrent->m_uploads_quota.given) + { + do + { + iterator p = find_seed_choke_candidate(); + --m_num_unchoked; + assert(p != m_peers.end()); + if (p == m_peers.end()) break; + + assert(!p->connection->is_choked()); + p->connection->send_choke(); + } while (m_num_unchoked > m_torrent->m_uploads_quota.given); + } + else if (m_num_unchoked > 0) + { + // optimistic unchoke. trade the 'worst' + // unchoked peer with one of the choked + // TODO: This rotation should happen + // far less frequent than this! + assert(m_num_unchoked <= m_torrent->num_peers()); + iterator p = find_seed_unchoke_candidate(); + if (p != m_peers.end()) + { + assert(p->connection->is_choked()); + seed_choke_one_peer(); + p->connection->send_unchoke(); + ++m_num_unchoked; + } + + } + + // make sure we have enough + // unchoked peers + while (m_num_unchoked < m_torrent->m_uploads_quota.given) + { + if (!seed_unchoke_one_peer()) break; + } +#ifndef NDEBUG + check_invariant(); +#endif + } + + // ---------------------------- + // downloading choking policy + // ---------------------------- + else + { + if (m_torrent->ratio() != 0) + { + // choke peers that have leeched too much without giving anything back + for (iterator i = m_peers.begin(); + i != m_peers.end(); ++i) + { + peer_connection* c = i->connection; + if (c == 0) continue; + + size_type diff = i->connection->share_diff(); + if (diff < -free_upload_amount + && !c->is_choked()) + { + // if we have uploaded more than a piece for free, choke peer and + // wait until we catch up with our download. + c->send_choke(); + --m_num_unchoked; + } + } + } + + if (m_torrent->m_uploads_quota.given < m_torrent->num_peers()) + { + assert(m_torrent->m_uploads_quota.given >= 0); + + // make sure we don't have too many + // unchoked peers + if (m_num_unchoked > m_torrent->m_uploads_quota.given) + { + do + { + iterator p = find_choke_candidate(); + if (p == m_peers.end()) break; + assert(p != m_peers.end()); + assert(!p->connection->is_choked()); + p->connection->send_choke(); + --m_num_unchoked; + } while (m_num_unchoked > m_torrent->m_uploads_quota.given); + } + else + { + // optimistic unchoke. trade the 'worst' + // unchoked peer with one of the choked + // TODO: This rotation should happen + // far less frequent than this! + assert(m_num_unchoked <= m_torrent->num_peers()); + iterator p = find_unchoke_candidate(); + if (p != m_peers.end()) + { + assert(p->connection->is_choked()); + choke_one_peer(); + p->connection->send_unchoke(); + ++m_num_unchoked; + } + } + } + + // make sure we have enough + // unchoked peers + while (m_num_unchoked < m_torrent->m_uploads_quota.given + && unchoke_one_peer()); + } + } + + void policy::new_connection(peer_connection& c) + { + assert(!c.is_local()); + + INVARIANT_CHECK; + + // if the connection comes from the tracker, + // it's probably just a NAT-check. Ignore the + // num connections constraint then. + + // TODO: only allow _one_ connection to use this + // override at a time + assert(c.remote() == c.get_socket()->remote_endpoint()); + + if (m_torrent->num_peers() >= m_torrent->m_connections_quota.given + && c.remote().address() != m_torrent->current_tracker().address()) + { + throw protocol_error("too many connections, refusing incoming connection"); // cause a disconnect + } + +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + if (c.remote().address() == m_torrent->current_tracker().address()) + { + m_torrent->debug_log("overriding connection limit for tracker NAT-check"); + } +#endif + + iterator i; + + if (m_torrent->settings().allow_multiple_connections_per_ip) + { + i = m_peers.end(); + } + else + { + i = std::find_if( + m_peers.begin() + , m_peers.end() + , match_peer_ip(c.remote())); + } + + if (i != m_peers.end()) + { + if (i->banned) + throw protocol_error("ip address banned, closing"); + + if (i->connection != 0) + { + assert(i->connection != &c); + // the new connection is a local (outgoing) connection + // or the current one is already connected + if (!i->connection->is_connecting() || c.is_local()) + { + throw protocol_error("duplicate connection, closing"); + } + else + { +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + m_torrent->debug_log("duplicate connection. existing connection" + " is connecting and this connection is incoming. closing existing " + "connection in favour of this one"); +#endif + i->connection->disconnect(); + } + } + } + else + { + // we don't have ny info about this peer. + // add a new entry + assert(c.remote() == c.get_socket()->remote_endpoint()); + + peer p(c.remote(), peer::not_connectable, 0); + m_peers.push_back(p); + i = boost::prior(m_peers.end()); + } + + c.set_peer_info(&*i); + assert(i->connection == 0); + c.add_stat(i->prev_amount_download, i->prev_amount_upload); + i->prev_amount_download = 0; + i->prev_amount_upload = 0; + i->connection = &c; + assert(i->connection); + i->connected = time_now(); + m_last_optimistic_disconnect = time_now(); + } + + void policy::peer_from_tracker(const tcp::endpoint& remote, const peer_id& pid + , int src, char flags) + { + INVARIANT_CHECK; + + // just ignore the obviously invalid entries + if(remote.address() == address() || remote.port() == 0) + return; + + try + { + iterator i; + + if (m_torrent->settings().allow_multiple_connections_per_ip) + { + i = m_peers.end(); + } + else + { + i = std::find_if( + m_peers.begin() + , m_peers.end() + , match_peer_ip(remote)); + } + + if (i == m_peers.end()) + { + aux::session_impl& ses = m_torrent->session(); + + // if the IP is blocked, don't add it + if (ses.m_ip_filter.access(remote.address()) & ip_filter::blocked) + { + if (ses.m_alerts.should_post(alert::info)) + { + ses.m_alerts.post_alert(peer_blocked_alert(remote.address() + , "blocked peer not added to peer list")); + } + return; + } + + // we don't have any info about this peer. + // add a new entry + peer p(remote, peer::connectable, src); + m_peers.push_back(p); + // the iterator is invalid + // because of the push_back() + i = boost::prior(m_peers.end()); +#ifndef TORRENT_DISABLE_ENCRYPTION + p.pe_support = (flags & 0x01); +#endif + p.seed = (flags & 0x02); + + // try to send a DHT ping to this peer + // as well, to figure out if it supports + // DHT (uTorrent and BitComet doesn't + // advertise support) +#ifndef TORRENT_DISABLE_DHT + udp::endpoint node(remote.address(), remote.port()); + m_torrent->session().add_dht_node(node); +#endif + } + else + { + i->type = peer::connectable; + + // in case we got the ip from a remote connection, port is + // not known, so save it. Client may also have changed port + // for some reason. + i->ip = remote; + i->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 + // if it comes from the DHT it might be stale though + if (i->failcount > 0 && src != peer_info::dht) + --i->failcount; + + i->seed = (flags & 0x02); + + if (i->connection) + { + // this means we're already connected + // to this peer. don't connect to + // it again. + +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + m_torrent->debug_log("already connected to peer: " + remote.address().to_string() + ":" + + boost::lexical_cast(remote.port()) + " " + + boost::lexical_cast(i->connection->pid())); +#endif + + assert(i->connection->associated_torrent().lock().get() == m_torrent); + return; + } + } + } + catch(std::exception& e) + { + if (m_torrent->alerts().should_post(alert::debug)) + { + m_torrent->alerts().post_alert( + peer_error_alert(remote, pid, e.what())); + } + } + } + + // this is called when we are choked by a peer + // i.e. a peer lets us know that we will not receive + // anything for a while + void policy::choked(peer_connection&) + { + } + + void policy::piece_finished(int index, bool successfully_verified) + { + INVARIANT_CHECK; + + assert(index >= 0 && index < m_torrent->torrent_file().num_pieces()); + + if (successfully_verified) + { + // have all peers update their interested-flag + for (iterator i = m_peers.begin(); + i != m_peers.end(); ++i) + { + if (i->connection == 0) continue; + // if we're not interested, we will not become interested + if (!i->connection->is_interesting()) continue; + if (!i->connection->has_piece(index)) continue; + + i->connection->update_interest(); + } + } + } + + // TODO: we must be able to get interested + // in a peer again, if a piece fails that + // this peer has. + void policy::block_finished(peer_connection& c, piece_block) + { + INVARIANT_CHECK; + + // if the peer hasn't choked us, ask for another piece + if (!c.has_peer_choked() && !m_torrent->is_seed()) + request_a_block(*m_torrent, c); + } + + // this is called when we are unchoked by a peer + // i.e. a peer lets us know that we will receive + // data from now on + void policy::unchoked(peer_connection& c) + { + INVARIANT_CHECK; + if (c.is_interesting()) + { + request_a_block(*m_torrent, c); + } + } + + // called when a peer is interested in us + void policy::interested(peer_connection& c) + { + INVARIANT_CHECK; + + assert(std::find_if(m_peers.begin(), m_peers.end() + , boost::bind(std::equal_to(), bind(&peer::connection, _1) + , &c)) != m_peers.end()); + + // if the peer is choked and we have upload slots left, + // then unchoke it. Another condition that has to be met + // is that the torrent doesn't keep track of the individual + // up/down ratio for each peer (ratio == 0) or (if it does + // keep track) this particular connection isn't a leecher. + // If the peer was choked because it was leeching, don't + // unchoke it again. + // The exception to this last condition is if we're a seed. + // In that case we don't care if people are leeching, they + // can't pay for their downloads anyway. + if (c.is_choked() + && m_num_unchoked < m_torrent->m_uploads_quota.given + && (m_torrent->ratio() == 0 + || c.share_diff() >= -free_upload_amount + || m_torrent->is_seed())) + { + c.send_unchoke(); + ++m_num_unchoked; + } + } + + // called when a peer is no longer interested in us + void policy::not_interested(peer_connection& c) + { + INVARIANT_CHECK; + + if (m_torrent->ratio() != 0.f) + { + assert(c.share_diff() < std::numeric_limits::max()); + size_type diff = c.share_diff(); + if (diff > 0 && c.is_seed()) + { + // the peer is a seed and has sent + // us more than we have sent it back. + // consider the download as free download + m_available_free_upload += diff; + c.add_free_upload(-diff); + } + } + if (!c.is_choked()) + { + c.send_choke(); + --m_num_unchoked; + + if (m_torrent->is_seed()) seed_unchoke_one_peer(); + else unchoke_one_peer(); + } + } + + bool policy::unchoke_one_peer() + { + INVARIANT_CHECK; + + iterator p = find_unchoke_candidate(); + if (p == m_peers.end()) return false; + assert(p->connection); + assert(!p->connection->is_disconnecting()); + + assert(p->connection->is_choked()); + p->connection->send_unchoke(); + p->last_optimistically_unchoked = time_now(); + ++m_num_unchoked; + return true; + } + + void policy::choke_one_peer() + { + INVARIANT_CHECK; + + iterator p = find_choke_candidate(); + if (p == m_peers.end()) return; + assert(p->connection); + assert(!p->connection->is_disconnecting()); + assert(!p->connection->is_choked()); + p->connection->send_choke(); + --m_num_unchoked; + } + + bool policy::connect_one_peer() + { + INVARIANT_CHECK; + + assert(m_torrent->want_more_peers()); + + iterator p = find_connect_candidate(); + if (p == m_peers.end()) return false; + + assert(!p->banned); + assert(!p->connection); + assert(p->type == peer::connectable); + + try + { + p->connected = m_last_optimistic_disconnect = time_now(); + p->connection = m_torrent->connect_to_peer(&*p); + if (p->connection == 0) return false; + p->connection->add_stat(p->prev_amount_download, p->prev_amount_upload); + p->prev_amount_download = 0; + p->prev_amount_upload = 0; + return true; + } + catch (std::exception& e) + { +#if defined(TORRENT_VERBOSE_LOGGING) + (*m_torrent->session().m_logger) << "*** CONNECTION FAILED '" + << e.what() << "'\n"; +#endif + ++p->failcount; + return false; + } + } + + bool policy::disconnect_one_peer() + { + iterator p = find_disconnect_candidate(); + if (p == m_peers.end()) + return false; +#if defined(TORRENT_VERBOSE_LOGGING) + (*p->connection->m_logger) << "*** CLOSING CONNECTION 'too many connections'\n"; +#endif + + p->connection->disconnect(); + return true; + } + + // this is called whenever a peer connection is closed + void policy::connection_closed(const peer_connection& c) try + { + INVARIANT_CHECK; + +// assert(c.is_disconnecting()); + bool unchoked = false; + + iterator i = std::find_if( + m_peers.begin() + , m_peers.end() + , match_peer_connection(c)); + + // if we couldn't find the connection in our list, just ignore it. + if (i == m_peers.end()) return; + assert(i->connection == &c); + i->connection = 0; + + i->connected = time_now(); + if (!c.is_choked() && !m_torrent->is_aborted()) + { + unchoked = true; + } + + if (c.failed()) + { + ++i->failcount; + i->connected = time_now(); + } + + // if the share ratio is 0 (infinite), the + // m_available_free_upload isn't used, + // because it isn't necessary. + if (m_torrent->ratio() != 0.f) + { + assert(c.associated_torrent().lock().get() == m_torrent); + assert(c.share_diff() < std::numeric_limits::max()); + m_available_free_upload += c.share_diff(); + } + i->prev_amount_download += c.statistics().total_payload_download(); + i->prev_amount_upload += c.statistics().total_payload_upload(); + + if (unchoked) + { + // if the peer that is diconnecting is unchoked + // then unchoke another peer in order to maintain + // the total number of unchoked peers + --m_num_unchoked; + if (m_torrent->is_seed()) seed_unchoke_one_peer(); + else unchoke_one_peer(); + } + } + catch (std::exception& e) + { +#ifndef NDEBUG + std::string err = e.what(); +#endif + assert(false); + } + + void policy::peer_is_interesting(peer_connection& c) + { + INVARIANT_CHECK; + + c.send_interested(); + if (c.has_peer_choked()) return; + request_a_block(*m_torrent, c); + } + +#ifndef NDEBUG + bool policy::has_connection(const peer_connection* c) + { + INVARIANT_CHECK; + + assert(c); + assert(c->remote() == c->get_socket()->remote_endpoint()); + + return std::find_if( + m_peers.begin() + , m_peers.end() + , match_peer_connection(*c)) != m_peers.end(); + } + + void policy::check_invariant() const + { + if (m_torrent->is_aborted()) return; + int actual_unchoked = 0; + int connected_peers = 0; + + int total_connections = 0; + int nonempty_connections = 0; + + std::set
unique_test; + for (const_iterator i = m_peers.begin(); + i != m_peers.end(); ++i) + { + if (!m_torrent->settings().allow_multiple_connections_per_ip) + assert(unique_test.find(i->ip.address()) == unique_test.end()); + unique_test.insert(i->ip.address()); + ++total_connections; + if (!i->connection) continue; + assert(i->connection->peer_info_struct() == 0 + || i->connection->peer_info_struct() == &*i); + ++nonempty_connections; + if (!i->connection->is_disconnecting()) + ++connected_peers; + if (!i->connection->is_choked()) ++actual_unchoked; + } +// assert(actual_unchoked <= m_torrent->m_uploads_quota.given); + assert(actual_unchoked == m_num_unchoked); + + int num_torrent_peers = 0; + for (torrent::const_peer_iterator i = m_torrent->begin(); + i != m_torrent->end(); ++i) + { + if (i->second->is_disconnecting()) continue; + // ignore web_peer_connections since they are not managed + // by the policy class + if (dynamic_cast(i->second)) continue; + ++num_torrent_peers; + } + + // this invariant is a bit complicated. + // the usual case should be that connected_peers + // == num_torrent_peers. But when there's an incoming + // connection, it will first be added to the policy + // and then be added to the torrent. + // When there's an outgoing connection, it will first + // be added to the torrent and then to the policy. + // that's why the two second cases are in there. +/* + assert(connected_peers == num_torrent_peers + || (connected_peers == num_torrent_peers + 1 + && connected_peers > 0) + || (connected_peers + 1 == num_torrent_peers + && num_torrent_peers > 0)); +*/ + } +#endif + + policy::peer::peer(const tcp::endpoint& ip_, peer::connection_type t, int src) + : ip(ip_) + , type(t) +#ifndef TORRENT_DISABLE_ENCRYPTION + , pe_support(true) +#endif + , failcount(0) + , hashfails(0) + , seed(false) + , last_optimistically_unchoked(min_time()) + , connected(min_time()) + , trust_points(0) + , on_parole(false) + , prev_amount_upload(0) + , prev_amount_download(0) + , banned(false) + , source(src) + , connection(0) + { + assert(connected < time_now()); + } + + size_type policy::peer::total_download() const + { + if (connection != 0) + { + assert(prev_amount_download == 0); + return connection->statistics().total_payload_download(); + } + else + { + return prev_amount_download; + } + } + + size_type policy::peer::total_upload() const + { + if (connection != 0) + { + assert(prev_amount_upload == 0); + return connection->statistics().total_payload_upload(); + } + else + { + return prev_amount_upload; + } + } +} + diff --git a/encryption/libtorrent/src/session.cpp b/encryption/libtorrent/src/session.cpp new file mode 100755 index 000000000..e1ac044c6 --- /dev/null +++ b/encryption/libtorrent/src/session.cpp @@ -0,0 +1,375 @@ +/* + +Copyright (c) 2006, Arvid Norberg, Magnus Jonsson +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/pch.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/peer_id.hpp" +#include "libtorrent/torrent_info.hpp" +#include "libtorrent/tracker_manager.hpp" +#include "libtorrent/bencode.hpp" +#include "libtorrent/hasher.hpp" +#include "libtorrent/entry.hpp" +#include "libtorrent/session.hpp" +#include "libtorrent/fingerprint.hpp" +#include "libtorrent/entry.hpp" +#include "libtorrent/alert_types.hpp" +#include "libtorrent/invariant_check.hpp" +#include "libtorrent/file.hpp" +#include "libtorrent/allocate_resources.hpp" +#include "libtorrent/bt_peer_connection.hpp" +#include "libtorrent/ip_filter.hpp" +#include "libtorrent/socket.hpp" +#include "libtorrent/aux_/session_impl.hpp" +#include "libtorrent/kademlia/dht_tracker.hpp" + +using boost::shared_ptr; +using boost::weak_ptr; +using boost::bind; +using boost::mutex; +using libtorrent::aux::session_impl; + +namespace libtorrent +{ + + namespace aux + { + filesystem_init::filesystem_init() + { +#if BOOST_VERSION < 103400 + using namespace boost::filesystem; + if (path::default_name_check_writable()) + path::default_name_check(no_check); +#endif + } + } + + session::session( + fingerprint const& id + , std::pair listen_port_range + , char const* listen_interface) + : m_impl(new session_impl(listen_port_range, id, listen_interface)) + { + // turn off the filename checking in boost.filesystem + assert(listen_port_range.first > 0); + assert(listen_port_range.first < listen_port_range.second); +#ifndef NDEBUG + // this test was added after it came to my attention + // that devstudios managed c++ failed to generate + // correct code for boost.function + boost::function0 test = boost::ref(*m_impl); + assert(!test.empty()); +#endif + } + + session::session(fingerprint const& id) + : m_impl(new session_impl(std::make_pair(0, 0), id)) + { +#ifndef NDEBUG + boost::function0 test = boost::ref(*m_impl); + assert(!test.empty()); +#endif + } + + session::~session() + { + assert(m_impl); + // if there is at least one destruction-proxy + // abort the session and let the destructor + // of the proxy to syncronize + if (!m_impl.unique()) + m_impl->abort(); + } + + void session::add_extension(boost::function(torrent*)> ext) + { + m_impl->add_extension(ext); + } + + void session::set_ip_filter(ip_filter const& f) + { + m_impl->set_ip_filter(f); + } + + void session::set_peer_id(peer_id const& id) + { + m_impl->set_peer_id(id); + } + + void session::set_key(int key) + { + m_impl->set_key(key); + } + + std::vector session::get_torrents() const + { + return m_impl->get_torrents(); + } + + torrent_handle session::find_torrent(sha1_hash const& info_hash) const + { + return m_impl->find_torrent_handle(info_hash); + } + + + // if the torrent already exists, this will throw duplicate_torrent + torrent_handle session::add_torrent( + torrent_info const& ti + , boost::filesystem::path const& save_path + , entry const& resume_data + , bool compact_mode + , int block_size + , storage_constructor_type sc) + { + return m_impl->add_torrent(ti, save_path, resume_data + , compact_mode, block_size, sc); + } + + torrent_handle session::add_torrent( + char const* tracker_url + , sha1_hash const& info_hash + , char const* name + , boost::filesystem::path const& save_path + , entry const& e + , bool compact_mode + , int block_size + , storage_constructor_type sc) + { + return m_impl->add_torrent(tracker_url, info_hash, name, save_path, e + , compact_mode, block_size, sc); + } + + void session::remove_torrent(const torrent_handle& h) + { + m_impl->remove_torrent(h); + } + + bool session::listen_on( + std::pair const& port_range + , const char* net_interface) + { + return m_impl->listen_on(port_range, net_interface); + } + + unsigned short session::listen_port() const + { + return m_impl->listen_port(); + } + + session_status session::status() const + { + return m_impl->status(); + } + +#ifndef TORRENT_DISABLE_DHT + + void session::start_dht(entry const& startup_state) + { + m_impl->start_dht(startup_state); + } + + void session::stop_dht() + { + m_impl->stop_dht(); + } + + void session::set_dht_settings(dht_settings const& settings) + { + m_impl->set_dht_settings(settings); + } + + entry session::dht_state() const + { + return m_impl->dht_state(); + } + + void session::add_dht_node(std::pair const& node) + { + m_impl->add_dht_node(node); + } + + void session::add_dht_router(std::pair const& node) + { + m_impl->add_dht_router(node); + } + +#endif + +#ifndef TORRENT_DISABLE_ENCRYPTION + void session::set_pe_settings(pe_settings const& settings) + { + m_impl->set_pe_settings(settings); + } +#endif + + bool session::is_listening() const + { + return m_impl->is_listening(); + } + + void session::set_settings(session_settings const& s) + { + m_impl->set_settings(s); + } + + session_settings const& session::settings() + { + return m_impl->settings(); + } + + void session::set_peer_proxy(proxy_settings const& s) + { + m_impl->set_peer_proxy(s); + } + + void session::set_web_seed_proxy(proxy_settings const& s) + { + m_impl->set_web_seed_proxy(s); + } + + void session::set_tracker_proxy(proxy_settings const& s) + { + m_impl->set_tracker_proxy(s); + } + + proxy_settings const& session::peer_proxy() const + { + return m_impl->peer_proxy(); + } + + proxy_settings const& session::web_seed_proxy() const + { + return m_impl->web_seed_proxy(); + } + + proxy_settings const& session::tracker_proxy() const + { + return m_impl->tracker_proxy(); + } + + +#ifndef TORRENT_DISABLE_DHT + void session::set_dht_proxy(proxy_settings const& s) + { + m_impl->set_dht_proxy(s); + } + + proxy_settings const& session::dht_proxy() const + { + return m_impl->dht_proxy(); + } +#endif + + void session::set_max_uploads(int limit) + { + m_impl->set_max_uploads(limit); + } + + void session::set_max_connections(int limit) + { + m_impl->set_max_connections(limit); + } + + void session::set_max_half_open_connections(int limit) + { + m_impl->set_max_half_open_connections(limit); + } + + int session::upload_rate_limit() const + { + return m_impl->upload_rate_limit(); + } + + int session::download_rate_limit() const + { + return m_impl->download_rate_limit(); + } + + void session::set_upload_rate_limit(int bytes_per_second) + { + m_impl->set_upload_rate_limit(bytes_per_second); + } + + void session::set_download_rate_limit(int bytes_per_second) + { + m_impl->set_download_rate_limit(bytes_per_second); + } + + int session::num_uploads() const + { + return m_impl->num_uploads(); + } + + int session::num_connections() const + { + return m_impl->num_connections(); + } + + std::auto_ptr session::pop_alert() + { + return m_impl->pop_alert(); + } + + void session::set_severity_level(alert::severity_t s) + { + m_impl->set_severity_level(s); + } + + connection_queue& session::get_connection_queue() + { + return m_impl->m_half_open; + } +} + diff --git a/encryption/libtorrent/src/session_impl.cpp b/encryption/libtorrent/src/session_impl.cpp new file mode 100755 index 000000000..3c16a53a7 --- /dev/null +++ b/encryption/libtorrent/src/session_impl.cpp @@ -0,0 +1,2096 @@ +/* + +Copyright (c) 2006, Arvid Norberg, Magnus Jonsson +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/pch.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/peer_id.hpp" +#include "libtorrent/torrent_info.hpp" +#include "libtorrent/tracker_manager.hpp" +#include "libtorrent/bencode.hpp" +#include "libtorrent/hasher.hpp" +#include "libtorrent/entry.hpp" +#include "libtorrent/session.hpp" +#include "libtorrent/fingerprint.hpp" +#include "libtorrent/entry.hpp" +#include "libtorrent/alert_types.hpp" +#include "libtorrent/invariant_check.hpp" +#include "libtorrent/file.hpp" +#include "libtorrent/allocate_resources.hpp" +#include "libtorrent/bt_peer_connection.hpp" +#include "libtorrent/ip_filter.hpp" +#include "libtorrent/socket.hpp" +#include "libtorrent/aux_/session_impl.hpp" +#include "libtorrent/kademlia/dht_tracker.hpp" + +using boost::shared_ptr; +using boost::weak_ptr; +using boost::bind; +using boost::mutex; +using libtorrent::aux::session_impl; + +namespace libtorrent { namespace detail +{ + + std::string generate_auth_string(std::string const& user + , std::string const& passwd) + { + if (user.empty()) return std::string(); + return user + ":" + passwd; + } + + + } namespace aux { + // This is the checker thread + // it is looping in an infinite loop + // until the session is aborted. It will + // normally just block in a wait() call, + // waiting for a signal from session that + // there's a new torrent to check. + + void checker_impl::operator()() + { + eh_initializer(); + // if we're currently performing a full file check, + // this is the torrent being processed + boost::shared_ptr processing; + boost::shared_ptr t; + for (;;) + { + // temporary torrent used while checking fastresume data + try + { + t.reset(); + { + boost::mutex::scoped_lock l(m_mutex); + + INVARIANT_CHECK; + + // if the job queue is empty and + // we shouldn't abort + // wait for a signal + while (m_torrents.empty() && !m_abort && !processing) + m_cond.wait(l); + + if (m_abort) + { + // no lock is needed here, because the main thread + // has already been shut down by now + processing.reset(); + t.reset(); + std::for_each(m_torrents.begin(), m_torrents.end() + , boost::bind(&torrent::abort + , boost::bind(&shared_ptr::get + , boost::bind(&piece_checker_data::torrent_ptr, _1)))); + m_torrents.clear(); + std::for_each(m_processing.begin(), m_processing.end() + , boost::bind(&torrent::abort + , boost::bind(&shared_ptr::get + , boost::bind(&piece_checker_data::torrent_ptr, _1)))); + m_processing.clear(); + return; + } + + if (!m_torrents.empty()) + { + t = m_torrents.front(); + if (t->abort) + { + // make sure the locking order is + // consistent to avoid dead locks + // we need to lock the session because closing + // torrents assume to have access to it + l.unlock(); + session_impl::mutex_t::scoped_lock l2(m_ses.m_mutex); + l.lock(); + + t->torrent_ptr->abort(); + m_torrents.pop_front(); + continue; + } + } + } + + if (t) + { + std::string error_msg; + t->parse_resume_data(t->resume_data, t->torrent_ptr->torrent_file() + , error_msg); + + if (!error_msg.empty() && m_ses.m_alerts.should_post(alert::warning)) + { + session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); + m_ses.m_alerts.post_alert(fastresume_rejected_alert( + t->torrent_ptr->get_handle() + , error_msg)); +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + (*m_ses.m_logger) << "fastresume data for " + << t->torrent_ptr->torrent_file().name() << " rejected: " + << error_msg << "\n"; +#endif + } + + // lock the session to add the new torrent + session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); + mutex::scoped_lock l2(m_mutex); + // clear the resume data now that it has been used + // (the fast resume data is now parsed and stored in t) + t->resume_data = entry(); + bool up_to_date = t->torrent_ptr->check_fastresume(*t); + + if (up_to_date) + { + INVARIANT_CHECK; + + assert(m_torrents.front() == t); + + t->torrent_ptr->files_checked(t->unfinished_pieces); + m_torrents.pop_front(); + + // we cannot add the torrent if the session is aborted. + if (!m_ses.is_aborted()) + { + m_ses.m_torrents.insert(std::make_pair(t->info_hash, t->torrent_ptr)); + if (t->torrent_ptr->is_seed() && m_ses.m_alerts.should_post(alert::info)) + { + m_ses.m_alerts.post_alert(torrent_finished_alert( + t->torrent_ptr->get_handle() + , "torrent is complete")); + } + + peer_id id; + std::fill(id.begin(), id.end(), 0); + for (std::vector::const_iterator i = t->peers.begin(); + i != t->peers.end(); ++i) + { + t->torrent_ptr->get_policy().peer_from_tracker(*i, id + , peer_info::resume_data, 0); + } + } + else + { + t->torrent_ptr->abort(); + } + t.reset(); + continue; + } + + l.unlock(); + + // move the torrent from + // m_torrents to m_processing + assert(m_torrents.front() == t); + + m_torrents.pop_front(); + m_processing.push_back(t); + if (!processing) + { + processing = t; + processing->processing = true; + t.reset(); + } + } + } + catch (const std::exception& e) + { + // This will happen if the storage fails to initialize + // for example if one of the files has an invalid filename. + session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); + mutex::scoped_lock l2(m_mutex); + + if (m_ses.m_alerts.should_post(alert::fatal)) + { + m_ses.m_alerts.post_alert( + file_error_alert( + t->torrent_ptr->get_handle() + , e.what())); + } + t->torrent_ptr->abort(); + + assert(!m_torrents.empty()); + m_torrents.pop_front(); + } + catch(...) + { +#ifndef NDEBUG + std::cerr << "error while checking resume data\n"; +#endif + mutex::scoped_lock l(m_mutex); + assert(!m_torrents.empty()); + m_torrents.pop_front(); + assert(false); + } + + if (!processing) continue; + + try + { + assert(processing); + + float finished = false; + float progress = 0.f; + boost::tie(finished, progress) = processing->torrent_ptr->check_files(); + + { + mutex::scoped_lock l(m_mutex); + + INVARIANT_CHECK; + + processing->progress = progress; + if (processing->abort) + { + assert(!m_processing.empty()); + assert(m_processing.front() == processing); + + processing->torrent_ptr->abort(); + + processing.reset(); + m_processing.pop_front(); + if (!m_processing.empty()) + { + processing = m_processing.front(); + processing->processing = true; + } + continue; + } + } + if (finished) + { + // lock the session to add the new torrent + session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); + mutex::scoped_lock l2(m_mutex); + + INVARIANT_CHECK; + + assert(!m_processing.empty()); + assert(m_processing.front() == processing); + + // TODO: factor out the adding of torrents to the session + // and to the checker thread to avoid duplicating the + // check for abortion. + if (!m_ses.is_aborted()) + { + processing->torrent_ptr->files_checked(processing->unfinished_pieces); + m_ses.m_torrents.insert(std::make_pair( + processing->info_hash, processing->torrent_ptr)); + if (processing->torrent_ptr->is_seed() + && m_ses.m_alerts.should_post(alert::info)) + { + m_ses.m_alerts.post_alert(torrent_finished_alert( + processing->torrent_ptr->get_handle() + , "torrent is complete")); + } + + peer_id id; + std::fill(id.begin(), id.end(), 0); + for (std::vector::const_iterator i = processing->peers.begin(); + i != processing->peers.end(); ++i) + { + processing->torrent_ptr->get_policy().peer_from_tracker(*i, id + , peer_info::resume_data, 0); + } + } + else + { + processing->torrent_ptr->abort(); + } + processing.reset(); + m_processing.pop_front(); + if (!m_processing.empty()) + { + processing = m_processing.front(); + processing->processing = true; + } + } + } + catch(std::exception const& e) + { + // This will happen if the storage fails to initialize + session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); + mutex::scoped_lock l2(m_mutex); + + if (m_ses.m_alerts.should_post(alert::fatal)) + { + m_ses.m_alerts.post_alert( + file_error_alert( + processing->torrent_ptr->get_handle() + , e.what())); + } + assert(!m_processing.empty()); + + processing->torrent_ptr->abort(); + + processing.reset(); + m_processing.pop_front(); + if (!m_processing.empty()) + { + processing = m_processing.front(); + processing->processing = true; + } + } + catch(...) + { +#ifndef NDEBUG + std::cerr << "error while checking files\n"; +#endif + mutex::scoped_lock l(m_mutex); + assert(!m_processing.empty()); + + processing.reset(); + m_processing.pop_front(); + if (!m_processing.empty()) + { + processing = m_processing.front(); + processing->processing = true; + } + + assert(false); + } + } + } + + aux::piece_checker_data* checker_impl::find_torrent(sha1_hash const& info_hash) + { + INVARIANT_CHECK; + for (std::deque >::iterator i + = m_torrents.begin(); i != m_torrents.end(); ++i) + { + if ((*i)->info_hash == info_hash) return i->get(); + } + for (std::deque >::iterator i + = m_processing.begin(); i != m_processing.end(); ++i) + { + + if ((*i)->info_hash == info_hash) return i->get(); + } + + return 0; + } + + void checker_impl::remove_torrent(sha1_hash const& info_hash) + { + INVARIANT_CHECK; + for (std::deque >::iterator i + = m_torrents.begin(); i != m_torrents.end(); ++i) + { + if ((*i)->info_hash == info_hash) + { + assert((*i)->processing == false); + m_torrents.erase(i); + return; + } + } + for (std::deque >::iterator i + = m_processing.begin(); i != m_processing.end(); ++i) + { + if ((*i)->info_hash == info_hash) + { + assert((*i)->processing == false); + m_processing.erase(i); + return; + } + } + + assert(false); + } + +#ifndef NDEBUG + void checker_impl::check_invariant() const + { + for (std::deque >::const_iterator i + = m_torrents.begin(); i != m_torrents.end(); ++i) + { + assert(*i); + assert((*i)->torrent_ptr); + } + for (std::deque >::const_iterator i + = m_processing.begin(); i != m_processing.end(); ++i) + { + assert(*i); + assert((*i)->torrent_ptr); + } + } +#endif + + struct seed_random_generator + { + seed_random_generator() + { + std::srand(total_microseconds(time_now() - min_time())); + } + }; + + session_impl::session_impl( + std::pair listen_port_range + , fingerprint const& cl_fprint + , char const* listen_interface) + : m_strand(m_io_service) + , m_files(40) + , m_half_open(m_io_service) + , m_dl_bandwidth_manager(m_io_service, peer_connection::download_channel) + , m_ul_bandwidth_manager(m_io_service, peer_connection::upload_channel) + , m_tracker_manager(m_settings, m_tracker_proxy) + , m_listen_port_range(listen_port_range) + , m_listen_interface(address::from_string(listen_interface), listen_port_range.first) + , m_external_listen_port(0) + , m_abort(false) + , m_max_uploads(-1) + , m_max_connections(-1) + , m_incoming_connection(false) + , m_last_tick(time_now()) +#ifndef TORRENT_DISABLE_DHT + , m_dht_same_port(true) + , m_external_udp_port(0) +#endif + , m_natpmp(m_io_service, m_listen_interface.address() + , bind(&session_impl::on_port_mapping, this, _1, _2, _3)) + , m_upnp(m_io_service, m_half_open, m_listen_interface.address() + , m_settings.user_agent + , bind(&session_impl::on_port_mapping, this, _1, _2, _3)) + , m_lsd(m_io_service, m_listen_interface.address() + , bind(&session_impl::on_lsd_peer, this, _1, _2)) + , m_timer(m_io_service) + , m_next_connect_torrent(0) + , m_checker_impl(*this) + { + +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + m_logger = create_log("main_session", listen_port(), false); + (*m_logger) << time_now_string() << "\n"; + + m_dl_bandwidth_manager.m_ses = this; + m_ul_bandwidth_manager.m_ses = this; +#endif + +#ifdef TORRENT_STATS + m_stats_logger.open("session_stats.log"); + m_stats_logger << + "1. second\n" + "2. upload rate\n" + "3. download rate\n" + "4. downloading torrents\n" + "5. seeding torrents\n" + "6. peers\n" + "7. connecting peers\n" + "\n"; + m_second_counter = 0; +#endif + + // ---- generate a peer id ---- + static seed_random_generator seeder; + + m_key = rand() + (rand() << 15) + (rand() << 30); + std::string print = cl_fprint.to_string(); + assert(print.length() <= 20); + + // the client's fingerprint + std::copy( + print.begin() + , print.begin() + print.length() + , m_peer_id.begin()); + + // http-accepted characters: + static char const printable[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz-_.!~*'()"; + + // the random number + for (unsigned char* i = m_peer_id.begin() + print.length(); + i != m_peer_id.end(); ++i) + { + *i = printable[rand() % (sizeof(printable)-1)]; + } + + m_timer.expires_from_now(seconds(1)); + m_timer.async_wait(m_strand.wrap( + bind(&session_impl::second_tick, this, _1))); + + m_thread.reset(new boost::thread(boost::ref(*this))); + m_checker_thread.reset(new boost::thread(boost::ref(m_checker_impl))); + } + +#ifndef TORRENT_DISABLE_EXTENSIONS + void session_impl::add_extension( + boost::function(torrent*)> ext) + { + m_extensions.push_back(ext); + } +#endif + +#ifndef TORRENT_DISABLE_DHT + void session_impl::add_dht_node(udp::endpoint n) + { + if (m_dht) m_dht->add_node(n); + } +#endif + + void session_impl::abort() + { + mutex_t::scoped_lock l(m_mutex); + assert(!m_abort); + // abort the main thread + m_abort = true; + m_io_service.stop(); + l.unlock(); + + mutex::scoped_lock l2(m_checker_impl.m_mutex); + // abort the checker thread + m_checker_impl.m_abort = true; + } + + void session_impl::set_ip_filter(ip_filter const& f) + { + mutex_t::scoped_lock l(m_mutex); + m_ip_filter = f; + + // Close connections whose endpoint is filtered + // by the new ip-filter + for (session_impl::connection_map::iterator i + = m_connections.begin(); i != m_connections.end();) + { + tcp::endpoint sender = i->first->remote_endpoint(); + if (m_ip_filter.access(sender.address()) & ip_filter::blocked) + { +#if defined(TORRENT_VERBOSE_LOGGING) + (*i->second->m_logger) << "*** CONNECTION FILTERED\n"; +#endif + if (m_alerts.should_post(alert::info)) + { + m_alerts.post_alert(peer_blocked_alert(sender.address() + , "peer connection closed by IP filter")); + } + + session_impl::connection_map::iterator j = i; + ++i; + j->second->disconnect(); + } + else ++i; + } + } + + void session_impl::set_settings(session_settings const& s) + { + mutex_t::scoped_lock l(m_mutex); + assert(s.connection_speed > 0); + assert(s.file_pool_size > 0); + m_settings = s; + m_files.resize(m_settings.file_pool_size); + // replace all occurances of '\n' with ' '. + std::string::iterator i = m_settings.user_agent.begin(); + while ((i = std::find(i, m_settings.user_agent.end(), '\n')) + != m_settings.user_agent.end()) + *i = ' '; + } + + void session_impl::open_listen_port() + { + try + { + // create listener socket + m_listen_socket = boost::shared_ptr(new socket_acceptor(m_io_service)); + + for(;;) + { + try + { + m_listen_socket->open(m_listen_interface.protocol()); + m_listen_socket->bind(m_listen_interface); + m_listen_socket->listen(); + m_external_listen_port = m_listen_interface.port(); + break; + } + catch (asio::system_error& e) + { + // TODO: make sure this is correct + if (e.code() == asio::error::host_not_found) + { + if (m_alerts.should_post(alert::fatal)) + { + std::string msg = "cannot listen on the given interface '" + + m_listen_interface.address().to_string() + "'"; + m_alerts.post_alert(listen_failed_alert(msg)); + } +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + std::string msg = "cannot listen on the given interface '" + + m_listen_interface.address().to_string() + "'"; + (*m_logger) << msg << "\n"; +#endif + assert(m_listen_socket.unique()); + m_listen_socket.reset(); + break; + } + m_listen_socket->close(); + m_listen_interface.port(m_listen_interface.port() + 1); + if (m_listen_interface.port() > m_listen_port_range.second) + { + std::stringstream msg; + msg << "none of the ports in the range [" + << m_listen_port_range.first + << ", " << m_listen_port_range.second + << "] could be opened for listening"; + m_alerts.post_alert(listen_failed_alert(msg.str())); +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + (*m_logger) << msg.str() << "\n"; +#endif + m_listen_socket.reset(); + break; + } + } + } + } + catch (asio::system_error& e) + { + if (m_alerts.should_post(alert::fatal)) + { + m_alerts.post_alert(listen_failed_alert( + std::string("failed to open listen port: ") + e.what())); + } + } + +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + if (m_listen_socket) + { + (*m_logger) << "listening on port: " << m_listen_interface.port() + << " external port: " << m_external_listen_port << "\n"; + } +#endif + if (m_listen_socket) async_accept(); + } + + void session_impl::async_accept() + { + shared_ptr c(new socket_type(m_io_service)); + c->instantiate(); + m_listen_socket->async_accept(c->get() + , bind(&session_impl::on_incoming_connection, this, c + , weak_ptr(m_listen_socket), _1)); + } + + void session_impl::on_incoming_connection(shared_ptr const& s + , weak_ptr const& listen_socket, asio::error_code const& e) try + { + if (listen_socket.expired()) + return; + + if (e == asio::error::operation_aborted) + return; + + mutex_t::scoped_lock l(m_mutex); + assert(listen_socket.lock() == m_listen_socket); + + if (m_abort) return; + + async_accept(); + if (e) + { +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + std::string msg = "error accepting connection on '" + + m_listen_interface.address().to_string() + "'"; + (*m_logger) << msg << "\n"; +#endif + assert(m_listen_socket.unique()); + return; + } + + // we got a connection request! + m_incoming_connection = true; + tcp::endpoint endp = s->remote_endpoint(); + +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + (*m_logger) << endp << " <== INCOMING CONNECTION\n"; +#endif + if (m_ip_filter.access(endp.address()) & ip_filter::blocked) + { +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + (*m_logger) << "filtered blocked ip\n"; +#endif + if (m_alerts.should_post(alert::info)) + { + m_alerts.post_alert(peer_blocked_alert(endp.address() + , "incoming connection blocked by IP filter")); + } + return; + } + + boost::intrusive_ptr c( + new bt_peer_connection(*this, s, 0)); +#ifndef NDEBUG + c->m_in_constructor = false; +#endif + + m_connections.insert(std::make_pair(s, c)); + } + catch (std::exception& exc) + { +#ifndef NDEBUG + std::string err = exc.what(); +#endif + }; + + void session_impl::connection_failed(boost::shared_ptr const& s + , tcp::endpoint const& a, char const* message) +#ifndef NDEBUG + try +#endif + { + mutex_t::scoped_lock l(m_mutex); + + connection_map::iterator p = m_connections.find(s); + + // the connection may have been disconnected in the receive or send phase + if (p == m_connections.end()) return; + if (m_alerts.should_post(alert::debug)) + { + m_alerts.post_alert( + peer_error_alert( + a + , p->second->pid() + , message)); + } + +#if defined(TORRENT_VERBOSE_LOGGING) + (*p->second->m_logger) << "*** CONNECTION FAILED " << message << "\n"; +#endif + p->second->set_failed(); + p->second->disconnect(); + } +#ifndef NDEBUG + catch (...) + { + assert(false); + }; +#endif + + void session_impl::close_connection(boost::intrusive_ptr const& p) + { + mutex_t::scoped_lock l(m_mutex); + + assert(p->is_disconnecting()); + connection_map::iterator i = m_connections.find(p->get_socket()); + if (i != m_connections.end()) + m_connections.erase(i); + } + + void session_impl::set_peer_id(peer_id const& id) + { + mutex_t::scoped_lock l(m_mutex); + m_peer_id = id; + } + + void session_impl::set_key(int key) + { + mutex_t::scoped_lock l(m_mutex); + m_key = key; + } + + void session_impl::second_tick(asio::error_code const& e) try + { + session_impl::mutex_t::scoped_lock l(m_mutex); + + if (e) + { +#if defined(TORRENT_LOGGING) + (*m_logger) << "*** SECOND TIMER FAILED " << e.message() << "\n"; +#endif + m_abort = true; + m_io_service.stop(); + return; + } + + if (m_abort) return; + float tick_interval = total_microseconds(time_now() - m_last_tick) / 1000000.f; + m_last_tick = time_now(); + + m_timer.expires_from_now(seconds(1)); + m_timer.async_wait(m_strand.wrap( + bind(&session_impl::second_tick, this, _1))); + +#ifdef TORRENT_STATS + ++m_second_counter; + int downloading_torrents = 0; + int seeding_torrents = 0; + for (torrent_map::iterator i = m_torrents.begin() + , end(m_torrents.end()); i != end; ++i) + { + if (i->second->is_seed()) + ++seeding_torrents; + else + ++downloading_torrents; + } + int num_connections = 0; + int num_half_open = 0; + for (connection_map::iterator i = m_connections.begin() + , end(m_connections.end()); i != end; ++i) + { + if (i->second->is_connecting()) + ++num_half_open; + else + ++num_connections; + } + + m_stats_logger + << m_second_counter << "\t" + << m_stat.upload_rate() << "\t" + << m_stat.download_rate() << "\t" + << downloading_torrents << "\t" + << seeding_torrents << "\t" + << num_connections << "\t" + << num_half_open << "\t" + << std::endl; +#endif + + + // let torrents connect to peers if they want to + // if there are any torrents and any free slots + + // this loop will "hand out" max(connection_speed + // , half_open.free_slots()) to the torrents, in a + // round robin fashion, so that every torrent is + // equallt likely to connect to a peer + + if (!m_torrents.empty() && m_half_open.free_slots()) + { + // this is the maximum number of connections we will + // attempt this tick + int max_connections = m_settings.connection_speed; + + torrent_map::iterator i = m_torrents.begin(); + if (m_next_connect_torrent < int(m_torrents.size())) + std::advance(i, m_next_connect_torrent); + else + m_next_connect_torrent = 0; + int steps_since_last_connect = 0; + int num_torrents = int(m_torrents.size()); + for (;;) + { + torrent& t = *i->second; + if (t.want_more_peers()) + if (t.try_connect_peer()) + { + --max_connections; + steps_since_last_connect = 0; + } + ++m_next_connect_torrent; + ++steps_since_last_connect; + ++i; + if (i == m_torrents.end()) + { + assert(m_next_connect_torrent == num_torrents); + i = m_torrents.begin(); + m_next_connect_torrent = 0; + } + // if we have gone one whole loop without + // handing out a single connection, break + if (steps_since_last_connect > num_torrents) break; + // if there are no more free connection slots, abort + if (m_half_open.free_slots() == 0) break; + // if we should not make any more connections + // attempts this tick, abort + if (max_connections == 0) break; + } + } + + // do the second_tick() on each connection + // this will update their statistics (download and upload speeds) + // also purge sockets that have timed out + // and keep sockets open by keeping them alive. + for (connection_map::iterator i = m_connections.begin(); + i != m_connections.end();) + { + // we need to do like this because j->second->disconnect() will + // erase the connection from the map we're iterating + connection_map::iterator j = i; + ++i; + // if this socket has timed out + // close it. + peer_connection& c = *j->second; + if (c.has_timed_out()) + { + if (m_alerts.should_post(alert::debug)) + { + m_alerts.post_alert( + peer_error_alert( + c.remote() + , c.pid() + , "connection timed out")); + } +#if defined(TORRENT_VERBOSE_LOGGING) + (*c.m_logger) << "*** CONNECTION TIMED OUT\n"; +#endif + + c.set_failed(); + c.disconnect(); + continue; + } + + try + { + c.keep_alive(); + } + catch (std::exception& exc) + { +#ifdef TORRENT_VERBOSE_LOGGING + (*c.m_logger) << "**ERROR**: " << exc.what() << "\n"; +#endif + c.set_failed(); + c.disconnect(); + } + } + + // check each torrent for tracker updates + // TODO: do this in a timer-event in each torrent instead + for (torrent_map::iterator i = m_torrents.begin(); + i != m_torrents.end();) + { + torrent& t = *i->second; + assert(!t.is_aborted()); + if (t.should_request()) + { + tracker_request req = t.generate_tracker_request(); + req.listen_port = m_external_listen_port; + req.key = m_key; + m_tracker_manager.queue_request(m_strand, m_half_open, req + , t.tracker_login(), m_listen_interface.address(), i->second); + + if (m_alerts.should_post(alert::info)) + { + m_alerts.post_alert( + tracker_announce_alert( + t.get_handle(), "tracker announce")); + } + } + + // second_tick() will set the used upload quota + t.second_tick(m_stat, tick_interval); + ++i; + } + + m_stat.second_tick(tick_interval); + // distribute the maximum upload rate among the torrents + + assert(m_max_uploads >= -1); + assert(m_max_connections >= -1); + + allocate_resources(m_max_uploads == -1 + ? std::numeric_limits::max() + : m_max_uploads + , m_torrents + , &torrent::m_uploads_quota); + + allocate_resources(m_max_connections == -1 + ? std::numeric_limits::max() + : m_max_connections + , m_torrents + , &torrent::m_connections_quota); + + for (std::map >::iterator i + = m_torrents.begin(); i != m_torrents.end(); ++i) + { +#ifndef NDEBUG + i->second->check_invariant(); +#endif + i->second->distribute_resources(tick_interval); + } + } + catch (std::exception& exc) + { +#ifndef NDEBUG + std::cerr << exc.what() << std::endl; + assert(false); +#endif + }; // msvc 7.1 seems to require this +/* + void session_impl::connection_completed( + boost::intrusive_ptr const& p) try + { + mutex_t::scoped_lock l(m_mutex); + + connection_map::iterator i = m_half_open.find(p->get_socket()); + m_connections.insert(std::make_pair(p->get_socket(), p)); + assert(i != m_half_open.end()); + if (i != m_half_open.end()) m_half_open.erase(i); + + if (m_abort) return; + + process_connection_queue(); + } + catch (std::exception& e) + { +#ifndef NDEBUG + std::cerr << e.what() << std::endl; + assert(false); +#endif + }; +*/ + void session_impl::operator()() + { + eh_initializer(); + + if (m_listen_port_range.first != 0 && m_listen_port_range.second != 0) + { + session_impl::mutex_t::scoped_lock l(m_mutex); + open_listen_port(); + m_natpmp.set_mappings(m_listen_interface.port(), 0); + m_upnp.set_mappings(m_listen_interface.port(), 0); + } + + ptime timer = time_now(); + + do + { + try + { + m_io_service.run(); + assert(m_abort == true); + } + catch (std::exception& e) + { +#ifndef NDEBUG + std::cerr << e.what() << "\n"; + std::string err = e.what(); +#endif + assert(false); + } + } + while (!m_abort); + + deadline_timer tracker_timer(m_io_service); + // this will remove the port mappings + m_natpmp.close(); + m_upnp.close(); + +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + (*m_logger) << time_now_string() << " locking mutex\n"; +#endif + session_impl::mutex_t::scoped_lock l(m_mutex); + +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + (*m_logger) << time_now_string() << " aborting all tracker requests\n"; +#endif + m_tracker_manager.abort_all_requests(); +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + (*m_logger) << time_now_string() << " sending stopped to all torrent's trackers\n"; +#endif + for (std::map >::iterator i = + m_torrents.begin(); i != m_torrents.end(); ++i) + { + i->second->abort(); + // generate a tracker request in case the torrent is not paused + // (in which case it's not currently announced with the tracker) + // or if the torrent itself thinks we should request. Do not build + // a request in case the torrent doesn't have any trackers + if ((!i->second->is_paused() || i->second->should_request()) + && !i->second->trackers().empty()) + { + tracker_request req = i->second->generate_tracker_request(); + assert(m_external_listen_port > 0); + req.listen_port = m_external_listen_port; + req.key = m_key; + std::string login = i->second->tracker_login(); +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + boost::shared_ptr tl(new tracker_logger(*this)); + m_tracker_loggers.push_back(tl); + m_tracker_manager.queue_request(m_strand, m_half_open, req, login + , m_listen_interface.address(), tl); +#else + m_tracker_manager.queue_request(m_strand, m_half_open, req, login + , m_listen_interface.address()); +#endif + } + } + + ptime start(time_now()); + l.unlock(); + +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + (*m_logger) << time_now_string() << " waiting for trackers to respond (" + << m_settings.stop_tracker_timeout << " seconds timeout)\n"; +#endif + + while (time_now() - start < seconds( + m_settings.stop_tracker_timeout) + && !m_tracker_manager.empty()) + { + tracker_timer.expires_from_now(milliseconds(100)); + tracker_timer.async_wait(m_strand.wrap( + bind(&io_service::stop, &m_io_service))); + + m_io_service.reset(); + m_io_service.run(); + } + +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + (*m_logger) << time_now_string() << " tracker shutdown complete, locking mutex\n"; +#endif + + l.lock(); + assert(m_abort); + m_abort = true; + +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + (*m_logger) << time_now_string() << " cleaning up connections\n"; +#endif + while (!m_connections.empty()) + m_connections.begin()->second->disconnect(); + +#ifndef NDEBUG + for (torrent_map::iterator i = m_torrents.begin(); + i != m_torrents.end(); ++i) + { + assert(i->second->num_peers() == 0); + } +#endif + +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + (*m_logger) << time_now_string() << " cleaning up torrents\n"; +#endif + m_torrents.clear(); + + assert(m_torrents.empty()); + assert(m_connections.empty()); + } + + + // the return value from this function is valid only as long as the + // session is locked! + boost::weak_ptr session_impl::find_torrent(sha1_hash const& info_hash) + { + std::map >::iterator i + = m_torrents.find(info_hash); +#ifndef NDEBUG + for (std::map >::iterator j + = m_torrents.begin(); j != m_torrents.end(); ++j) + { + torrent* p = boost::get_pointer(j->second); + assert(p); + } +#endif + if (i != m_torrents.end()) return i->second; + return boost::weak_ptr(); + } + +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + boost::shared_ptr session_impl::create_log(std::string const& name + , int instance, bool append) + { + // current options are file_logger, cout_logger and null_logger + return boost::shared_ptr(new logger(name + ".log", instance, append)); + } +#endif + + std::vector session_impl::get_torrents() + { + mutex_t::scoped_lock l(m_mutex); + mutex::scoped_lock l2(m_checker_impl.m_mutex); + std::vector ret; + for (std::deque >::iterator i + = m_checker_impl.m_torrents.begin() + , end(m_checker_impl.m_torrents.end()); i != end; ++i) + { + if ((*i)->abort) continue; + ret.push_back(torrent_handle(this, &m_checker_impl + , (*i)->info_hash)); + } + + for (std::deque >::iterator i + = m_checker_impl.m_processing.begin() + , end(m_checker_impl.m_processing.end()); i != end; ++i) + { + if ((*i)->abort) continue; + ret.push_back(torrent_handle(this, &m_checker_impl + , (*i)->info_hash)); + } + + for (session_impl::torrent_map::iterator i + = m_torrents.begin(), end(m_torrents.end()); + i != end; ++i) + { + if (i->second->is_aborted()) continue; + ret.push_back(torrent_handle(this, &m_checker_impl + , i->first)); + } + return ret; + } + + torrent_handle session_impl::find_torrent_handle(sha1_hash const& info_hash) + { + return torrent_handle(this, &m_checker_impl, info_hash); + } + + torrent_handle session_impl::add_torrent( + torrent_info const& ti + , boost::filesystem::path const& save_path + , entry const& resume_data + , bool compact_mode + , int block_size + , storage_constructor_type sc) + { + // if you get this assert, you haven't managed to + // open a listen port. call listen_on() first. + assert(m_external_listen_port > 0); + + // make sure the block_size is an even power of 2 +#ifndef NDEBUG + for (int i = 0; i < 32; ++i) + { + if (block_size & (1 << i)) + { + assert((block_size & ~(1 << i)) == 0); + break; + } + } +#endif + + assert(!save_path.empty()); + + if (ti.begin_files() == ti.end_files()) + throw std::runtime_error("no files in torrent"); + + // lock the session and the checker thread (the order is important!) + mutex_t::scoped_lock l(m_mutex); + mutex::scoped_lock l2(m_checker_impl.m_mutex); + + if (is_aborted()) + throw std::runtime_error("session is closing"); + + // is the torrent already active? + if (!find_torrent(ti.info_hash()).expired()) + throw duplicate_torrent(); + + // is the torrent currently being checked? + if (m_checker_impl.find_torrent(ti.info_hash())) + throw duplicate_torrent(); + + // create the torrent and the data associated with + // the checker thread and store it before starting + // the thread + boost::shared_ptr torrent_ptr( + new torrent(*this, m_checker_impl, ti, save_path + , m_listen_interface, compact_mode, block_size + , settings(), sc)); + torrent_ptr->start(); + +#ifndef TORRENT_DISABLE_EXTENSIONS + for (extension_list_t::iterator i = m_extensions.begin() + , end(m_extensions.end()); i != end; ++i) + { + boost::shared_ptr tp((*i)(torrent_ptr.get())); + if (tp) torrent_ptr->add_extension(tp); + } +#endif + + boost::shared_ptr d( + new aux::piece_checker_data); + d->torrent_ptr = torrent_ptr; + d->save_path = save_path; + d->info_hash = ti.info_hash(); + d->resume_data = resume_data; + +#ifndef TORRENT_DISABLE_DHT + if (m_dht) + { + torrent_info::nodes_t const& nodes = ti.nodes(); + std::for_each(nodes.begin(), nodes.end(), bind( + (void(dht::dht_tracker::*)(std::pair const&)) + &dht::dht_tracker::add_node + , boost::ref(m_dht), _1)); + } +#endif + + // add the torrent to the queue to be checked + m_checker_impl.m_torrents.push_back(d); + // and notify the thread that it got another + // job in its queue + m_checker_impl.m_cond.notify_one(); + + return torrent_handle(this, &m_checker_impl, ti.info_hash()); + } + + torrent_handle session_impl::add_torrent( + char const* tracker_url + , sha1_hash const& info_hash + , char const* name + , boost::filesystem::path const& save_path + , entry const& + , bool compact_mode + , int block_size + , storage_constructor_type sc) + { + // make sure the block_size is an even power of 2 +#ifndef NDEBUG + for (int i = 0; i < 32; ++i) + { + if (block_size & (1 << i)) + { + assert((block_size & ~(1 << i)) == 0); + break; + } + } +#endif + + // TODO: support resume data in this case + assert(!save_path.empty()); + { + // lock the checker_thread + mutex::scoped_lock l(m_checker_impl.m_mutex); + + // is the torrent currently being checked? + if (m_checker_impl.find_torrent(info_hash)) + throw duplicate_torrent(); + } + + // lock the session + session_impl::mutex_t::scoped_lock l(m_mutex); + + // is the torrent already active? + if (!find_torrent(info_hash).expired()) + throw duplicate_torrent(); + + // you cannot add new torrents to a session that is closing down + assert(!is_aborted()); + + // create the torrent and the data associated with + // the checker thread and store it before starting + // the thread + boost::shared_ptr torrent_ptr( + new torrent(*this, m_checker_impl, tracker_url, info_hash, name + , save_path, m_listen_interface, compact_mode, block_size + , settings(), sc)); + torrent_ptr->start(); + +#ifndef TORRENT_DISABLE_EXTENSIONS + for (extension_list_t::iterator i = m_extensions.begin() + , end(m_extensions.end()); i != end; ++i) + { + boost::shared_ptr tp((*i)(torrent_ptr.get())); + if (tp) torrent_ptr->add_extension(tp); + } +#endif + + m_torrents.insert( + std::make_pair(info_hash, torrent_ptr)).first; + + return torrent_handle(this, &m_checker_impl, info_hash); + } + + void session_impl::remove_torrent(const torrent_handle& h) + { + if (h.m_ses != this) return; + assert(h.m_chk == &m_checker_impl || h.m_chk == 0); + assert(h.m_ses != 0); + + mutex_t::scoped_lock l(m_mutex); + session_impl::torrent_map::iterator i = + m_torrents.find(h.m_info_hash); + if (i != m_torrents.end()) + { + torrent& t = *i->second; + t.abort(); + + if ((!t.is_paused() || t.should_request()) + && !t.torrent_file().trackers().empty()) + { + tracker_request req = t.generate_tracker_request(); + assert(req.event == tracker_request::stopped); + assert(m_external_listen_port > 0); + req.listen_port = m_external_listen_port; + req.key = m_key; + +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + boost::shared_ptr tl(new tracker_logger(*this)); + m_tracker_loggers.push_back(tl); + m_tracker_manager.queue_request(m_strand, m_half_open, req + , t.tracker_login(), m_listen_interface.address(), tl); +#else + m_tracker_manager.queue_request(m_strand, m_half_open, req + , t.tracker_login(), m_listen_interface.address()); +#endif + + if (m_alerts.should_post(alert::info)) + { + m_alerts.post_alert( + tracker_announce_alert( + t.get_handle(), "tracker announce, event=stopped")); + } + } +#ifndef NDEBUG + sha1_hash i_hash = t.torrent_file().info_hash(); +#endif + m_torrents.erase(i); + assert(m_torrents.find(i_hash) == m_torrents.end()); + return; + } + l.unlock(); + + if (h.m_chk) + { + mutex::scoped_lock l(m_checker_impl.m_mutex); + + aux::piece_checker_data* d = m_checker_impl.find_torrent(h.m_info_hash); + if (d != 0) + { + if (d->processing) d->abort = true; + else m_checker_impl.remove_torrent(h.m_info_hash); + return; + } + } + } + + bool session_impl::listen_on( + std::pair const& port_range + , const char* net_interface) + { + session_impl::mutex_t::scoped_lock l(m_mutex); + + tcp::endpoint new_interface; + if (net_interface && std::strlen(net_interface) > 0) + new_interface = tcp::endpoint(address::from_string(net_interface), port_range.first); + else + new_interface = tcp::endpoint(address(), port_range.first); + + m_listen_port_range = port_range; + + // if the interface is the same and the socket is open + // don't do anything + if (new_interface == m_listen_interface + && m_listen_socket) return true; + + if (m_listen_socket) + m_listen_socket.reset(); + + m_incoming_connection = false; + m_listen_interface = new_interface; + + open_listen_port(); + + bool new_listen_address = m_listen_interface.address() != new_interface.address(); + + if (new_listen_address) + { + m_natpmp.rebind(new_interface.address()); + m_upnp.rebind(new_interface.address()); + m_lsd.rebind(new_interface.address()); + } + + m_natpmp.set_mappings(m_listen_interface.port(), 0); + m_upnp.set_mappings(m_listen_interface.port(), 0); + +#ifndef TORRENT_DISABLE_DHT + if ((new_listen_address || m_dht_same_port) && m_dht) + { + if (m_dht_same_port) + m_dht_settings.service_port = new_interface.port(); + // the listen interface changed, rebind the dht listen socket as well + m_dht->rebind(new_interface.address() + , m_dht_settings.service_port); + m_natpmp.set_mappings(0, m_dht_settings.service_port); + m_upnp.set_mappings(0, m_dht_settings.service_port); + } +#endif + +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + m_logger = create_log("main_session", listen_port(), false); + (*m_logger) << time_now_string() << "\n"; +#endif + + return m_listen_socket; + } + + unsigned short session_impl::listen_port() const + { + mutex_t::scoped_lock l(m_mutex); + return m_external_listen_port; + } + + void session_impl::announce_lsd(sha1_hash const& ih) + { + mutex_t::scoped_lock l(m_mutex); + // use internal listen port for local peers + m_lsd.announce(ih, m_listen_interface.port()); + } + + void session_impl::on_lsd_peer(tcp::endpoint peer, sha1_hash const& ih) + { + mutex_t::scoped_lock l(m_mutex); + + boost::shared_ptr t = find_torrent(ih).lock(); + if (!t) return; + +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + (*m_logger) << time_now_string() + << ": added peer from local discovery: " << peer << "\n"; +#endif + t->get_policy().peer_from_tracker(peer, peer_id(0), peer_info::lsd, 0); + } + + void session_impl::on_port_mapping(int tcp_port, int udp_port + , std::string const& errmsg) + { +#ifndef TORRENT_DISABLE_DHT + if (udp_port != 0) + { + m_external_udp_port = udp_port; + m_dht_settings.service_port = udp_port; + if (m_alerts.should_post(alert::info)) + { + std::stringstream msg; + msg << "successfully mapped UDP port " << udp_port; + m_alerts.post_alert(portmap_alert(msg.str())); + } + } +#endif + + if (tcp_port != 0) + { + m_external_listen_port = tcp_port; + if (m_alerts.should_post(alert::info)) + { + std::stringstream msg; + msg << "successfully mapped TCP port " << tcp_port; + m_alerts.post_alert(portmap_alert(msg.str())); + } + } + + if (!errmsg.empty()) + { + if (m_alerts.should_post(alert::warning)) + { + std::stringstream msg; + msg << "Error while mapping ports on NAT router: " << errmsg; + m_alerts.post_alert(portmap_error_alert(msg.str())); + } + } + } + + session_status session_impl::status() const + { + mutex_t::scoped_lock l(m_mutex); + session_status s; + s.has_incoming_connections = m_incoming_connection; + s.num_peers = (int)m_connections.size(); + + s.download_rate = m_stat.download_rate(); + s.upload_rate = m_stat.upload_rate(); + + s.payload_download_rate = m_stat.download_payload_rate(); + s.payload_upload_rate = m_stat.upload_payload_rate(); + + s.total_download = m_stat.total_protocol_download() + + m_stat.total_payload_download(); + + s.total_upload = m_stat.total_protocol_upload() + + m_stat.total_payload_upload(); + + s.total_payload_download = m_stat.total_payload_download(); + s.total_payload_upload = m_stat.total_payload_upload(); + +#ifndef TORRENT_DISABLE_DHT + if (m_dht) + { + m_dht->dht_status(s); + } + else + { + s.dht_nodes = 0; + s.dht_node_cache = 0; + s.dht_torrents = 0; + s.dht_global_nodes = 0; + } +#endif + + return s; + } + +#ifndef TORRENT_DISABLE_DHT + + void session_impl::start_dht(entry const& startup_state) + { + mutex_t::scoped_lock l(m_mutex); + if (m_dht) + { + m_dht->stop(); + m_dht = 0; + } + if (m_dht_settings.service_port == 0 + || m_dht_same_port) + { + m_dht_same_port = true; + // if you hit this assert you are trying to start the + // DHT with the same port as the tcp listen port + // (which is default) _before_ you have opened the + // tcp listen port (so there is no configured port to use) + // basically, make sure you call listen_on() before + // start_dht(). See documentation for listen_on() for + // more information. + assert(m_listen_interface.port() > 0); + m_dht_settings.service_port = m_listen_interface.port(); + } + m_external_udp_port = m_dht_settings.service_port; + m_natpmp.set_mappings(0, m_dht_settings.service_port); + m_upnp.set_mappings(0, m_dht_settings.service_port); + m_dht = new dht::dht_tracker(m_io_service + , m_dht_settings, m_listen_interface.address() + , startup_state); + } + + void session_impl::stop_dht() + { + mutex_t::scoped_lock l(m_mutex); + if (!m_dht) return; + m_dht->stop(); + m_dht = 0; + } + + void session_impl::set_dht_settings(dht_settings const& settings) + { + mutex_t::scoped_lock l(m_mutex); + // only change the dht listen port in case the settings + // contains a vaiid port, and if it is different from + // the current setting + if (settings.service_port != 0) + m_dht_same_port = false; + else + m_dht_same_port = true; + if (!m_dht_same_port + && settings.service_port != m_dht_settings.service_port + && m_dht) + { + m_dht->rebind(m_listen_interface.address() + , settings.service_port); + m_natpmp.set_mappings(0, m_dht_settings.service_port); + m_upnp.set_mappings(0, m_dht_settings.service_port); + m_external_udp_port = settings.service_port; + } + m_dht_settings = settings; + if (m_dht_same_port) + m_dht_settings.service_port = m_listen_interface.port(); + } + + entry session_impl::dht_state() const + { + assert(m_dht); + mutex_t::scoped_lock l(m_mutex); + return m_dht->state(); + } + + void session_impl::add_dht_node(std::pair const& node) + { + assert(m_dht); + mutex_t::scoped_lock l(m_mutex); + m_dht->add_node(node); + } + + void session_impl::add_dht_router(std::pair const& node) + { + assert(m_dht); + mutex_t::scoped_lock l(m_mutex); + m_dht->add_router_node(node); + } + +#endif + +#ifndef TORRENT_DISABLE_ENCRYPTION + void session_impl::set_pe_settings(pe_settings const& settings) + { + mutex_t::scoped_lock l(m_mutex); + m_pe_settings = settings; + } +#endif + + void session_impl::set_download_rate_limit(int bytes_per_second) + { + assert(bytes_per_second > 0 || bytes_per_second == -1); + mutex_t::scoped_lock l(m_mutex); + if (bytes_per_second == -1) bytes_per_second = bandwidth_limit::inf; + m_dl_bandwidth_manager.throttle(bytes_per_second); + } + + bool session_impl::is_listening() const + { + mutex_t::scoped_lock l(m_mutex); + return m_listen_socket; + } + + session_impl::~session_impl() + { +#ifndef TORRENT_DISABLE_DHT + stop_dht(); +#endif + +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + (*m_logger) << time_now_string() << "\n\n *** shutting down session *** \n\n"; +#endif + // lock the main thread and abort it + mutex_t::scoped_lock l(m_mutex); + m_abort = true; + m_io_service.stop(); + l.unlock(); + +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + (*m_logger) << time_now_string() << " waiting for main thread\n"; +#endif + m_thread->join(); + + assert(m_torrents.empty()); + + // it's important that the main thread is closed completely before + // the checker thread is terminated. Because all the connections + // have to be closed and removed from the torrents before they + // can be destructed. (because the weak pointers in the + // peer_connections will be invalidated when the torrents are + // destructed and then the invariant will be broken). + + { + mutex::scoped_lock l(m_checker_impl.m_mutex); + // abort the checker thread + m_checker_impl.m_abort = true; + + // abort the currently checking torrent + if (!m_checker_impl.m_torrents.empty()) + { + m_checker_impl.m_torrents.front()->abort = true; + } + m_checker_impl.m_cond.notify_one(); + } + +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + (*m_logger) << time_now_string() << " waiting for checker thread\n"; +#endif + m_checker_thread->join(); + + assert(m_torrents.empty()); + assert(m_connections.empty()); +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + (*m_logger) << time_now_string() << " shutdown complete!\n"; +#endif + } + + void session_impl::set_max_uploads(int limit) + { + assert(limit > 0 || limit == -1); + mutex_t::scoped_lock l(m_mutex); + m_max_uploads = limit; + } + + void session_impl::set_max_connections(int limit) + { + assert(limit > 0 || limit == -1); + mutex_t::scoped_lock l(m_mutex); + m_max_connections = limit; + } + + void session_impl::set_max_half_open_connections(int limit) + { + assert(limit > 0 || limit == -1); + mutex_t::scoped_lock l(m_mutex); + + m_half_open.limit(limit); + } + + void session_impl::set_upload_rate_limit(int bytes_per_second) + { + assert(bytes_per_second > 0 || bytes_per_second == -1); + mutex_t::scoped_lock l(m_mutex); + if (bytes_per_second == -1) bytes_per_second = bandwidth_limit::inf; + m_ul_bandwidth_manager.throttle(bytes_per_second); + } + + int session_impl::num_uploads() const + { + int uploads = 0; + mutex_t::scoped_lock l(m_mutex); + for (torrent_map::const_iterator i = m_torrents.begin() + , end(m_torrents.end()); i != end; i++) + { + uploads += i->second->get_policy().num_uploads(); + } + return uploads; + } + + int session_impl::num_connections() const + { + mutex_t::scoped_lock l(m_mutex); + return m_connections.size(); + } + + + std::auto_ptr session_impl::pop_alert() + { + mutex_t::scoped_lock l(m_mutex); + if (m_alerts.pending()) + return m_alerts.get(); + return std::auto_ptr(0); + } + + void session_impl::set_severity_level(alert::severity_t s) + { + mutex_t::scoped_lock l(m_mutex); + m_alerts.set_severity(s); + } + + int session_impl::upload_rate_limit() const + { + mutex_t::scoped_lock l(m_mutex); + return m_ul_bandwidth_manager.throttle(); + } + + int session_impl::download_rate_limit() const + { + mutex_t::scoped_lock l(m_mutex); + return m_dl_bandwidth_manager.throttle(); + } + +#ifndef NDEBUG + void session_impl::check_invariant(const char *place) + { + assert(place); + for (connection_map::iterator i = m_connections.begin(); + i != m_connections.end(); ++i) + { + assert(i->second); + boost::shared_ptr t = i->second->associated_torrent().lock(); + + if (t) + { + assert(t->get_policy().has_connection(boost::get_pointer(i->second))); + } + } + } +#endif + + void piece_checker_data::parse_resume_data( + const entry& resume_data + , const torrent_info& info + , std::string& error) + { + // if we don't have any resume data, return + if (resume_data.type() == entry::undefined_t) return; + + entry rd = resume_data; + + try + { + if (rd["file-format"].string() != "libtorrent resume file") + { + error = "missing file format tag"; + return; + } + + if (rd["file-version"].integer() > 1) + { + error = "incompatible file version " + + boost::lexical_cast(rd["file-version"].integer()); + return; + } + + // verify info_hash + sha1_hash hash = rd["info-hash"].string(); + if (hash != info.info_hash()) + { + error = "mismatching info-hash: " + boost::lexical_cast(hash); + return; + } + + // the peers + + if (rd.find_key("peers")) + { + entry::list_type& peer_list = rd["peers"].list(); + + std::vector tmp_peers; + tmp_peers.reserve(peer_list.size()); + for (entry::list_type::iterator i = peer_list.begin(); + i != peer_list.end(); ++i) + { + tcp::endpoint a( + address::from_string((*i)["ip"].string()) + , (unsigned short)(*i)["port"].integer()); + tmp_peers.push_back(a); + } + + peers.swap(tmp_peers); + } + + // read piece map + const entry::list_type& slots = rd["slots"].list(); + if ((int)slots.size() > info.num_pieces()) + { + error = "file has more slots than torrent (slots: " + + boost::lexical_cast(slots.size()) + " size: " + + boost::lexical_cast(info.num_pieces()) + " )"; + return; + } + + std::vector tmp_pieces; + tmp_pieces.reserve(slots.size()); + for (entry::list_type::const_iterator i = slots.begin(); + i != slots.end(); ++i) + { + int index = (int)i->integer(); + if (index >= info.num_pieces() || index < -2) + { + error = "too high index number in slot map (index: " + + boost::lexical_cast(index) + " size: " + + boost::lexical_cast(info.num_pieces()) + ")"; + return; + } + tmp_pieces.push_back(index); + } + + // only bother to check the partial pieces if we have the same block size + // as in the fast resume data. If the blocksize has changed, then throw + // away all partial pieces. + std::vector tmp_unfinished; + int num_blocks_per_piece = (int)rd["blocks per piece"].integer(); + if (num_blocks_per_piece == info.piece_length() / torrent_ptr->block_size()) + { + // the unfinished pieces + + entry::list_type& unfinished = rd["unfinished"].list(); + int unfinished_size = int(unfinished.size()); + block_info.resize(num_blocks_per_piece * unfinished_size); + tmp_unfinished.reserve(unfinished_size); + int index = 0; + for (entry::list_type::iterator i = unfinished.begin(); + i != unfinished.end(); ++i, ++index) + { + piece_picker::downloading_piece p; + p.info = &block_info[index * num_blocks_per_piece]; + p.index = (int)(*i)["piece"].integer(); + if (p.index < 0 || p.index >= info.num_pieces()) + { + error = "invalid piece index in unfinished piece list (index: " + + boost::lexical_cast(p.index) + " size: " + + boost::lexical_cast(info.num_pieces()) + ")"; + return; + } + + const std::string& bitmask = (*i)["bitmask"].string(); + + const int num_bitmask_bytes = std::max(num_blocks_per_piece / 8, 1); + if ((int)bitmask.size() != num_bitmask_bytes) + { + error = "invalid size of bitmask (" + boost::lexical_cast(bitmask.size()) + ")"; + return; + } + for (int j = 0; j < num_bitmask_bytes; ++j) + { + unsigned char bits = bitmask[j]; + for (int k = 0; k < 8; ++k) + { + const int bit = j * 8 + k; + if (bits & (1 << k)) + { + p.info[bit].finished = true; + ++p.finished; + } + } + } + + if (p.finished == 0) continue; + + std::vector::iterator slot_iter + = std::find(tmp_pieces.begin(), tmp_pieces.end(), p.index); + if (slot_iter == tmp_pieces.end()) + { + // this piece is marked as unfinished + // but doesn't have any storage + error = "piece " + boost::lexical_cast(p.index) + " is " + "marked as unfinished, but doesn't have any storage"; + return; + } + + assert(*slot_iter == p.index); + int slot_index = static_cast(slot_iter - tmp_pieces.begin()); + unsigned long adler + = torrent_ptr->filesystem().piece_crc( + slot_index + , torrent_ptr->block_size() + , p.info); + + const entry& ad = (*i)["adler32"]; + + // crc's didn't match, don't use the resume data + if (ad.integer() != entry::integer_type(adler)) + { + error = "checksum mismatch on piece " + + boost::lexical_cast(p.index); + return; + } + + tmp_unfinished.push_back(p); + } + } + + if (!torrent_ptr->verify_resume_data(rd, error)) + return; + + piece_map.swap(tmp_pieces); + unfinished_pieces.swap(tmp_unfinished); + } + catch (invalid_encoding&) + { + return; + } + catch (type_error&) + { + return; + } + catch (file_error&) + { + return; + } + } +}} + diff --git a/encryption/libtorrent/src/sha1.cpp b/encryption/libtorrent/src/sha1.cpp new file mode 100755 index 000000000..a7ea67113 --- /dev/null +++ b/encryption/libtorrent/src/sha1.cpp @@ -0,0 +1,317 @@ +/* +SHA-1 C++ conversion + +original version: + +SHA-1 in C +By Steve Reid +100% Public Domain + +changelog at the end of the file. +*/ + +#include "libtorrent/pch.hpp" + +#include +#include + +// if you don't want boost +// replace with +// #include + +#include +using boost::uint32_t; +using boost::uint8_t; + +#include "libtorrent/config.hpp" + +struct TORRENT_EXPORT SHA_CTX +{ + uint32_t state[5]; + uint32_t count[2]; + uint8_t buffer[64]; +}; + +TORRENT_EXPORT void SHA1_Init(SHA_CTX* context); +TORRENT_EXPORT void SHA1_Update(SHA_CTX* context, uint8_t const* data, uint32_t len); +TORRENT_EXPORT void SHA1_Final(uint8_t* digest, SHA_CTX* context); + +namespace +{ + union CHAR64LONG16 + { + uint8_t c[64]; + uint32_t l[16]; + }; + +#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) + +// blk0() and blk() perform the initial expand. +// I got the idea of expanding during the round function from SSLeay + struct little_endian_blk0 + { + static uint32_t apply(CHAR64LONG16* block, int i) + { + return block->l[i] = (rol(block->l[i],24)&0xFF00FF00) + | (rol(block->l[i],8)&0x00FF00FF); + } + }; + + struct big_endian_blk0 + { + static uint32_t apply(CHAR64LONG16* block, int i) + { + return block->l[i]; + } + }; + + +#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \ + ^block->l[(i+2)&15]^block->l[i&15],1)) + +// (R0+R1), R2, R3, R4 are the different operations used in SHA1 +#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+BlkFun::apply(block, i)+0x5A827999+rol(v,5);w=rol(w,30); +#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30); +#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30); +#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30); +#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30); + + // Hash a single 512-bit block. This is the core of the algorithm. + template + void SHA1Transform(uint32_t state[5], uint8_t const buffer[64]) + { + using namespace std; + uint32_t a, b, c, d, e; + + CHAR64LONG16* block; + uint8_t workspace[64]; + block = (CHAR64LONG16*)workspace; + memcpy(block, buffer, 64); + + // Copy context->state[] to working vars + a = state[0]; + b = state[1]; + c = state[2]; + d = state[3]; + e = state[4]; + // 4 rounds of 20 operations each. Loop unrolled. + R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3); + R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7); + R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11); + R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15); + R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); + R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23); + R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27); + R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31); + R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35); + R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39); + R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43); + R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47); + R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51); + R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55); + R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59); + R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63); + R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67); + R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71); + R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75); + R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79); + // Add the working vars back into context.state[] + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + state[4] += e; + // Wipe variables + a = b = c = d = e = 0; + } + + void SHAPrintContext(SHA_CTX *context, char *msg) + { + using namespace std; + printf("%s (%d,%d) %x %x %x %x %x\n" + , msg, context->count[0], context->count[1] + , context->state[0], context->state[1] + , context->state[2], context->state[3] + , context->state[4]); + } + + template + void internal_update(SHA_CTX* context, uint8_t const* data, uint32_t len) + { + using namespace std; + uint32_t i, j; // JHB + +#ifdef VERBOSE + SHAPrintContext(context, "before"); +#endif + j = (context->count[0] >> 3) & 63; + if ((context->count[0] += len << 3) < (len << 3)) context->count[1]++; + context->count[1] += (len >> 29); + if ((j + len) > 63) + { + memcpy(&context->buffer[j], data, (i = 64-j)); + SHA1Transform(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 + } + + bool is_big_endian() + { + uint32_t test = 1; + return *reinterpret_cast(&test) == 0; + } +} + +// 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, uint8_t const* data, uint32_t len) +{ +#if defined __BIG_ENDIAN__ + internal_update(context, data, len); +#elif defined LITTLE_ENDIAN + 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(uint8_t* digest, SHA_CTX* context) +{ + uint8_t finalcount[8]; + + for (uint32_t 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, (uint8_t const*)"\200", 1); + while ((context->count[0] & 504) != 448) + SHA1_Update(context, (uint8_t const*)"\0", 1); + SHA1_Update(context, finalcount, 8); // Should cause a SHA1Transform() + + for (uint32_t i = 0; i < 20; ++i) + { + digest[i] = static_cast( + (context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255); + } +} + +/************************************************************ + +----------------- +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 __BIG_ENDIAN__ or LITTLE_ENDIAN + are defined, endianess is determined + at runtime. templates are used to duplicate + the transform function for each endianess +4- using anonymous namespace to avoid external + linkage on internal functions +5- using standard C++ includes +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 +*/ diff --git a/encryption/libtorrent/src/socks5_stream.cpp b/encryption/libtorrent/src/socks5_stream.cpp new file mode 100644 index 000000000..b1679c4ac --- /dev/null +++ b/encryption/libtorrent/src/socks5_stream.cpp @@ -0,0 +1,315 @@ +/* + +Copyright (c) 2007, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/pch.hpp" + +#include "libtorrent/socks5_stream.hpp" + +namespace libtorrent +{ + + void socks5_stream::name_lookup(asio::error_code const& e, tcp::resolver::iterator i + , boost::shared_ptr h) + { + if (e || i == tcp::resolver::iterator()) + { + (*h)(e); + close(); + return; + } + + m_sock.async_connect(i->endpoint(), boost::bind( + &socks5_stream::connected, this, _1, h)); + } + + void socks5_stream::connected(asio::error_code const& e, boost::shared_ptr h) + { + if (e) + { + (*h)(e); + close(); + return; + } + + using namespace libtorrent::detail; + // 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 + } + asio::async_write(m_sock, asio::buffer(m_buffer) + , boost::bind(&socks5_stream::handshake1, this, _1, h)); + } + + void socks5_stream::handshake1(asio::error_code const& e, boost::shared_ptr h) + { + if (e) + { + (*h)(e); + close(); + return; + } + + m_buffer.resize(2); + asio::async_read(m_sock, asio::buffer(m_buffer) + , boost::bind(&socks5_stream::handshake2, this, _1, h)); + } + + void socks5_stream::handshake2(asio::error_code const& e, boost::shared_ptr h) + { + if (e) + { + (*h)(e); + close(); + return; + } + + using namespace libtorrent::detail; + + char* p = &m_buffer[0]; + int version = read_uint8(p); + int method = read_uint8(p); + + if (version < 5) + { + (*h)(asio::error::operation_not_supported); + close(); + return; + } + + if (method == 0) + { + socks_connect(h); + } + else if (method == 2) + { + if (m_user.empty()) + { + (*h)(asio::error::operation_not_supported); + close(); + return; + } + + // start sub-negotiation + m_buffer.resize(m_user.size() + m_password.size() + 3); + char* 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); + asio::async_write(m_sock, asio::buffer(m_buffer) + , boost::bind(&socks5_stream::handshake3, this, _1, h)); + } + else + { + (*h)(asio::error::operation_not_supported); + close(); + return; + } + } + + void socks5_stream::handshake3(asio::error_code const& e + , boost::shared_ptr h) + { + if (e) + { + (*h)(e); + close(); + return; + } + + m_buffer.resize(2); + asio::async_read(m_sock, asio::buffer(m_buffer) + , boost::bind(&socks5_stream::handshake4, this, _1, h)); + } + + void socks5_stream::handshake4(asio::error_code const& e + , boost::shared_ptr h) + { + if (e) + { + (*h)(e); + close(); + return; + } + + using namespace libtorrent::detail; + + char* p = &m_buffer[0]; + int version = read_uint8(p); + int status = read_uint8(p); + + if (version != 1) + { + (*h)(asio::error::operation_not_supported); + close(); + return; + } + + if (status != 0) + { + (*h)(asio::error::operation_not_supported); + close(); + return; + } + + std::vector().swap(m_buffer); + (*h)(e); + } + + void socks5_stream::socks_connect(boost::shared_ptr h) + { + using namespace libtorrent::detail; + + // send SOCKS5 connect command + m_buffer.resize(6 + (m_remote_endpoint.address().is_v4()?4:16)); + char* p = &m_buffer[0]; + write_uint8(5, p); // SOCKS VERSION 5 + write_uint8(1, p); // CONNECT command + write_uint8(0, p); // reserved + 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); + assert(p - &m_buffer[0] == int(m_buffer.size())); + + asio::async_write(m_sock, asio::buffer(m_buffer) + , boost::bind(&socks5_stream::connect1, this, _1, h)); + } + + void socks5_stream::connect1(asio::error_code const& e, boost::shared_ptr h) + { + if (e) + { + (*h)(e); + close(); + return; + } + + m_buffer.resize(6 + 4); // assume an IPv4 address + asio::async_read(m_sock, asio::buffer(m_buffer) + , boost::bind(&socks5_stream::connect2, this, _1, h)); + } + + void socks5_stream::connect2(asio::error_code const& e, boost::shared_ptr h) + { + if (e) + { + (*h)(e); + close(); + return; + } + + using namespace libtorrent::detail; + + // send SOCKS5 connect command + char* p = &m_buffer[0]; + int version = read_uint8(p); + if (version < 5) + { + (*h)(asio::error::operation_not_supported); + close(); + return; + } + int response = read_uint8(p); + if (response != 0) + { + asio::error_code e = asio::error::fault; + switch (response) + { + case 1: e = asio::error::fault; break; + case 2: e = asio::error::no_permission; break; + case 3: e = asio::error::network_unreachable; break; + case 4: e = asio::error::host_unreachable; break; + case 5: e = asio::error::connection_refused; break; + case 6: e = asio::error::timed_out; break; + case 7: e = asio::error::operation_not_supported; break; + case 8: e = asio::error::address_family_not_supported; break; + } + (*h)(e); + close(); + return; + } + p += 1; // reserved + int atyp = read_uint8(p); + // we ignore the proxy IP it was bound to + if (atyp == 1) + { + std::vector().swap(m_buffer); + (*h)(e); + return; + } + int skip_bytes = 0; + if (atyp == 4) + { + skip_bytes = 12; + } + else if (atyp == 3) + { + skip_bytes = read_uint8(p) - 3; + } + else + { + (*h)(asio::error::operation_not_supported); + close(); + return; + } + m_buffer.resize(skip_bytes); + + asio::async_read(m_sock, asio::buffer(m_buffer) + , boost::bind(&socks5_stream::connect3, this, _1, h)); + } + + void socks5_stream::connect3(asio::error_code const& e, boost::shared_ptr h) + { + if (e) + { + (*h)(e); + close(); + return; + } + + std::vector().swap(m_buffer); + (*h)(e); + } +} + diff --git a/encryption/libtorrent/src/stat.cpp b/encryption/libtorrent/src/stat.cpp new file mode 100755 index 000000000..d695edc42 --- /dev/null +++ b/encryption/libtorrent/src/stat.cpp @@ -0,0 +1,93 @@ +/* + +Copyright (c) 2003, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +// TODO: Use two algorithms to estimate transfer rate. +// one (simple) for transfer rates that are >= 1 packet +// per second and one (low pass-filter) for rates < 1 +// packet per second. + +#include "libtorrent/pch.hpp" + +#include +#include + +#include "libtorrent/stat.hpp" +#include "libtorrent/invariant_check.hpp" + +#if defined _MSC_VER && _MSC_VER <= 1200 +#define for if (false) {} else for +#endif + +using namespace libtorrent; + +void libtorrent::stat::second_tick(float tick_interval) +{ + INVARIANT_CHECK; + + for (int i = history - 2; i >= 0; --i) + { + m_download_rate_history[i + 1] = m_download_rate_history[i]; + m_upload_rate_history[i + 1] = m_upload_rate_history[i]; + m_download_payload_rate_history[i + 1] = m_download_payload_rate_history[i]; + m_upload_payload_rate_history[i + 1] = m_upload_payload_rate_history[i]; + } + + m_download_rate_history[0] = (m_downloaded_payload + m_downloaded_protocol) + / tick_interval; + m_upload_rate_history[0] = (m_uploaded_payload + m_uploaded_protocol) + / tick_interval; + m_download_payload_rate_history[0] = m_downloaded_payload / tick_interval; + m_upload_payload_rate_history[0] = m_uploaded_payload / tick_interval; + + m_downloaded_payload = 0; + m_uploaded_payload = 0; + m_downloaded_protocol = 0; + m_uploaded_protocol = 0; + + m_mean_download_rate = 0; + m_mean_upload_rate = 0; + m_mean_download_payload_rate = 0; + m_mean_upload_payload_rate = 0; + + for (int i = 0; i < history; ++i) + { + m_mean_download_rate += m_download_rate_history[i]; + m_mean_upload_rate += m_upload_rate_history[i]; + m_mean_download_payload_rate += m_download_payload_rate_history[i]; + m_mean_upload_payload_rate += m_upload_payload_rate_history[i]; + } + + m_mean_download_rate /= history; + m_mean_upload_rate /= history; + m_mean_download_payload_rate /= history; + m_mean_upload_payload_rate /= history; +} diff --git a/encryption/libtorrent/src/storage.cpp b/encryption/libtorrent/src/storage.cpp new file mode 100755 index 000000000..0623dfeb6 --- /dev/null +++ b/encryption/libtorrent/src/storage.cpp @@ -0,0 +1,2325 @@ +/* + +Copyright (c) 2003, Arvid Norberg, Daniel Wallin +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/pch.hpp" + +#include +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/storage.hpp" +#include "libtorrent/torrent.hpp" +#include "libtorrent/hasher.hpp" +#include "libtorrent/session.hpp" +#include "libtorrent/peer_id.hpp" +#include "libtorrent/file.hpp" +#include "libtorrent/invariant_check.hpp" +#include "libtorrent/file_pool.hpp" +#include "libtorrent/aux_/session_impl.hpp" + +#ifndef NDEBUG +#include +#include +#include +#include +#endif + +#if defined(__APPLE__) +// for getattrlist() +#include +#include +// for statfs() +#include +#include +#endif + +#if defined(__linux__) +#include +#endif + +#if defined(_WIN32) && defined(UNICODE) + +#include +#include +#include "libtorrent/utf8.hpp" + +namespace libtorrent +{ + std::wstring safe_convert(std::string const& s) + { + try + { + return libtorrent::utf8_wchar(s); + } + catch (std::exception) + { + std::wstring ret; + const char* end = &s[0] + s.size(); + for (const char* i = &s[0]; i < end;) + { + wchar_t c = '.'; + int result = std::mbtowc(&c, i, end - i); + if (result > 0) i += result; + else ++i; + ret += c; + } + return ret; + } + } +} +#endif + +#if defined(_WIN32) && defined(UNICODE) && BOOST_VERSION < 103400 +namespace +{ + using libtorrent::safe_convert; + using namespace boost::filesystem; + + // based on code from Boost.Fileystem + bool create_directories_win(const path& ph) + { + if (ph.empty() || exists(ph)) + { + if ( !ph.empty() && !is_directory(ph) ) + boost::throw_exception( filesystem_error( + "boost::filesystem::create_directories", + ph, "path exists and is not a directory", + not_directory_error ) ); + return false; + } + + // First create branch, by calling ourself recursively + create_directories_win(ph.branch_path()); + // Now that parent's path exists, create the directory + std::wstring wph(safe_convert(ph.native_directory_string())); + CreateDirectory(wph.c_str(), 0); + return true; + } + + bool exists_win( const path & ph ) + { + std::wstring wpath(safe_convert(ph.string())); + if(::GetFileAttributes( wpath.c_str() ) == 0xFFFFFFFF) + { + UINT err = ::GetLastError(); + if((err == ERROR_FILE_NOT_FOUND) + || (err == ERROR_INVALID_PARAMETER) + || (err == ERROR_NOT_READY) + || (err == ERROR_PATH_NOT_FOUND) + || (err == ERROR_INVALID_NAME) + || (err == ERROR_BAD_NETPATH )) + return false; // GetFileAttributes failed because the path does not exist + // for any other error we assume the file does exist and fall through, + // this may not be the best policy though... (JM 20040330) + return true; + } + return true; + } + + boost::intmax_t file_size_win( const path & ph ) + { + std::wstring wpath(safe_convert(ph.string())); + // by now, intmax_t is 64-bits on all Windows compilers + WIN32_FILE_ATTRIBUTE_DATA fad; + if ( !::GetFileAttributesExW( wpath.c_str(), + ::GetFileExInfoStandard, &fad ) ) + boost::throw_exception( filesystem_error( + "boost::filesystem::file_size", + ph, detail::system_error_code() ) ); + if ( (fad.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) !=0 ) + boost::throw_exception( filesystem_error( + "boost::filesystem::file_size", + ph, "invalid: is a directory", + is_directory_error ) ); + return (static_cast(fad.nFileSizeHigh) + << (sizeof(fad.nFileSizeLow)*8)) + + fad.nFileSizeLow; + } + + std::time_t last_write_time_win( const path & ph ) + { + struct _stat path_stat; + std::wstring wph(safe_convert(ph.native_file_string())); + if ( ::_wstat( wph.c_str(), &path_stat ) != 0 ) + boost::throw_exception( filesystem_error( + "boost::filesystem::last_write_time", + ph, detail::system_error_code() ) ); + return path_stat.st_mtime; + } + + void rename_win( const path & old_path, + const path & new_path ) + { + std::wstring wold_path(safe_convert(old_path.string())); + std::wstring wnew_path(safe_convert(new_path.string())); + if ( !::MoveFile( wold_path.c_str(), wnew_path.c_str() ) ) + boost::throw_exception( filesystem_error( + "boost::filesystem::rename", + old_path, new_path, detail::system_error_code() ) ); + } + +} // anonymous namespace + +#endif + +#if BOOST_VERSION < 103200 +bool operator<(boost::filesystem::path const& lhs + , boost::filesystem::path const& rhs) +{ + return lhs.string() < rhs.string(); +} +#endif + +using namespace boost::filesystem; +using boost::bind; +using namespace ::boost::multi_index; +using boost::multi_index::multi_index_container; + +#if !defined(NDEBUG) && defined(TORRENT_STORAGE_DEBUG) +namespace +{ + using namespace libtorrent; + + void print_to_log(const std::string& s) + { + static std::ofstream log("log.txt"); + log << s; + log.flush(); + } +} +#endif + +namespace libtorrent +{ + + std::vector > get_filesizes( + torrent_info const& t, path p) + { + p = complete(p); + std::vector > sizes; + for (torrent_info::file_iterator i = t.begin_files(); + i != t.end_files(); ++i) + { + size_type size = 0; + std::time_t time = 0; + try + { + path f = p / i->path; +#if defined(_WIN32) && defined(UNICODE) && BOOST_VERSION < 103400 + size = file_size_win(f); + time = last_write_time_win(f); +#else + size = file_size(f); + time = last_write_time(f); +#endif + } + catch (std::exception&) {} + sizes.push_back(std::make_pair(size, time)); + } + return sizes; + } + + // matches the sizes and timestamps of the files passed in + // in non-compact mode, actual file sizes and timestamps + // are allowed to be bigger and more recent than the fast + // resume data. This is because full allocation will not move + // pieces, so any older version of the resume data will + // still be a correct subset of the actual data on disk. + bool match_filesizes( + torrent_info const& t + , path p + , std::vector > const& sizes + , bool compact_mode + , std::string* error) + { + if ((int)sizes.size() != t.num_files()) + { + if (error) *error = "mismatching number of files"; + return false; + } + p = complete(p); + + std::vector >::const_iterator s + = sizes.begin(); + for (torrent_info::file_iterator i = t.begin_files(); + i != t.end_files(); ++i, ++s) + { + size_type size = 0; + std::time_t time = 0; + try + { + path f = p / i->path; +#if defined(_WIN32) && defined(UNICODE) && BOOST_VERSION < 103400 + size = file_size_win(f); + time = last_write_time_win(f); +#else + size = file_size(f); + time = last_write_time(f); +#endif + } + catch (std::exception&) {} + if (size != s->first + || (!compact_mode && size < s->first)) + { + if (error) *error = "filesize mismatch for file '" + + i->path.native_file_string() + + "', size: " + boost::lexical_cast(size) + + ", expected to be " + boost::lexical_cast(s->first) + + " bytes"; + return false; + } + if (time != s->second + || (!compact_mode && time < s->second)) + { + if (error) *error = "timestamp mismatch for file '" + + i->path.native_file_string() + + "', modification date: " + boost::lexical_cast(time) + + ", expected to have modification date " + + boost::lexical_cast(s->second); + return false; + } + } + return true; + } + + struct thread_safe_storage + { + thread_safe_storage(std::size_t n) + : slots(n, false) + {} + + boost::mutex mutex; + boost::condition condition; + std::vector slots; + }; + + struct slot_lock + { + slot_lock(thread_safe_storage& s, int slot_) + : storage_(s) + , slot(slot_) + { + assert(slot_>=0 && slot_ < (int)s.slots.size()); + boost::mutex::scoped_lock lock(storage_.mutex); + + while (storage_.slots[slot]) + storage_.condition.wait(lock); + storage_.slots[slot] = true; + } + + ~slot_lock() + { + storage_.slots[slot] = false; + storage_.condition.notify_all(); + } + + thread_safe_storage& storage_; + int slot; + }; + + class storage : public storage_interface, thread_safe_storage, boost::noncopyable + { + public: + storage(torrent_info const& info, path const& path, file_pool& fp) + : thread_safe_storage(info.num_pieces()) + , m_info(info) + , m_files(fp) + { + assert(info.begin_files() != info.end_files()); + m_save_path = complete(path); + assert(m_save_path.is_complete()); + } + + void release_files(); + void initialize(bool allocate_files); + bool move_storage(path save_path); + size_type read(char* buf, int slot, int offset, int size); + void write(const char* buf, int slot, int offset, int size); + void move_slot(int src_slot, int dst_slot); + void swap_slots(int slot1, int slot2); + void swap_slots3(int slot1, int slot2, int slot3); + bool verify_resume_data(entry& rd, std::string& error); + void write_resume_data(entry& rd) const; + + size_type read_impl(char* buf, int slot, int offset, int size, bool fill_zero); + + ~storage() + { + m_files.release(this); + } + + torrent_info const& m_info; + path m_save_path; + // the file pool is typically stored in + // the session, to make all storage + // instances use the same pool + file_pool& m_files; + + // temporary storage for moving pieces + std::vector m_scratch_buffer; + }; + + void storage::initialize(bool allocate_files) + { + // first, create all missing directories + path last_path; + for (torrent_info::file_iterator file_iter = m_info.begin_files(), + end_iter = m_info.end_files(); file_iter != end_iter; ++file_iter) + { + path dir = (m_save_path / file_iter->path).branch_path(); + + if (dir != last_path) + { + last_path = dir; + +#if defined(_WIN32) && defined(UNICODE) && BOOST_VERSION < 103400 + if (!exists_win(last_path)) + create_directories_win(last_path); +#else + if (!exists(last_path)) + create_directories(last_path); +#endif + } + + // if the file is empty, just create it. But also make sure + // the directory exits. + if (file_iter->size == 0) + { + file(m_save_path / file_iter->path, file::out); + continue; + } + + if (allocate_files) + { + m_files.open_file(this, m_save_path / file_iter->path, file::in | file::out) + ->set_size(file_iter->size); + } + } + } + + void storage::release_files() + { + m_files.release(this); + std::vector().swap(m_scratch_buffer); + } + + void storage::write_resume_data(entry& rd) const + { + std::vector > file_sizes + = get_filesizes(m_info, m_save_path); + + rd["file sizes"] = entry::list_type(); + entry::list_type& fl = rd["file sizes"].list(); + for (std::vector >::iterator i + = file_sizes.begin(), end(file_sizes.end()); i != end; ++i) + { + entry::list_type p; + p.push_back(entry(i->first)); + p.push_back(entry(i->second)); + fl.push_back(entry(p)); + } + } + + bool storage::verify_resume_data(entry& rd, std::string& error) + { + std::vector > file_sizes; + entry::list_type& l = rd["file sizes"].list(); + + for (entry::list_type::iterator i = l.begin(); + i != l.end(); ++i) + { + file_sizes.push_back(std::pair( + i->list().front().integer() + , i->list().back().integer())); + } + + if (file_sizes.empty()) + { + error = "the number of files in resume data is 0"; + return false; + } + + entry::list_type& slots = rd["slots"].list(); + bool seed = int(slots.size()) == m_info.num_pieces() + && std::find_if(slots.begin(), slots.end() + , boost::bind(std::less() + , boost::bind((size_type const& (entry::*)() const) + &entry::integer, _1), 0)) == slots.end(); + + bool full_allocation_mode = false; + try + { + full_allocation_mode = rd["allocation"].string() == "full"; + } + catch (std::exception&) {} + + if (seed) + { + if (m_info.num_files() != (int)file_sizes.size()) + { + error = "the number of files does not match the torrent (num: " + + boost::lexical_cast(file_sizes.size()) + " actual: " + + boost::lexical_cast(m_info.num_files()) + ")"; + return false; + } + + std::vector >::iterator + fs = file_sizes.begin(); + // the resume data says we have the entire torrent + // make sure the file sizes are the right ones + for (torrent_info::file_iterator i = m_info.begin_files() + , end(m_info.end_files()); i != end; ++i, ++fs) + { + if (i->size != fs->first) + { + error = "file size for '" + i->path.native_file_string() + + "' was expected to be " + + boost::lexical_cast(i->size) + " bytes"; + return false; + } + } + return true; + } + + return match_filesizes(m_info, m_save_path, file_sizes + , !full_allocation_mode, &error); + } + + // returns true on success + bool storage::move_storage(path save_path) + { + path old_path; + path new_path; + + save_path = complete(save_path); + +#if defined(_WIN32) && defined(UNICODE) && BOOST_VERSION < 103400 + std::wstring wsave_path(safe_convert(save_path.native_file_string())); + if (!exists_win(save_path)) + { + CreateDirectory(wsave_path.c_str(), 0); + } + else if ((GetFileAttributes(wsave_path.c_str()) & FILE_ATTRIBUTE_DIRECTORY) == 0) + { + return false; + } +#else + if (!exists(save_path)) + create_directory(save_path); + else if (!is_directory(save_path)) + return false; +#endif + + m_files.release(this); + + old_path = m_save_path / m_info.name(); + new_path = save_path / m_info.name(); + + try + { +#if defined(_WIN32) && defined(UNICODE) && BOOST_VERSION < 103400 + rename_win(old_path, new_path); +#else + rename(old_path, new_path); +#endif + m_save_path = save_path; + return true; + } + catch (std::exception&) {} + return false; + } + +#ifndef NDEBUG +/* + void storage::shuffle() + { + int num_pieces = m_info.num_pieces(); + + std::vector pieces(num_pieces); + for (std::vector::iterator i = pieces.begin(); + i != pieces.end(); ++i) + { + *i = static_cast(i - pieces.begin()); + } + std::srand((unsigned int)std::time(0)); + std::vector targets(pieces); + std::random_shuffle(pieces.begin(), pieces.end()); + std::random_shuffle(targets.begin(), targets.end()); + + for (int i = 0; i < (std::max)(num_pieces / 50, 1); ++i) + { + const int slot_index = targets[i]; + const int piece_index = pieces[i]; + const int slot_size =static_cast(m_info.piece_size(slot_index)); + std::vector buf(slot_size); + read(&buf[0], piece_index, 0, slot_size); + write(&buf[0], slot_index, 0, slot_size); + } + } +*/ +#endif + + void storage::move_slot(int src_slot, int dst_slot) + { + int piece_size = m_info.piece_size(dst_slot); + m_scratch_buffer.resize(piece_size); + read_impl(&m_scratch_buffer[0], src_slot, 0, piece_size, true); + write(&m_scratch_buffer[0], dst_slot, 0, piece_size); + } + + void storage::swap_slots(int slot1, int slot2) + { + // the size of the target slot is the size of the piece + int piece_size = m_info.piece_length(); + int piece1_size = m_info.piece_size(slot2); + int piece2_size = m_info.piece_size(slot1); + m_scratch_buffer.resize(piece_size * 2); + read_impl(&m_scratch_buffer[0], slot1, 0, piece1_size, true); + read_impl(&m_scratch_buffer[piece_size], slot2, 0, piece2_size, true); + write(&m_scratch_buffer[0], slot2, 0, piece1_size); + write(&m_scratch_buffer[piece_size], slot1, 0, piece2_size); + } + + void storage::swap_slots3(int slot1, int slot2, int slot3) + { + // the size of the target slot is the size of the piece + int piece_size = m_info.piece_length(); + int piece1_size = m_info.piece_size(slot2); + int piece2_size = m_info.piece_size(slot3); + int piece3_size = m_info.piece_size(slot1); + m_scratch_buffer.resize(piece_size * 2); + read_impl(&m_scratch_buffer[0], slot1, 0, piece1_size, true); + read_impl(&m_scratch_buffer[piece_size], slot2, 0, piece2_size, true); + write(&m_scratch_buffer[0], slot2, 0, piece1_size); + read_impl(&m_scratch_buffer[0], slot3, 0, piece3_size, true); + write(&m_scratch_buffer[piece_size], slot3, 0, piece2_size); + write(&m_scratch_buffer[0], slot1, 0, piece3_size); + } + + size_type storage::read( + char* buf + , int slot + , int offset + , int size) + { + return read_impl(buf, slot, offset, size, false); + } + + size_type storage::read_impl( + char* buf + , int slot + , int offset + , int size + , bool fill_zero) + { + assert(buf != 0); + assert(slot >= 0 && slot < m_info.num_pieces()); + assert(offset >= 0); + assert(offset < m_info.piece_size(slot)); + assert(size > 0); + + slot_lock lock(*this, slot); + +#ifndef NDEBUG + std::vector slices + = m_info.map_block(slot, offset, size); + assert(!slices.empty()); +#endif + + size_type start = slot * (size_type)m_info.piece_length() + offset; + assert(start + size <= m_info.total_size()); + + // find the file iterator and file offset + size_type file_offset = start; + std::vector::const_iterator file_iter; + + for (file_iter = m_info.begin_files();;) + { + if (file_offset < file_iter->size) + break; + + file_offset -= file_iter->size; + ++file_iter; + } + + int buf_pos = 0; + boost::shared_ptr in(m_files.open_file( + this, m_save_path / file_iter->path, file::in)); + + assert(file_offset < file_iter->size); + + assert(slices[0].offset == file_offset); + + size_type new_pos = in->seek(file_offset); + if (new_pos != file_offset) + { + // the file was not big enough + if (!fill_zero) + throw file_error("slot has no storage"); + std::memset(buf + buf_pos, 0, size - buf_pos); + return size; + } + +#ifndef NDEBUG + size_type in_tell = in->tell(); + assert(in_tell == file_offset); +#endif + + int left_to_read = size; + int slot_size = static_cast(m_info.piece_size(slot)); + + if (offset + left_to_read > slot_size) + left_to_read = slot_size - offset; + + assert(left_to_read >= 0); + + size_type result = left_to_read; + +#ifndef NDEBUG + int counter = 0; +#endif + + while (left_to_read > 0) + { + int read_bytes = left_to_read; + if (file_offset + read_bytes > file_iter->size) + read_bytes = static_cast(file_iter->size - file_offset); + + if (read_bytes > 0) + { +#ifndef NDEBUG + assert(int(slices.size()) > counter); + size_type slice_size = slices[counter].size; + assert(slice_size == read_bytes); + assert(m_info.file_at(slices[counter].file_index).path + == file_iter->path); +#endif + + size_type actual_read = in->read(buf + buf_pos, read_bytes); + + if (read_bytes != actual_read) + { + // the file was not big enough + if (actual_read > 0) buf_pos += actual_read; + if (!fill_zero) + throw file_error("slot has no storage"); + std::memset(buf + buf_pos, 0, size - buf_pos); + return size; + } + + left_to_read -= read_bytes; + buf_pos += read_bytes; + assert(buf_pos >= 0); + file_offset += read_bytes; + } + + if (left_to_read > 0) + { + ++file_iter; +#ifndef NDEBUG + // empty files are not returned by map_block, so if + // this file was empty, don't increment the slice counter + if (read_bytes > 0) ++counter; +#endif + path path = m_save_path / file_iter->path; + + file_offset = 0; + in = m_files.open_file( + this, path, file::in); + in->seek(0); + } + } + return result; + } + + // throws file_error if it fails to write + void storage::write( + const char* buf + , int slot + , int offset + , int size) + { + assert(buf != 0); + assert(slot >= 0); + assert(slot < m_info.num_pieces()); + assert(offset >= 0); + assert(size > 0); + + slot_lock lock(*this, slot); + +#ifndef NDEBUG + std::vector slices + = m_info.map_block(slot, offset, size); + assert(!slices.empty()); +#endif + + size_type start = slot * (size_type)m_info.piece_length() + offset; + + // find the file iterator and file offset + size_type file_offset = start; + std::vector::const_iterator file_iter; + + for (file_iter = m_info.begin_files();;) + { + if (file_offset < file_iter->size) + break; + + file_offset -= file_iter->size; + ++file_iter; + assert(file_iter != m_info.end_files()); + } + + path p(m_save_path / file_iter->path); + boost::shared_ptr out = m_files.open_file( + this, p, file::out | file::in); + + assert(file_offset < file_iter->size); + assert(slices[0].offset == file_offset); + + size_type pos = out->seek(file_offset); + + if (pos != file_offset) + { + std::stringstream s; + s << "no storage for slot " << slot; + throw file_error(s.str()); + } + + int left_to_write = size; + int slot_size = static_cast(m_info.piece_size(slot)); + + if (offset + left_to_write > slot_size) + left_to_write = slot_size - offset; + + assert(left_to_write >= 0); + + int buf_pos = 0; +#ifndef NDEBUG + int counter = 0; +#endif + while (left_to_write > 0) + { + int write_bytes = left_to_write; + if (file_offset + write_bytes > file_iter->size) + { + assert(file_iter->size >= file_offset); + write_bytes = static_cast(file_iter->size - file_offset); + } + + if (write_bytes > 0) + { + assert(int(slices.size()) > counter); + assert(slices[counter].size == write_bytes); + assert(m_info.file_at(slices[counter].file_index).path + == file_iter->path); + + assert(buf_pos >= 0); + assert(write_bytes >= 0); + size_type written = out->write(buf + buf_pos, write_bytes); + + if (written != write_bytes) + { + std::stringstream s; + s << "no storage for slot " << slot; + throw file_error(s.str()); + } + + left_to_write -= write_bytes; + buf_pos += write_bytes; + assert(buf_pos >= 0); + file_offset += write_bytes; + assert(file_offset <= file_iter->size); + } + + if (left_to_write > 0) + { + #ifndef NDEBUG + if (write_bytes > 0) ++counter; + #endif + ++file_iter; + + assert(file_iter != m_info.end_files()); + path p = m_save_path / file_iter->path; + file_offset = 0; + out = m_files.open_file( + this, p, file::out | file::in); + + out->seek(0); + } + } + } + + storage_interface* default_storage_constructor(torrent_info const& ti + , boost::filesystem::path const& path, file_pool& fp) + { + return new storage(ti, path, fp); + } + + bool supports_sparse_files(path const& p) + { + assert(p.is_complete()); +#if defined(_WIN32) + // assume windows API is available + DWORD max_component_len = 0; + DWORD volume_flags = 0; + std::string root_device = p.root_name() + "\\"; +#if defined(UNICODE) + std::wstring wph(safe_convert(root_device)); + bool ret = ::GetVolumeInformation(wph.c_str(), 0 + , 0, 0, &max_component_len, &volume_flags, 0, 0); +#else + bool ret = ::GetVolumeInformation(root_device.c_str(), 0 + , 0, 0, &max_component_len, &volume_flags, 0, 0); +#endif + + if (!ret) return false; + if (volume_flags & FILE_SUPPORTS_SPARSE_FILES) + return true; +#endif + +#if defined(__APPLE__) || defined(__linux__) + // find the last existing directory of the save path + path query_path = p; + while (!query_path.empty() && !exists(query_path)) + query_path = query_path.branch_path(); +#endif + +#if defined(__APPLE__) + + struct statfs fsinfo; + int ret = statfs(query_path.native_directory_string().c_str(), &fsinfo); + if (ret != 0) return false; + + attrlist request; + request.bitmapcount = ATTR_BIT_MAP_COUNT; + request.reserved = 0; + request.commonattr = 0; + request.volattr = ATTR_VOL_CAPABILITIES; + request.dirattr = 0; + request.fileattr = 0; + request.forkattr = 0; + + struct vol_capabilities_attr_buf + { + unsigned long length; + vol_capabilities_attr_t info; + } vol_cap; + + ret = getattrlist(fsinfo.f_mntonname, &request, &vol_cap + , sizeof(vol_cap), 0); + if (ret != 0) return false; + + if (vol_cap.info.capabilities[VOL_CAPABILITIES_FORMAT] + & (VOL_CAP_FMT_SPARSE_FILES | VOL_CAP_FMT_ZERO_RUNS)) + { + return true; + } + + return true; +#endif + +#if defined(__linux__) + struct statfs buf; + int err = statfs(query_path.native_directory_string().c_str(), &buf); + if (err == 0) + { +#ifndef NDEBUG + std::cerr << "buf.f_type " << std::hex << buf.f_type << std::endl; +#endif + switch (buf.f_type) + { + case 0x5346544e: // NTFS + case 0xEF51: // EXT2 OLD + case 0xEF53: // EXT2 and EXT3 + case 0x00011954: // UFS + case 0x52654973: // ReiserFS + case 0x58465342: // XFS + return true; + } + } +#ifndef NDEBUG + else + { + std::cerr << "statfs returned " << err << std::endl; + std::cerr << "errno: " << errno << std::endl; + std::cerr << "path: " << query_path.native_directory_string() << std::endl; + } +#endif +#endif + + // TODO: POSIX implementation + return false; + } + + // -- piece_manager ----------------------------------------------------- + + class piece_manager::impl + { + friend class invariant_access; + public: + + impl( + torrent_info const& info + , path const& path + , file_pool& fp + , storage_constructor_type sc); + + bool check_fastresume( + aux::piece_checker_data& d + , std::vector& pieces + , int& num_pieces + , bool compact_mode); + + std::pair check_files( + std::vector& pieces + , int& num_pieces, boost::recursive_mutex& mutex); + + void release_files(); + + bool allocate_slots(int num_slots, bool abort_on_disk = false); + void mark_failed(int index); + unsigned long piece_crc( + int slot_index + , int block_size + , piece_picker::block_info const* bi); + + int slot_for_piece(int piece_index) const; + + size_type read( + char* buf + , int piece_index + , int offset + , int size); + + void write( + const char* buf + , int piece_index + , int offset + , int size); + + path const& save_path() const + { return m_save_path; } + + bool move_storage(path save_path) + { + if (m_storage->move_storage(save_path)) + { + m_save_path = complete(save_path); + return true; + } + return false; + } + + void export_piece_map(std::vector& p) const; + + // returns the slot currently associated with the given + // piece or assigns the given piece_index to a free slot + + int identify_data( + const std::vector& piece_data + , int current_slot + , std::vector& have_pieces + , int& num_pieces + , const std::multimap& hash_to_piece + , boost::recursive_mutex& mutex); + + int allocate_slot_for_piece(int piece_index); +#ifndef NDEBUG + void check_invariant() const; +#ifdef TORRENT_STORAGE_DEBUG + void debug_log() const; +#endif +#endif + boost::scoped_ptr m_storage; + + // if this is true, pieces are always allocated at the + // lowest possible slot index. If it is false, pieces + // are always written to their final place immediately + bool m_compact_mode; + + // if this is true, pieces that haven't been downloaded + // will be filled with zeroes. Not filling with zeroes + // will not work in some cases (where a seek cannot pass + // the end of the file). + bool m_fill_mode; + + // a bitmask representing the pieces we have + std::vector m_have_piece; + + torrent_info const& m_info; + + // slots that haven't had any file storage allocated + std::vector m_unallocated_slots; + // slots that have file storage, but isn't assigned to a piece + std::vector m_free_slots; + + enum + { + has_no_slot = -3 // the piece has no storage + }; + + // maps piece indices to slots. If a piece doesn't + // have any storage, it is set to 'has_no_slot' + std::vector m_piece_to_slot; + + enum + { + unallocated = -1, // the slot is unallocated + unassigned = -2 // the slot is allocated but not assigned to a piece + }; + + // maps slots to piece indices, if a slot doesn't have a piece + // it can either be 'unassigned' or 'unallocated' + std::vector m_slot_to_piece; + + path m_save_path; + + mutable boost::recursive_mutex m_mutex; + + bool m_allocating; + boost::mutex m_allocating_monitor; + boost::condition m_allocating_condition; + + // these states are used while checking/allocating the torrent + + enum { + // the default initial state + state_none, + // the file checking is complete + state_finished, + // creating the directories + state_create_files, + // checking the files + state_full_check, + // allocating files (in non-compact mode) + state_allocating + } m_state; + int m_current_slot; + + std::vector m_piece_data; + + // this maps a piece hash to piece index. It will be + // build the first time it is used (to save time if it + // isn't needed) + std::multimap m_hash_to_piece; + }; + + piece_manager::impl::impl( + torrent_info const& info + , path const& save_path + , file_pool& fp + , storage_constructor_type sc) + : m_storage(sc(info, save_path, fp)) + , m_compact_mode(false) + , m_fill_mode(true) + , m_info(info) + , m_save_path(complete(save_path)) + , m_allocating(false) + { + m_fill_mode = !supports_sparse_files(save_path); + } + + piece_manager::piece_manager( + torrent_info const& info + , path const& save_path + , file_pool& fp + , storage_constructor_type sc) + : m_pimpl(new impl(info, save_path, fp, sc)) + { + } + + piece_manager::~piece_manager() + { + } + + void piece_manager::write_resume_data(entry& rd) const + { + m_pimpl->m_storage->write_resume_data(rd); + } + + bool piece_manager::verify_resume_data(entry& rd, std::string& error) + { + return m_pimpl->m_storage->verify_resume_data(rd, error); + } + + void piece_manager::release_files() + { + m_pimpl->release_files(); + } + + void piece_manager::impl::release_files() + { + // synchronization ------------------------------------------------------ + boost::recursive_mutex::scoped_lock lock(m_mutex); + // ---------------------------------------------------------------------- + + m_storage->release_files(); + } + + void piece_manager::impl::export_piece_map( + std::vector& p) const + { + // synchronization ------------------------------------------------------ + boost::recursive_mutex::scoped_lock lock(m_mutex); + // ---------------------------------------------------------------------- + + INVARIANT_CHECK; + + p.clear(); + std::vector::const_reverse_iterator last; + for (last = m_slot_to_piece.rbegin(); + last != m_slot_to_piece.rend(); ++last) + { + if (*last != unallocated) break; + } + + for (std::vector::const_iterator i = + m_slot_to_piece.begin(); + i != last.base(); ++i) + { + p.push_back(*i); + } + } + + bool piece_manager::compact_allocation() const + { return m_pimpl->m_compact_mode; } + + void piece_manager::export_piece_map( + std::vector& p) const + { + m_pimpl->export_piece_map(p); + } + + void piece_manager::impl::mark_failed(int piece_index) + { + // synchronization ------------------------------------------------------ + boost::recursive_mutex::scoped_lock lock(m_mutex); + // ---------------------------------------------------------------------- + + INVARIANT_CHECK; + + assert(piece_index >= 0 && piece_index < (int)m_piece_to_slot.size()); + assert(m_piece_to_slot[piece_index] >= 0); + + int slot_index = m_piece_to_slot[piece_index]; + + assert(slot_index >= 0); + + m_slot_to_piece[slot_index] = unassigned; + m_piece_to_slot[piece_index] = has_no_slot; + m_free_slots.push_back(slot_index); + } + + void piece_manager::mark_failed(int index) + { + m_pimpl->mark_failed(index); + } + + bool piece_manager::is_allocating() const + { + return m_pimpl->m_state + == impl::state_allocating; + } + + int piece_manager::slot_for_piece(int piece_index) const + { + return m_pimpl->slot_for_piece(piece_index); + } + + int piece_manager::impl::slot_for_piece(int piece_index) const + { + assert(piece_index >= 0 && piece_index < m_info.num_pieces()); + return m_piece_to_slot[piece_index]; + } + + unsigned long piece_manager::piece_crc( + int index + , int block_size + , piece_picker::block_info const* bi) + { + return m_pimpl->piece_crc(index, block_size, bi); + } + + unsigned long piece_manager::impl::piece_crc( + int slot_index + , int block_size + , piece_picker::block_info const* bi) + try + { + assert(slot_index >= 0); + assert(slot_index < m_info.num_pieces()); + assert(block_size > 0); + + adler32_crc crc; + std::vector buf(block_size); + int num_blocks = static_cast(m_info.piece_size(slot_index)) / block_size; + int last_block_size = static_cast(m_info.piece_size(slot_index)) % block_size; + if (last_block_size == 0) last_block_size = block_size; + + for (int i = 0; i < num_blocks-1; ++i) + { + if (!bi[i].finished) continue; + m_storage->read( + &buf[0] + , slot_index + , i * block_size + , block_size); + crc.update(&buf[0], block_size); + } + if (bi[num_blocks - 1].finished) + { + m_storage->read( + &buf[0] + , slot_index + , block_size * (num_blocks - 1) + , last_block_size); + crc.update(&buf[0], last_block_size); + } + return crc.final(); + } + catch (std::exception&) + { + return 0; + } + + size_type piece_manager::impl::read( + char* buf + , int piece_index + , int offset + , int size) + { + assert(buf); + assert(offset >= 0); + assert(size > 0); + assert(piece_index >= 0 && piece_index < (int)m_piece_to_slot.size()); + assert(m_piece_to_slot[piece_index] >= 0 + && m_piece_to_slot[piece_index] < (int)m_slot_to_piece.size()); + int slot = m_piece_to_slot[piece_index]; + assert(slot >= 0 && slot < (int)m_slot_to_piece.size()); + return m_storage->read(buf, slot, offset, size); + } + + size_type piece_manager::read( + char* buf + , int piece_index + , int offset + , int size) + { + return m_pimpl->read(buf, piece_index, offset, size); + } + + void piece_manager::impl::write( + const char* buf + , int piece_index + , int offset + , int size) + { + assert(buf); + assert(offset >= 0); + assert(size > 0); + assert(piece_index >= 0 && piece_index < (int)m_piece_to_slot.size()); + int slot = allocate_slot_for_piece(piece_index); + assert(slot >= 0 && slot < (int)m_slot_to_piece.size()); + m_storage->write(buf, slot, offset, size); + } + + void piece_manager::write( + const char* buf + , int piece_index + , int offset + , int size) + { + m_pimpl->write(buf, piece_index, offset, size); + } + + int piece_manager::impl::identify_data( + const std::vector& piece_data + , int current_slot + , std::vector& have_pieces + , int& num_pieces + , const std::multimap& hash_to_piece + , boost::recursive_mutex& mutex) + { +// INVARIANT_CHECK; + + assert((int)have_pieces.size() == m_info.num_pieces()); + + const int piece_size = static_cast(m_info.piece_length()); + const int last_piece_size = static_cast(m_info.piece_size( + m_info.num_pieces() - 1)); + + assert((int)piece_data.size() >= last_piece_size); + + // calculate a small digest, with the same + // size as the last piece. And a large digest + // which has the same size as a normal piece + hasher small_digest; + small_digest.update(&piece_data[0], last_piece_size); + hasher large_digest(small_digest); + assert(piece_size - last_piece_size >= 0); + if (piece_size - last_piece_size > 0) + { + large_digest.update( + &piece_data[last_piece_size] + , piece_size - last_piece_size); + } + sha1_hash large_hash = large_digest.final(); + sha1_hash small_hash = small_digest.final(); + + typedef std::multimap::const_iterator map_iter; + map_iter begin1; + map_iter end1; + map_iter begin2; + map_iter end2; + + // makes the lookups for the small digest and the large digest + boost::tie(begin1, end1) = hash_to_piece.equal_range(small_hash); + boost::tie(begin2, end2) = hash_to_piece.equal_range(large_hash); + + // copy all potential piece indices into this vector + std::vector matching_pieces; + for (map_iter i = begin1; i != end1; ++i) + matching_pieces.push_back(i->second); + for (map_iter i = begin2; i != end2; ++i) + matching_pieces.push_back(i->second); + + // no piece matched the data in the slot + if (matching_pieces.empty()) + return unassigned; + + // ------------------------------------------ + // CHECK IF THE PIECE IS IN ITS CORRECT PLACE + // ------------------------------------------ + + if (std::find( + matching_pieces.begin() + , matching_pieces.end() + , current_slot) != matching_pieces.end()) + { + const int piece_index = current_slot; + + // lock because we're writing to have_pieces + boost::recursive_mutex::scoped_lock l(mutex); + + if (have_pieces[piece_index]) + { + // we have already found a piece with + // this index. + int other_slot = m_piece_to_slot[piece_index]; + assert(other_slot >= 0); + + // take one of the other matching pieces + // that hasn't already been assigned + int other_piece = -1; + for (std::vector::iterator i = matching_pieces.begin(); + i != matching_pieces.end(); ++i) + { + if (have_pieces[*i] || *i == piece_index) continue; + other_piece = *i; + break; + } + if (other_piece >= 0) + { + // replace the old slot with 'other_piece' + assert(have_pieces[other_piece] == false); + have_pieces[other_piece] = true; + m_slot_to_piece[other_slot] = other_piece; + m_piece_to_slot[other_piece] = other_slot; + ++num_pieces; + } + else + { + // this index is the only piece with this + // hash. The previous slot we found with + // this hash must be the same piece. Mark + // that piece as unassigned, since this slot + // is the correct place for the piece. + m_slot_to_piece[other_slot] = unassigned; + m_free_slots.push_back(other_slot); + } + assert(m_piece_to_slot[piece_index] != current_slot); + assert(m_piece_to_slot[piece_index] >= 0); + m_piece_to_slot[piece_index] = has_no_slot; +#ifndef NDEBUG + // to make the assert happy, a few lines down + have_pieces[piece_index] = false; +#endif + } + else + { + ++num_pieces; + } + + assert(have_pieces[piece_index] == false); + assert(m_piece_to_slot[piece_index] == has_no_slot); + have_pieces[piece_index] = true; + + return piece_index; + } + + // find a matching piece that hasn't + // already been assigned + int free_piece = unassigned; + for (std::vector::iterator i = matching_pieces.begin(); + i != matching_pieces.end(); ++i) + { + if (have_pieces[*i]) continue; + free_piece = *i; + break; + } + + if (free_piece >= 0) + { + // lock because we're writing to have_pieces + boost::recursive_mutex::scoped_lock l(mutex); + + assert(have_pieces[free_piece] == false); + assert(m_piece_to_slot[free_piece] == has_no_slot); + have_pieces[free_piece] = true; + ++num_pieces; + + return free_piece; + } + else + { + assert(free_piece == unassigned); + return unassigned; + } + } + + // check if the fastresume data is up to date + // if it is, use it and return true. If it + // isn't return false and the full check + // will be run + bool piece_manager::impl::check_fastresume( + aux::piece_checker_data& data + , std::vector& pieces + , int& num_pieces, bool compact_mode) + { + assert(m_info.piece_length() > 0); + // synchronization ------------------------------------------------------ + boost::recursive_mutex::scoped_lock lock(m_mutex); + // ---------------------------------------------------------------------- + + INVARIANT_CHECK; + + m_compact_mode = compact_mode; + + // This will corrupt the storage + // use while debugging to find + // states that cannot be scanned + // by check_pieces. +// m_storage->shuffle(); + + m_piece_to_slot.resize(m_info.num_pieces(), has_no_slot); + m_slot_to_piece.resize(m_info.num_pieces(), unallocated); + m_free_slots.clear(); + m_unallocated_slots.clear(); + + pieces.clear(); + pieces.resize(m_info.num_pieces(), false); + num_pieces = 0; + + // if we have fast-resume info + // use it instead of doing the actual checking + if (!data.piece_map.empty() + && data.piece_map.size() <= m_slot_to_piece.size()) + { + for (int i = 0; i < (int)data.piece_map.size(); ++i) + { + m_slot_to_piece[i] = data.piece_map[i]; + if (data.piece_map[i] >= 0) + { + m_piece_to_slot[data.piece_map[i]] = i; + int found_piece = data.piece_map[i]; + + // if the piece is not in the unfinished list + // we have all of it + if (std::find_if( + data.unfinished_pieces.begin() + , data.unfinished_pieces.end() + , piece_picker::has_index(found_piece)) + == data.unfinished_pieces.end()) + { + ++num_pieces; + pieces[found_piece] = true; + } + } + else if (data.piece_map[i] == unassigned) + { + m_free_slots.push_back(i); + } + else + { + assert(data.piece_map[i] == unallocated); + m_unallocated_slots.push_back(i); + } + } + + m_unallocated_slots.reserve(int(pieces.size() - data.piece_map.size())); + for (int i = (int)data.piece_map.size(); i < (int)pieces.size(); ++i) + { + m_unallocated_slots.push_back(i); + } + + if (!m_compact_mode && !m_unallocated_slots.empty()) + { + m_state = state_allocating; + return false; + } + else + { + m_state = state_finished; + return true; + } + } + + m_state = state_create_files; + return false; + } + + // performs the full check and full allocation + // (if necessary). returns true if finished and + // false if it should be called again + // the second return value is the progress the + // file check is at. 0 is nothing done, and 1 + // is finished + std::pair piece_manager::impl::check_files( + std::vector& pieces, int& num_pieces, boost::recursive_mutex& mutex) + { + assert(num_pieces == std::count(pieces.begin(), pieces.end(), true)); + + if (m_state == state_allocating) + { + if (m_compact_mode) + { + m_state = state_finished; + return std::make_pair(true, 1.f); + } + + if (m_unallocated_slots.empty()) + { + m_state = state_finished; + return std::make_pair(true, 1.f); + } + + // if we're not in compact mode, make sure the + // pieces are spread out and placed at their + // final position. + assert(!m_unallocated_slots.empty()); + + if (!m_fill_mode) + { + // if we're not filling the allocation + // just make sure we move the current pieces + // into place, and just skip all other + // allocation + // allocate_slots returns true if it had to + // move any data + allocate_slots(m_unallocated_slots.size(), true); + } + else + { + allocate_slots(1); + } + + return std::make_pair(false, 1.f - (float)m_unallocated_slots.size() + / (float)m_slot_to_piece.size()); + } + + if (m_state == state_create_files) + { + m_storage->initialize(!m_fill_mode && !m_compact_mode); + + m_current_slot = 0; + m_state = state_full_check; + m_piece_data.resize(int(m_info.piece_length())); + return std::make_pair(false, 0.f); + } + + assert(m_state == state_full_check); + + // ------------------------ + // DO THE FULL CHECK + // ------------------------ + + try + { + + int piece_size = int(m_info.piece_size(m_current_slot)); + int num_read = m_storage->read(&m_piece_data[0] + , m_current_slot, 0, piece_size); + + // if the file is incomplete, skip the rest of it + if (num_read != piece_size) + throw file_error(""); + + if (m_hash_to_piece.empty()) + { + for (int i = 0; i < m_info.num_pieces(); ++i) + { + m_hash_to_piece.insert(std::make_pair(m_info.hash_for_piece(i), i)); + } + } + + int piece_index = identify_data(m_piece_data, m_current_slot + , pieces, num_pieces, m_hash_to_piece, mutex); + + assert(num_pieces == std::count(pieces.begin(), pieces.end(), true)); + assert(piece_index == unassigned || piece_index >= 0); + + const bool this_should_move = piece_index >= 0 && m_slot_to_piece[piece_index] != unallocated; + const bool other_should_move = m_piece_to_slot[m_current_slot] != has_no_slot; + + // check if this piece should be swapped with any other slot + // this section will ensure that the storage is correctly sorted + // libtorrent will never leave the storage in a state that + // requires this sorting, but other clients may. + + // example of worst case: + // | m_current_slot = 5 + // V + // +---+- - - +---+- - - +---+- - + // | x | | 5 | | 3 | <- piece data in slots + // +---+- - - +---+- - - +---+- - + // 3 y 5 <- slot index + + // in this example, the data in the m_current_slot (5) + // is piece 3. It has to be moved into slot 3. The data + // in slot y (piece 5) should be moved into the m_current_slot. + // and the data in slot 3 (piece x) should be moved to slot y. + + // there are three possible cases. + // 1. There's another piece that should be placed into this slot + // 2. This piece should be placed into another slot. + // 3. There's another piece that should be placed into this slot + // and this piece should be placed into another slot + + // swap piece_index with this slot + + // case 1 + if (this_should_move && !other_should_move) + { + assert(piece_index != m_current_slot); + + const int other_slot = piece_index; + assert(other_slot >= 0); + int other_piece = m_slot_to_piece[other_slot]; + + m_slot_to_piece[other_slot] = piece_index; + m_slot_to_piece[m_current_slot] = other_piece; + m_piece_to_slot[piece_index] = piece_index; + if (other_piece >= 0) m_piece_to_slot[other_piece] = m_current_slot; + + if (other_piece == unassigned) + { + std::vector::iterator i = + std::find(m_free_slots.begin(), m_free_slots.end(), other_slot); + assert(i != m_free_slots.end()); + m_free_slots.erase(i); + m_free_slots.push_back(m_current_slot); + } + + if (other_piece >= 0) + m_storage->swap_slots(other_slot, m_current_slot); + else + m_storage->move_slot(m_current_slot, other_slot); + + assert(m_slot_to_piece[m_current_slot] == unassigned + || m_piece_to_slot[m_slot_to_piece[m_current_slot]] == m_current_slot); + } + // case 2 + else if (!this_should_move && other_should_move) + { + assert(piece_index != m_current_slot); + + const int other_piece = m_current_slot; + const int other_slot = m_piece_to_slot[other_piece]; + assert(other_slot >= 0); + + m_slot_to_piece[m_current_slot] = other_piece; + m_slot_to_piece[other_slot] = piece_index; + m_piece_to_slot[other_piece] = m_current_slot; + + if (piece_index == unassigned) + m_free_slots.push_back(other_slot); + + if (piece_index >= 0) + { + m_piece_to_slot[piece_index] = other_slot; + m_storage->swap_slots(other_slot, m_current_slot); + } + else + { + m_storage->move_slot(other_slot, m_current_slot); + } + assert(m_slot_to_piece[m_current_slot] == unassigned + || m_piece_to_slot[m_slot_to_piece[m_current_slot]] == m_current_slot); + } + else if (this_should_move && other_should_move) + { + assert(piece_index != m_current_slot); + assert(piece_index >= 0); + + const int piece1 = m_slot_to_piece[piece_index]; + const int piece2 = m_current_slot; + const int slot1 = piece_index; + const int slot2 = m_piece_to_slot[piece2]; + + assert(slot1 >= 0); + assert(slot2 >= 0); + assert(piece2 >= 0); + + if (slot1 == slot2) + { + // this means there are only two pieces involved in the swap + assert(piece1 >= 0); + + // movement diagram: + // +-------------------------------+ + // | | + // +--> slot1 --> m_current_slot --+ + + m_slot_to_piece[slot1] = piece_index; + m_slot_to_piece[m_current_slot] = piece1; + + m_piece_to_slot[piece_index] = slot1; + m_piece_to_slot[piece1] = m_current_slot; + + assert(piece1 == m_current_slot); + assert(piece_index == slot1); + + m_storage->swap_slots(m_current_slot, slot1); + + assert(m_slot_to_piece[m_current_slot] == unassigned + || m_piece_to_slot[m_slot_to_piece[m_current_slot]] == m_current_slot); + } + else + { + assert(slot1 != slot2); + assert(piece1 != piece2); + + // movement diagram: + // +-----------------------------------------+ + // | | + // +--> slot1 --> slot2 --> m_current_slot --+ + + m_slot_to_piece[slot1] = piece_index; + m_slot_to_piece[slot2] = piece1; + m_slot_to_piece[m_current_slot] = piece2; + + m_piece_to_slot[piece_index] = slot1; + m_piece_to_slot[m_current_slot] = piece2; + + if (piece1 == unassigned) + { + std::vector::iterator i = + std::find(m_free_slots.begin(), m_free_slots.end(), slot1); + assert(i != m_free_slots.end()); + m_free_slots.erase(i); + m_free_slots.push_back(slot2); + } + + if (piece1 >= 0) + { + m_piece_to_slot[piece1] = slot2; + m_storage->swap_slots3(m_current_slot, slot1, slot2); + } + else + { + m_storage->move_slot(m_current_slot, slot1); + m_storage->move_slot(slot2, m_current_slot); + } + + assert(m_slot_to_piece[m_current_slot] == unassigned + || m_piece_to_slot[m_slot_to_piece[m_current_slot]] == m_current_slot); + } + } + else + { + assert(m_piece_to_slot[m_current_slot] == has_no_slot || piece_index != m_current_slot); + assert(m_slot_to_piece[m_current_slot] == unallocated); + assert(piece_index == unassigned || m_piece_to_slot[piece_index] == has_no_slot); + + // the slot was identified as piece 'piece_index' + if (piece_index != unassigned) + m_piece_to_slot[piece_index] = m_current_slot; + else + m_free_slots.push_back(m_current_slot); + + m_slot_to_piece[m_current_slot] = piece_index; + + assert(m_slot_to_piece[m_current_slot] == unassigned + || m_piece_to_slot[m_slot_to_piece[m_current_slot]] == m_current_slot); + } + } + catch (file_error&) + { + // find the file that failed, and skip all the blocks in that file + size_type file_offset = 0; + size_type current_offset = m_current_slot * m_info.piece_length(); + for (torrent_info::file_iterator i = m_info.begin_files(); + i != m_info.end_files(); ++i) + { + file_offset += i->size; + if (file_offset > current_offset) break; + } + + assert(file_offset > current_offset); + int skip_blocks = static_cast( + (file_offset - current_offset + m_info.piece_length() - 1) + / m_info.piece_length()); + + for (int i = m_current_slot; i < m_current_slot + skip_blocks; ++i) + { + assert(m_slot_to_piece[i] == unallocated); + m_unallocated_slots.push_back(i); + } + + // current slot will increase by one at the end of the for-loop too + m_current_slot += skip_blocks - 1; + } + ++m_current_slot; + + if (m_current_slot >= m_info.num_pieces()) + { + assert(m_current_slot == m_info.num_pieces()); + + // clear the memory we've been using + std::vector().swap(m_piece_data); + std::multimap().swap(m_hash_to_piece); + m_state = state_allocating; + assert(num_pieces == std::count(pieces.begin(), pieces.end(), true)); + return std::make_pair(false, 1.f); + } + + assert(num_pieces == std::count(pieces.begin(), pieces.end(), true)); + + return std::make_pair(false, (float)m_current_slot / m_info.num_pieces()); + } + + bool piece_manager::check_fastresume( + aux::piece_checker_data& d, std::vector& pieces + , int& num_pieces, bool compact_mode) + { + return m_pimpl->check_fastresume(d, pieces, num_pieces, compact_mode); + } + + std::pair piece_manager::check_files( + std::vector& pieces + , int& num_pieces + , boost::recursive_mutex& mutex) + { + return m_pimpl->check_files(pieces, num_pieces, mutex); + } + + int piece_manager::impl::allocate_slot_for_piece(int piece_index) + { + // synchronization ------------------------------------------------------ + boost::recursive_mutex::scoped_lock lock(m_mutex); + // ---------------------------------------------------------------------- + +// INVARIANT_CHECK; + + assert(piece_index >= 0); + assert(piece_index < (int)m_piece_to_slot.size()); + assert(m_piece_to_slot.size() == m_slot_to_piece.size()); + + int slot_index = m_piece_to_slot[piece_index]; + + if (slot_index != has_no_slot) + { + assert(slot_index >= 0); + assert(slot_index < (int)m_slot_to_piece.size()); + return slot_index; + } + + if (m_free_slots.empty()) + { + allocate_slots(1); + assert(!m_free_slots.empty()); + } + + std::vector::iterator iter( + std::find( + m_free_slots.begin() + , m_free_slots.end() + , piece_index)); + + if (iter == m_free_slots.end()) + { + assert(m_slot_to_piece[piece_index] != unassigned); + assert(!m_free_slots.empty()); + iter = m_free_slots.end() - 1; + + // special case to make sure we don't use the last slot + // when we shouldn't, since it's smaller than ordinary slots + if (*iter == m_info.num_pieces() - 1 && piece_index != *iter) + { + if (m_free_slots.size() == 1) + allocate_slots(1); + assert(m_free_slots.size() > 1); + // assumes that all allocated slots + // are put at the end of the free_slots vector + iter = m_free_slots.end() - 1; + } + } + + slot_index = *iter; + m_free_slots.erase(iter); + + assert(m_slot_to_piece[slot_index] == unassigned); + + m_slot_to_piece[slot_index] = piece_index; + m_piece_to_slot[piece_index] = slot_index; + + // there is another piece already assigned to + // the slot we are interested in, swap positions + if (slot_index != piece_index + && m_slot_to_piece[piece_index] >= 0) + { + +#if !defined(NDEBUG) && defined(TORRENT_STORAGE_DEBUG) + std::stringstream s; + + s << "there is another piece at our slot, swapping.."; + + s << "\n piece_index: "; + s << piece_index; + s << "\n slot_index: "; + s << slot_index; + s << "\n piece at our slot: "; + s << m_slot_to_piece[piece_index]; + s << "\n"; + + print_to_log(s.str()); + debug_log(); +#endif + + int piece_at_our_slot = m_slot_to_piece[piece_index]; + assert(m_piece_to_slot[piece_at_our_slot] == piece_index); + + std::swap( + m_slot_to_piece[piece_index] + , m_slot_to_piece[slot_index]); + + std::swap( + m_piece_to_slot[piece_index] + , m_piece_to_slot[piece_at_our_slot]); + + m_storage->move_slot(piece_index, slot_index); + + assert(m_slot_to_piece[piece_index] == piece_index); + assert(m_piece_to_slot[piece_index] == piece_index); + + slot_index = piece_index; + +#if !defined(NDEBUG) && defined(TORRENT_STORAGE_DEBUG) + debug_log(); +#endif + } + + assert(slot_index >= 0); + assert(slot_index < (int)m_slot_to_piece.size()); + return slot_index; + } + + namespace + { + // this is used to notify potential other + // threads that the allocation-function has exited + struct allocation_syncronization + { + allocation_syncronization( + bool& flag + , boost::condition& cond + , boost::mutex& monitor) + : m_flag(flag) + , m_cond(cond) + , m_monitor(monitor) + { + boost::mutex::scoped_lock lock(m_monitor); + + while (m_flag) + m_cond.wait(lock); + + m_flag = true; + } + + ~allocation_syncronization() + { + boost::mutex::scoped_lock lock(m_monitor); + m_flag = false; + m_cond.notify_one(); + } + + bool& m_flag; + boost::condition& m_cond; + boost::mutex& m_monitor; + }; + + } + + bool piece_manager::impl::allocate_slots(int num_slots, bool abort_on_disk) + { + assert(num_slots > 0); + + // this object will syncronize the allocation with + // potential other threads + allocation_syncronization sync_obj( + m_allocating + , m_allocating_condition + , m_allocating_monitor); + + // synchronization ------------------------------------------------------ + boost::recursive_mutex::scoped_lock lock(m_mutex); + // ---------------------------------------------------------------------- + +// INVARIANT_CHECK; + + assert(!m_unallocated_slots.empty()); + + const int stack_buffer_size = 16*1024; + char zeroes[stack_buffer_size]; + memset(zeroes, 0, stack_buffer_size); + + bool written = false; + + for (int i = 0; i < num_slots && !m_unallocated_slots.empty(); ++i) + { +// INVARIANT_CHECK; + + int pos = m_unallocated_slots.front(); + assert(m_slot_to_piece[pos] == unallocated); + assert(m_piece_to_slot[pos] != pos); + + int new_free_slot = pos; + if (m_piece_to_slot[pos] != has_no_slot) + { + new_free_slot = m_piece_to_slot[pos]; + m_storage->move_slot(new_free_slot, pos); + m_slot_to_piece[pos] = pos; + m_piece_to_slot[pos] = pos; + written = true; + } + else if (m_fill_mode) + { + int piece_size = int(m_info.piece_size(pos)); + int offset = 0; + for (; piece_size > 0; piece_size -= stack_buffer_size + , offset += stack_buffer_size) + { + m_storage->write(zeroes, pos, offset + , std::min(piece_size, stack_buffer_size)); + } + written = true; + } + m_unallocated_slots.erase(m_unallocated_slots.begin()); + m_slot_to_piece[new_free_slot] = unassigned; + m_free_slots.push_back(new_free_slot); + if (abort_on_disk && written) return true; + } + + assert(m_free_slots.size() > 0); + return written; + } + + bool piece_manager::allocate_slots(int num_slots, bool abort_on_disk) + { + return m_pimpl->allocate_slots(num_slots, abort_on_disk); + } + + path const& piece_manager::save_path() const + { + return m_pimpl->save_path(); + } + + bool piece_manager::move_storage(path const& save_path) + { + return m_pimpl->move_storage(save_path); + } + +#ifndef NDEBUG + void piece_manager::impl::check_invariant() const + { + // synchronization ------------------------------------------------------ + boost::recursive_mutex::scoped_lock lock(m_mutex); + // ---------------------------------------------------------------------- + if (m_piece_to_slot.empty()) return; + + assert((int)m_piece_to_slot.size() == m_info.num_pieces()); + assert((int)m_slot_to_piece.size() == m_info.num_pieces()); + + for (std::vector::const_iterator i = m_free_slots.begin(); + i != m_free_slots.end(); ++i) + { + assert(*i < (int)m_slot_to_piece.size()); + assert(*i >= 0); + assert(m_slot_to_piece[*i] == unassigned); + assert(std::find(i+1, m_free_slots.end(), *i) + == m_free_slots.end()); + } + + for (std::vector::const_iterator i = m_unallocated_slots.begin(); + i != m_unallocated_slots.end(); ++i) + { + assert(*i < (int)m_slot_to_piece.size()); + assert(*i >= 0); + assert(m_slot_to_piece[*i] == unallocated); + assert(std::find(i+1, m_unallocated_slots.end(), *i) + == m_unallocated_slots.end()); + } + + for (int i = 0; i < m_info.num_pieces(); ++i) + { + // Check domain of piece_to_slot's elements + if (m_piece_to_slot[i] != has_no_slot) + { + assert(m_piece_to_slot[i] >= 0); + assert(m_piece_to_slot[i] < (int)m_slot_to_piece.size()); + } + + // Check domain of slot_to_piece's elements + if (m_slot_to_piece[i] != unallocated + && m_slot_to_piece[i] != unassigned) + { + assert(m_slot_to_piece[i] >= 0); + assert(m_slot_to_piece[i] < (int)m_piece_to_slot.size()); + } + + // do more detailed checks on piece_to_slot + if (m_piece_to_slot[i] >= 0) + { + assert(m_slot_to_piece[m_piece_to_slot[i]] == i); + if (m_piece_to_slot[i] != i) + { + assert(m_slot_to_piece[i] == unallocated); + } + } + else + { + assert(m_piece_to_slot[i] == has_no_slot); + } + + // do more detailed checks on slot_to_piece + + if (m_slot_to_piece[i] >= 0) + { + assert(m_slot_to_piece[i] < (int)m_piece_to_slot.size()); + assert(m_piece_to_slot[m_slot_to_piece[i]] == i); +#ifdef TORRENT_STORAGE_DEBUG + assert( + std::find( + m_unallocated_slots.begin() + , m_unallocated_slots.end() + , i) == m_unallocated_slots.end() + ); + assert( + std::find( + m_free_slots.begin() + , m_free_slots.end() + , i) == m_free_slots.end() + ); +#endif + } + else if (m_slot_to_piece[i] == unallocated) + { +#ifdef TORRENT_STORAGE_DEBUG + assert(m_unallocated_slots.empty() + || (std::find( + m_unallocated_slots.begin() + , m_unallocated_slots.end() + , i) != m_unallocated_slots.end()) + ); +#endif + } + else if (m_slot_to_piece[i] == unassigned) + { +#ifdef TORRENT_STORAGE_DEBUG + assert( + std::find( + m_free_slots.begin() + , m_free_slots.end() + , i) != m_free_slots.end() + ); +#endif + } + else + { + assert(false && "m_slot_to_piece[i] is invalid"); + } + } + } + +#ifdef TORRENT_STORAGE_DEBUG + void piece_manager::impl::debug_log() const + { + std::stringstream s; + + s << "index\tslot\tpiece\n"; + + for (int i = 0; i < m_info.num_pieces(); ++i) + { + s << i << "\t" << m_slot_to_piece[i] << "\t"; + s << m_piece_to_slot[i] << "\n"; + } + + s << "---------------------------------\n"; + + print_to_log(s.str()); + } +#endif +#endif +} // namespace libtorrent + diff --git a/encryption/libtorrent/src/torrent.cpp b/encryption/libtorrent/src/torrent.cpp new file mode 100755 index 000000000..6678a756e --- /dev/null +++ b/encryption/libtorrent/src/torrent.cpp @@ -0,0 +1,2721 @@ +/* + +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 "libtorrent/pch.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/torrent_handle.hpp" +#include "libtorrent/session.hpp" +#include "libtorrent/torrent_info.hpp" +#include "libtorrent/tracker_manager.hpp" +#include "libtorrent/bencode.hpp" +#include "libtorrent/hasher.hpp" +#include "libtorrent/entry.hpp" +#include "libtorrent/peer.hpp" +#include "libtorrent/bt_peer_connection.hpp" +#include "libtorrent/web_peer_connection.hpp" +#include "libtorrent/peer_id.hpp" +#include "libtorrent/alert.hpp" +#include "libtorrent/identify_client.hpp" +#include "libtorrent/alert_types.hpp" +#include "libtorrent/extensions.hpp" +#include "libtorrent/aux_/session_impl.hpp" +#include "libtorrent/instantiate_connection.hpp" + +using namespace libtorrent; +using boost::tuples::tuple; +using boost::tuples::get; +using boost::tuples::make_tuple; +using boost::filesystem::complete; +using boost::bind; +using boost::mutex; +using libtorrent::aux::session_impl; + +namespace +{ + + enum + { + // wait 60 seconds before retrying a failed tracker + tracker_retry_delay_min = 60 + // when tracker_failed_max trackers + // has failed, wait 10 minutes instead + , tracker_retry_delay_max = 10 * 60 + , tracker_failed_max = 5 + }; + + int calculate_block_size(const torrent_info& i, int default_block_size) + { + if (default_block_size < 1024) default_block_size = 1024; + + // if pieces are too small, adjust the block size + if (i.piece_length() < default_block_size) + { + return static_cast(i.piece_length()); + } + + // otherwise, go with the default + return default_block_size; + } + + struct find_peer_by_ip + { + find_peer_by_ip(tcp::endpoint const& a, const torrent* t) + : ip(a) + , tor(t) + { assert(t != 0); } + + bool operator()(const session_impl::connection_map::value_type& c) const + { + tcp::endpoint sender = c.first->remote_endpoint(); + if (sender.address() != ip.address()) return false; + if (tor != c.second->associated_torrent().lock().get()) return false; + return true; + } + + tcp::endpoint const& ip; + torrent const* tor; + }; + + struct peer_by_id + { + peer_by_id(const peer_id& i): pid(i) {} + + bool operator()(const std::pair& p) const + { + if (p.second->pid() != pid) return false; + // have a special case for all zeros. We can have any number + // of peers with that pid, since it's used to indicate no pid. + if (std::count(pid.begin(), pid.end(), 0) == 20) return false; + return true; + } + + peer_id const& pid; + }; +} + +namespace libtorrent +{ + torrent::torrent( + session_impl& ses + , aux::checker_impl& checker + , torrent_info const& tf + , boost::filesystem::path const& save_path + , tcp::endpoint const& net_interface + , bool compact_mode + , int block_size + , session_settings const& s + , storage_constructor_type sc) + : m_torrent_file(tf) + , m_abort(false) + , m_paused(false) + , m_just_paused(false) + , m_event(tracker_request::started) + , m_block_size(0) + , m_storage(0) + , m_next_request(time_now()) + , m_duration(1800) + , m_complete(-1) + , m_incomplete(-1) + , m_host_resolver(ses.m_io_service) +#ifndef TORRENT_DISABLE_RESOLVE_COUNTRIES + , m_resolving_country(false) + , m_resolve_countries(false) +#endif + , m_announce_timer(ses.m_io_service) + , m_last_dht_announce(time_now() - minutes(15)) + , m_policy() + , m_ses(ses) + , m_checker(checker) + , m_picker(0) + , m_trackers(m_torrent_file.trackers()) + , m_last_working_tracker(-1) + , m_currently_trying_tracker(0) + , m_failed_trackers(0) + , m_time_scaler(0) + , m_num_pieces(0) + , m_sequenced_download_threshold(0) + , m_got_tracker_response(false) + , m_ratio(0.f) + , m_total_failed_bytes(0) + , m_total_redundant_bytes(0) + , m_net_interface(net_interface.address(), 0) + , m_save_path(complete(save_path)) + , m_compact_mode(compact_mode) + , m_default_block_size(block_size) + , m_connections_initialized(true) + , m_settings(s) + , m_storage_constructor(sc) + { +#ifndef NDEBUG + m_initial_done = 0; +#endif + + INVARIANT_CHECK; + + m_uploads_quota.min = 2; + m_connections_quota.min = 2; + // this will be corrected the next time the main session + // distributes resources, i.e. on average in 0.5 seconds + m_connections_quota.given = 100; + m_uploads_quota.max = std::numeric_limits::max(); + m_connections_quota.max = std::numeric_limits::max(); + m_policy.reset(new policy(this)); + init(); + } + + + torrent::torrent( + session_impl& ses + , aux::checker_impl& checker + , char const* tracker_url + , sha1_hash const& info_hash + , char const* name + , boost::filesystem::path const& save_path + , tcp::endpoint const& net_interface + , bool compact_mode + , int block_size + , session_settings const& s + , storage_constructor_type sc) + : m_torrent_file(info_hash) + , m_abort(false) + , m_paused(false) + , m_just_paused(false) + , m_event(tracker_request::started) + , m_block_size(0) + , m_storage(0) + , m_next_request(time_now()) + , m_duration(1800) + , m_complete(-1) + , m_incomplete(-1) + , m_host_resolver(ses.m_io_service) +#ifndef TORRENT_DISABLE_RESOLVE_COUNTRIES + , m_resolving_country(false) + , m_resolve_countries(false) +#endif + , m_announce_timer(ses.m_io_service) + , m_last_dht_announce(time_now() - minutes(15)) + , m_policy() + , m_ses(ses) + , m_checker(checker) + , m_picker(0) + , m_last_working_tracker(-1) + , m_currently_trying_tracker(0) + , m_failed_trackers(0) + , m_time_scaler(0) + , m_num_pieces(0) + , m_sequenced_download_threshold(0) + , m_got_tracker_response(false) + , m_ratio(0.f) + , m_total_failed_bytes(0) + , m_total_redundant_bytes(0) + , m_net_interface(net_interface.address(), 0) + , m_save_path(complete(save_path)) + , m_compact_mode(compact_mode) + , m_default_block_size(block_size) + , m_connections_initialized(false) + , m_settings(s) + , m_storage_constructor(sc) + { +#ifndef NDEBUG + m_initial_done = 0; +#endif + + INVARIANT_CHECK; + + if (name) m_name.reset(new std::string(name)); + + m_uploads_quota.min = 2; + m_connections_quota.min = 2; + // this will be corrected the next time the main session + // distributes resources, i.e. on average in 0.5 seconds + m_connections_quota.given = 100; + m_uploads_quota.max = std::numeric_limits::max(); + m_connections_quota.max = std::numeric_limits::max(); + if (tracker_url) + { + m_trackers.push_back(announce_entry(tracker_url)); + m_torrent_file.add_tracker(tracker_url); + } + + m_policy.reset(new policy(this)); + } + + void torrent::start() + { + boost::weak_ptr self(shared_from_this()); + m_announce_timer.expires_from_now(seconds(1)); + m_announce_timer.async_wait(m_ses.m_strand.wrap( + bind(&torrent::on_announce_disp, self, _1))); + } + +#ifndef TORRENT_DISABLE_DHT + bool torrent::should_announce_dht() const + { + // don't announce private torrents + if (m_torrent_file.is_valid() && m_torrent_file.priv()) return false; + + if (m_trackers.empty()) return true; + + return m_failed_trackers > 0 || !m_ses.settings().use_dht_as_fallback; + } +#endif + + torrent::~torrent() + { + // The invariant can't be maintained here, since the torrent + // is being destructed, all weak references to it have been + // reset, which means that all its peers already have an + // invalidated torrent pointer (so it cannot be verified to be correct) + + // i.e. the invariant can only be maintained if all connections have + // been closed by the time the torrent is destructed. And they are + // supposed to be closed. So we can still do the invariant check. + + assert(m_connections.empty()); + + INVARIANT_CHECK; + + if (m_ses.is_aborted()) + m_abort = true; + if (!m_connections.empty()) + disconnect_all(); + } + + 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); + } +#endif + + void torrent::init() + { + INVARIANT_CHECK; + + assert(m_torrent_file.is_valid()); + assert(m_torrent_file.num_files() > 0); + assert(m_torrent_file.total_size() >= 0); + + m_have_pieces.resize(m_torrent_file.num_pieces(), false); + m_storage.reset(new piece_manager(m_torrent_file, m_save_path + , m_ses.m_files, m_storage_constructor)); + m_block_size = calculate_block_size(m_torrent_file, m_default_block_size); + m_picker.reset(new piece_picker( + static_cast(m_torrent_file.piece_length() / m_block_size) + , static_cast((m_torrent_file.total_size()+m_block_size-1)/m_block_size))); + + std::vector const& url_seeds = m_torrent_file.url_seeds(); + std::copy(url_seeds.begin(), url_seeds.end(), std::inserter(m_web_seeds + , m_web_seeds.begin())); + } + + void torrent::use_interface(const char* net_interface) + { + INVARIANT_CHECK; + + m_net_interface = tcp::endpoint(address::from_string(net_interface), 0); + } + + void torrent::on_announce_disp(boost::weak_ptr p + , asio::error_code const& e) + { + if (e) return; + boost::shared_ptr t = p.lock(); + if (!t) return; + t->on_announce(); + } + + void torrent::on_announce() +#ifndef NDEBUG + try +#endif + { + boost::weak_ptr self(shared_from_this()); + + // announce on local network every 5 minutes + m_announce_timer.expires_from_now(minutes(5)); + m_announce_timer.async_wait(m_ses.m_strand.wrap( + bind(&torrent::on_announce_disp, self, _1))); + + // announce with the local discovery service + m_ses.announce_lsd(m_torrent_file.info_hash()); + +#ifndef TORRENT_DISABLE_DHT + if (!m_ses.m_dht) return; + ptime now = time_now(); + if (should_announce_dht() && now - m_last_dht_announce > minutes(14)) + { + m_last_dht_announce = now; + // TODO: There should be a way to abort an announce operation on the dht. + // when the torrent is destructed + assert(m_ses.m_external_listen_port > 0); + m_ses.m_dht->announce(m_torrent_file.info_hash() + , m_ses.m_external_listen_port + , m_ses.m_strand.wrap(bind(&torrent::on_dht_announce_response_disp, self, _1))); + } +#endif + } +#ifndef NDEBUG + catch (std::exception& e) + { + std::cerr << e.what() << std::endl; + assert(false); + }; +#endif + +#ifndef TORRENT_DISABLE_DHT + + 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) + { + if (peers.empty()) return; + + if (m_ses.m_alerts.should_post(alert::info)) + { + m_ses.m_alerts.post_alert(tracker_reply_alert( + get_handle(), peers.size(), "Got peers from DHT")); + } + std::for_each(peers.begin(), peers.end(), bind( + &policy::peer_from_tracker, boost::ref(m_policy), _1, peer_id(0) + , peer_info::dht, 0)); + } + +#endif + + // returns true if it is time for this torrent to make another + // tracker request + bool torrent::should_request() + { + INVARIANT_CHECK; + + if (m_torrent_file.trackers().empty()) return false; + + if (m_just_paused) + { + m_just_paused = false; + return true; + } + return !m_paused && m_next_request < time_now(); + } + + void torrent::tracker_warning(std::string const& msg) + { + session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); + + INVARIANT_CHECK; + + if (m_ses.m_alerts.should_post(alert::warning)) + { + m_ses.m_alerts.post_alert(tracker_warning_alert(get_handle(), msg)); + } + } + + void torrent::tracker_response( + tracker_request const& + , std::vector& peer_list + , int interval + , int complete + , int incomplete) + { + session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); + + INVARIANT_CHECK; + + m_failed_trackers = 0; + // announce intervals less than 5 minutes + // are insane. + if (interval < 60 * 5) interval = 60 * 5; + + m_last_working_tracker + = prioritize_tracker(m_currently_trying_tracker); + m_currently_trying_tracker = 0; + + m_duration = interval; + m_next_request = time_now() + seconds(m_duration); + + if (complete >= 0) m_complete = complete; + if (incomplete >= 0) m_incomplete = incomplete; + + // connect to random peers from the list + std::random_shuffle(peer_list.begin(), peer_list.end()); + +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + std::stringstream s; + s << "TRACKER RESPONSE:\n" + "interval: " << m_duration << "\n" + "peers:\n"; + for (std::vector::const_iterator i = peer_list.begin(); + i != peer_list.end(); ++i) + { + s << " " << std::setfill(' ') << std::setw(16) << i->ip + << " " << std::setw(5) << std::dec << i->port << " "; + if (!i->pid.is_all_zeros()) s << " " << i->pid << " " << identify_client(i->pid); + s << "\n"; + } + debug_log(s.str()); +#endif + // for each of the peers we got from the tracker + for (std::vector::iterator i = peer_list.begin(); + i != peer_list.end(); ++i) + { + // don't make connections to ourself + if (i->pid == m_ses.get_peer_id()) + continue; + + try + { + tcp::endpoint a(address::from_string(i->ip), i->port); + + if (m_ses.m_ip_filter.access(a.address()) & ip_filter::blocked) + { +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + debug_log("blocked ip from tracker: " + i->ip); +#endif + if (m_ses.m_alerts.should_post(alert::info)) + { + m_ses.m_alerts.post_alert(peer_blocked_alert(a.address() + , "peer from tracker blocked by IP filter")); + } + + continue; + } + + m_policy->peer_from_tracker(a, i->pid, peer_info::tracker, 0); + } + catch (std::exception&) + { + // assume this is because we got a hostname instead of + // an ip address from the tracker + + tcp::resolver::query q(i->ip, boost::lexical_cast(i->port)); + m_host_resolver.async_resolve(q, m_ses.m_strand.wrap( + bind(&torrent::on_peer_name_lookup, shared_from_this(), _1, _2, i->pid))); + } + } + m_policy->pulse(); + + if (m_ses.m_alerts.should_post(alert::info)) + { + std::stringstream s; + s << "Got response from tracker: " + << m_trackers[m_last_working_tracker].url; + m_ses.m_alerts.post_alert(tracker_reply_alert( + get_handle(), peer_list.size(), s.str())); + } + m_got_tracker_response = true; + } + + void torrent::on_peer_name_lookup(asio::error_code const& e, tcp::resolver::iterator host + , peer_id pid) try + { + session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); + + INVARIANT_CHECK; + + if (e || host == tcp::resolver::iterator() || + m_ses.is_aborted()) return; + + if (m_ses.m_ip_filter.access(host->endpoint().address()) & ip_filter::blocked) + { +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + debug_log("blocked ip from tracker: " + host->endpoint().address().to_string()); +#endif + if (m_ses.m_alerts.should_post(alert::info)) + { + m_ses.m_alerts.post_alert(peer_blocked_alert(host->endpoint().address() + , "peer from tracker blocked by IP filter")); + } + + return; + } + + m_policy->peer_from_tracker(*host, pid, peer_info::tracker, 0); + } + catch (std::exception&) + {}; + + size_type torrent::bytes_left() const + { + // if we don't have the metadata yet, we + // cannot tell how big the torrent is. + if (!valid_metadata()) return -1; + return m_torrent_file.total_size() + - quantized_bytes_done(); + } + + size_type torrent::quantized_bytes_done() const + { +// INVARIANT_CHECK; + + if (!valid_metadata()) return 0; + + if (m_torrent_file.num_pieces() == 0) + return 0; + + if (is_seed()) return m_torrent_file.total_size(); + + const int last_piece = m_torrent_file.num_pieces() - 1; + + size_type total_done + = m_num_pieces * m_torrent_file.piece_length(); + + // if we have the last piece, we have to correct + // the amount we have, since the first calculation + // assumed all pieces were of equal size + if (m_have_pieces[last_piece]) + { + int corr = m_torrent_file.piece_size(last_piece) + - m_torrent_file.piece_length(); + total_done += corr; + } + return total_done; + } + + // the first value is the total number of bytes downloaded + // the second value is the number of bytes of those that haven't + // been filtered as not wanted we have downloaded + tuple torrent::bytes_done() const + { + INVARIANT_CHECK; + + if (!valid_metadata() || m_torrent_file.num_pieces() == 0) + return tuple(0,0); + + const int last_piece = m_torrent_file.num_pieces() - 1; + + if (is_seed()) + return make_tuple(m_torrent_file.total_size() + , m_torrent_file.total_size()); + + size_type wanted_done = (m_num_pieces - m_picker->num_have_filtered()) + * m_torrent_file.piece_length(); + + size_type total_done + = m_num_pieces * m_torrent_file.piece_length(); + assert(m_num_pieces < m_torrent_file.num_pieces()); + + // if we have the last piece, we have to correct + // the amount we have, since the first calculation + // assumed all pieces were of equal size + if (m_have_pieces[last_piece]) + { + int corr = m_torrent_file.piece_size(last_piece) + - m_torrent_file.piece_length(); + total_done += corr; + if (m_picker->piece_priority(last_piece) != 0) + wanted_done += corr; + } + + assert(total_done <= m_torrent_file.total_size()); + assert(wanted_done <= m_torrent_file.total_size()); + + const std::vector& dl_queue + = m_picker->get_download_queue(); + + const int blocks_per_piece = static_cast( + m_torrent_file.piece_length() / m_block_size); + + for (std::vector::const_iterator i = + dl_queue.begin(); i != dl_queue.end(); ++i) + { + int corr = 0; + int index = i->index; + assert(!m_have_pieces[index]); + assert(i->finished < m_picker->blocks_in_piece(index)); + +#ifndef NDEBUG + for (std::vector::const_iterator j = boost::next(i); + j != dl_queue.end(); ++j) + { + assert(j->index != index); + } +#endif + + for (int j = 0; j < blocks_per_piece; ++j) + { + assert(i->info[j].finished == 0 || i->info[j].finished == 1); + assert(m_picker->is_finished(piece_block(index, j)) == i->info[j].finished); + corr += i->info[j].finished * m_block_size; + assert(index != last_piece || j < m_picker->blocks_in_last_piece() + || i->info[j].finished == 0); + } + + // correction if this was the last piece + // and if we have the last block + if (i->index == last_piece + && i->info[m_picker->blocks_in_last_piece()-1].finished) + { + corr -= m_block_size; + corr += m_torrent_file.piece_size(last_piece) % m_block_size; + } + total_done += corr; + if (m_picker->piece_priority(index) != 0) + wanted_done += corr; + } + + assert(total_done < m_torrent_file.total_size()); + assert(wanted_done < m_torrent_file.total_size()); + + std::map downloading_piece; + for (const_peer_iterator i = begin(); i != end(); ++i) + { + peer_connection* pc = i->second; + boost::optional p + = pc->downloading_piece_progress(); + if (p) + { + if (m_have_pieces[p->piece_index]) + continue; + + piece_block block(p->piece_index, p->block_index); + if (m_picker->is_finished(block)) + continue; + + std::map::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; + } +#ifndef NDEBUG + assert(p->bytes_downloaded <= p->full_block_bytes); + int last_piece = m_torrent_file.num_pieces() - 1; + if (p->piece_index == last_piece + && p->block_index == m_torrent_file.piece_size(last_piece) / block_size()) + assert(p->full_block_bytes == m_torrent_file.piece_size(last_piece) % block_size()); + else + assert(p->full_block_bytes == block_size()); +#endif + } + } + for (std::map::iterator i = downloading_piece.begin(); + i != downloading_piece.end(); ++i) + { + total_done += i->second; + if (m_picker->piece_priority(i->first.piece_index) != 0) + wanted_done += i->second; + } + +#ifndef NDEBUG + + if (total_done >= m_torrent_file.total_size()) + { + std::copy(m_have_pieces.begin(), m_have_pieces.end() + , std::ostream_iterator(std::cerr, " ")); + std::cerr << std::endl; + std::cerr << "num_pieces: " << m_num_pieces << std::endl; + + std::cerr << "unfinished:" << std::endl; + + for (std::vector::const_iterator i = + dl_queue.begin(); i != dl_queue.end(); ++i) + { + std::cerr << " " << i->index << " "; + for (int j = 0; j < blocks_per_piece; ++j) + { + std::cerr << i->info[j].finished; + } + std::cerr << std::endl; + } + + std::cerr << "downloading pieces:" << std::endl; + + for (std::map::iterator i = downloading_piece.begin(); + i != downloading_piece.end(); ++i) + { + std::cerr << " " << i->first.piece_index << ":" << i->first.block_index + << " " << i->second << std::endl; + } + + } + + assert(total_done < m_torrent_file.total_size()); + assert(wanted_done < m_torrent_file.total_size()); + +#endif + + assert(total_done >= wanted_done); + return make_tuple(total_done, wanted_done); + } + + 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; + + assert(m_storage.get()); + assert(m_picker.get()); + assert(index >= 0); + assert(index < m_torrent_file.num_pieces()); + + if (m_ses.m_alerts.should_post(alert::info)) + { + std::stringstream s; + s << "hash for piece " << index << " failed"; + m_ses.m_alerts.post_alert(hash_failed_alert(get_handle(), index, s.str())); + } + // increase the total amount of failed bytes + m_total_failed_bytes += m_torrent_file.piece_size(index); + + std::vector downloaders; + m_picker->get_downloaders(downloaders, index); + + // decrease the trust point of all peers that sent + // parts of this piece. + // first, build a set of all peers that participated + std::set peers; + std::copy(downloaders.begin(), downloaders.end(), std::inserter(peers, peers.begin())); + +#ifndef TORRENT_DISABLE_EXTENSIONS + for (extension_list_t::iterator i = m_extensions.begin() + , end(m_extensions.end()); i != end; ++i) + { + try { (*i)->on_piece_failed(index); } catch (std::exception&) {} + } +#endif + + for (std::set::iterator i = peers.begin() + , end(peers.end()); i != end; ++i) + { + peer_iterator p = m_connections.find(*i); + if (p == m_connections.end()) continue; + peer_connection& peer = *p->second; + peer.received_invalid_data(index); + + // either, we have received too many failed hashes + // or this was the only peer that sent us this piece. + // TODO: make this a changable setting + if ((peer.peer_info_struct() + && peer.peer_info_struct()->trust_points <= -7) + || peers.size() == 1) + { + // we don't trust this peer anymore + // ban it. + if (m_ses.m_alerts.should_post(alert::info)) + { + m_ses.m_alerts.post_alert(peer_ban_alert( + p->first + , get_handle() + , "banning peer because of too many corrupt pieces")); + } + + // mark the peer as banned + policy::peer* peerinfo = p->second->peer_info_struct(); + if (peerinfo) + { + peerinfo->banned = true; + } + else + { + // it might be a web seed + if (web_peer_connection const* wpc + = dynamic_cast(p->second)) + { + remove_url_seed(wpc->url()); + } + } + +#if defined(TORRENT_VERBOSE_LOGGING) + (*p->second->m_logger) << "*** BANNING PEER 'too many corrupt pieces'\n"; +#endif + p->second->disconnect(); + } + } + + // we have to let the piece_picker know that + // this piece failed the check as it can restore it + // and mark it as being interesting for download + // TODO: do this more intelligently! and keep track + // of how much crap (data that failed hash-check) and + // how much redundant data we have downloaded + // if some clients has sent more than one piece + // start with redownloading the pieces that the client + // that has sent the least number of pieces + m_picker->restore_piece(index); + m_storage->mark_failed(index); + + assert(m_have_pieces[index] == false); + } + + void torrent::abort() + { + INVARIANT_CHECK; + + m_abort = true; + // if the torrent is paused, it doesn't need + // to announce with even=stopped again. + if (!m_paused) + m_event = tracker_request::stopped; + // disconnect all peers and close all + // files belonging to the torrents + disconnect_all(); + if (m_storage.get()) m_storage->release_files(); + } + + void torrent::announce_piece(int index) + { +// INVARIANT_CHECK; + + assert(index >= 0); + assert(index < m_torrent_file.num_pieces()); + + 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; + std::copy(downloaders.begin(), downloaders.end(), std::inserter(peers, peers.begin())); + + if (!m_have_pieces[index]) + m_num_pieces++; + m_have_pieces[index] = true; + + assert(std::accumulate(m_have_pieces.begin(), m_have_pieces.end(), 0) + == m_num_pieces); + + m_picker->we_have(index); + for (peer_iterator i = m_connections.begin(); i != m_connections.end(); ++i) + try { i->second->announce_piece(index); } catch (std::exception&) {} + + for (std::set::iterator i = peers.begin() + , end(peers.end()); i != end; ++i) + { + peer_iterator p = m_connections.find(*i); + if (p == m_connections.end()) continue; + p->second->received_valid_data(index); + } + +#ifndef TORRENT_DISABLE_EXTENSIONS + for (extension_list_t::iterator i = m_extensions.begin() + , end(m_extensions.end()); i != end; ++i) + { + try { (*i)->on_piece_pass(index); } catch (std::exception&) {} + } +#endif + if (is_seed()) + { + m_picker.reset(); + m_torrent_file.seed_free(); + } + } + + std::string torrent::tracker_login() const + { + if (m_username.empty() && m_password.empty()) return ""; + return m_username + ":" + m_password; + } + + + + void torrent::set_piece_priority(int index, int priority) + { + INVARIANT_CHECK; + + assert(valid_metadata()); + if (is_seed()) return; + + // this call is only valid on torrents with metadata + assert(m_picker.get()); + assert(index >= 0); + assert(index < m_torrent_file.num_pieces()); + + m_picker->set_piece_priority(index, priority); + update_peer_interest(); + } + + int torrent::piece_priority(int index) const + { + INVARIANT_CHECK; + + assert(valid_metadata()); + if (is_seed()) return 1; + + // this call is only valid on torrents with metadata + assert(m_picker.get()); + assert(index >= 0); + assert(index < m_torrent_file.num_pieces()); + + return m_picker->piece_priority(index); + } + + void torrent::prioritize_pieces(std::vector const& pieces) + { + INVARIANT_CHECK; + + // this call is only valid on torrents with metadata + assert(valid_metadata()); + if (is_seed()) return; + + assert(m_picker.get()); + + int index = 0; + for (std::vector::const_iterator i = pieces.begin() + , end(pieces.end()); i != end; ++i, ++index) + { + assert(*i >= 0); + assert(*i <= 7); + m_picker->set_piece_priority(index, *i); + } + update_peer_interest(); + } + + void torrent::piece_priorities(std::vector& pieces) const + { + INVARIANT_CHECK; + + // this call is only valid on torrents with metadata + assert(valid_metadata()); + if (is_seed()) + { + pieces.clear(); + pieces.resize(m_torrent_file.num_pieces(), 1); + return; + } + + assert(m_picker.get()); + m_picker->piece_priorities(pieces); + } + + namespace + { + void set_if_greater(int& piece_prio, int file_prio) + { + if (file_prio > piece_prio) piece_prio = file_prio; + } + } + + void torrent::prioritize_files(std::vector const& files) + { + 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 + assert(int(files.size()) == m_torrent_file.num_files()); + + size_type position = 0; + + if (m_torrent_file.num_pieces() == 0) return; + + int 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); + for (int i = 0; i < int(files.size()); ++i) + { + size_type start = position; + size_type size = m_torrent_file.file_at(i).size; + if (size == 0) continue; + position += size; + // 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 start_piece = int(start / piece_length); + int last_piece = int((position - 1) / piece_length); + 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 + , bind(&set_if_greater, _1, files[i])); + } + prioritize_pieces(pieces); + update_peer_interest(); + } + + // updates the interested flag in peers + void torrent::update_peer_interest() + { + for (peer_iterator i = begin(); i != end(); ++i) + i->second->update_interest(); + } + + void torrent::filter_piece(int index, bool filter) + { + INVARIANT_CHECK; + + assert(valid_metadata()); + if (is_seed()) return; + + // this call is only valid on torrents with metadata + assert(m_picker.get()); + assert(index >= 0); + assert(index < m_torrent_file.num_pieces()); + + m_picker->set_piece_priority(index, filter ? 1 : 0); + update_peer_interest(); + } + + void torrent::filter_pieces(std::vector const& bitmask) + { + INVARIANT_CHECK; + + // this call is only valid on torrents with metadata + assert(valid_metadata()); + if (is_seed()) return; + + assert(m_picker.get()); + + 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(); + } + + bool torrent::is_piece_filtered(int index) const + { + // this call is only valid on torrents with metadata + assert(valid_metadata()); + if (is_seed()) return false; + + assert(m_picker.get()); + assert(index >= 0); + assert(index < m_torrent_file.num_pieces()); + + 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 + assert(valid_metadata()); + if (is_seed()) + { + bitmask.clear(); + bitmask.resize(m_torrent_file.num_pieces(), false); + return; + } + + 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 + assert((int)bitmask.size() == m_torrent_file.num_files()); + + size_type position = 0; + + if (m_torrent_file.num_pieces()) + { + int piece_length = m_torrent_file.piece_length(); + // mark all pieces as filtered, then clear the bits for files + // that should be downloaded + std::vector piece_filter(m_torrent_file.num_pieces(), true); + for (int i = 0; i < (int)bitmask.size(); ++i) + { + size_type start = position; + position += m_torrent_file.file_at(i).size; + // is the file selected for download? + if (!bitmask[i]) + { + // mark all pieces of the file as downloadable + int start_piece = int(start / piece_length); + int last_piece = int(position / piece_length); + // if one piece spans several files, we might + // come here several times with the same start_piece, end_piece + std::fill(piece_filter.begin() + start_piece, piece_filter.begin() + + last_piece + 1, false); + } + } + filter_pieces(piece_filter); + } + } + + void torrent::replace_trackers(std::vector const& urls) + { + assert(!urls.empty()); + m_trackers = urls; + if (m_currently_trying_tracker >= (int)m_trackers.size()) + m_currently_trying_tracker = (int)m_trackers.size()-1; + m_last_working_tracker = -1; + } + + tracker_request torrent::generate_tracker_request() + { + INVARIANT_CHECK; + + assert(!m_trackers.empty()); + + m_next_request = time_now() + seconds(tracker_retry_delay_max); + + tracker_request req; + req.info_hash = m_torrent_file.info_hash(); + req.pid = m_ses.get_peer_id(); + req.downloaded = m_stat.total_payload_download(); + req.uploaded = m_stat.total_payload_upload(); + req.left = bytes_left(); + if (req.left == -1) req.left = 16*1024; + req.event = m_event; + + if (m_event != tracker_request::stopped) + m_event = tracker_request::none; + req.url = m_trackers[m_currently_trying_tracker].url; + req.num_want = 50; + // if we are aborting. we don't want any new peers + if (req.event == tracker_request::stopped) + req.num_want = 0; + + // default initialize, these should be set by caller + // before passing the request to the tracker_manager + req.listen_port = 0; + req.key = 0; + + return req; + } + + void torrent::remove_peer(peer_connection* p) try + { + INVARIANT_CHECK; + + assert(p != 0); + + peer_iterator i = m_connections.find(p->remote()); + if (i == m_connections.end()) + { + assert(false); + return; + } + + if (ready_for_connections()) + { + assert(p->associated_torrent().lock().get() == this); + + if (p->is_seed()) + { + if (m_picker.get()) + { + assert(!is_seed()); + m_picker->dec_refcount_all(); + } + } + else + { + // if we're a seed, we don't keep track of piece availability + if (!is_seed()) + { + const std::vector& pieces = p->get_bitfield(); + + for (std::vector::const_iterator i = pieces.begin(); + i != pieces.end(); ++i) + { + if (*i) peer_lost(static_cast(i - pieces.begin())); + } + } + } + } + + m_policy->connection_closed(*p); + p->set_peer_info(0); + m_connections.erase(i); +#ifndef NDEBUG + m_policy->check_invariant(); +#endif + } + catch (std::exception& e) + { +#ifndef NDEBUG + std::string err = e.what(); +#endif + assert(false); + }; + + void torrent::connect_to_url_seed(std::string const& url) + { + INVARIANT_CHECK; + +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + (*m_ses.m_logger) << time_now_string() << " resolving: " << url << "\n"; +#endif + + m_resolving_web_seeds.insert(url); + proxy_settings const& ps = m_ses.web_seed_proxy(); + if (ps.type == proxy_settings::http + || ps.type == proxy_settings::http_pw) + { + // use proxy + tcp::resolver::query q(ps.hostname + , boost::lexical_cast(ps.port)); + m_host_resolver.async_resolve(q, m_ses.m_strand.wrap( + bind(&torrent::on_proxy_name_lookup, shared_from_this(), _1, _2, url))); + } + else + { + std::string protocol; + std::string auth; + std::string hostname; + int port; + std::string path; + boost::tie(protocol, auth, hostname, port, path) + = parse_url_components(url); + + // TODO: should auth be used here? + + tcp::resolver::query q(hostname, boost::lexical_cast(port)); + m_host_resolver.async_resolve(q, m_ses.m_strand.wrap( + bind(&torrent::on_name_lookup, shared_from_this(), _1, _2, url + , tcp::endpoint()))); + } + + } + + void torrent::on_proxy_name_lookup(asio::error_code const& e, tcp::resolver::iterator host + , std::string url) try + { + session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); + + INVARIANT_CHECK; + +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + (*m_ses.m_logger) << time_now_string() << " completed resolve proxy hostname for: " << url << "\n"; +#endif + + if (e || host == tcp::resolver::iterator()) + { + if (m_ses.m_alerts.should_post(alert::warning)) + { + std::stringstream msg; + msg << "HTTP seed proxy hostname lookup failed: " << e.message(); + m_ses.m_alerts.post_alert( + url_seed_alert(get_handle(), url, msg.str())); + } + + // the name lookup failed for the http host. Don't try + // this host again + remove_url_seed(url); + return; + } + + if (m_ses.is_aborted()) return; + + tcp::endpoint a(host->endpoint()); + + using boost::tuples::ignore; + std::string hostname; + int port; + boost::tie(ignore, ignore, hostname, port, ignore) + = parse_url_components(url); + + if (m_ses.m_ip_filter.access(a.address()) & ip_filter::blocked) + { + if (m_ses.m_alerts.should_post(alert::info)) + { + m_ses.m_alerts.post_alert(peer_blocked_alert(a.address() + , "proxy (" + hostname + ") blocked by IP filter")); + } + return; + } + + tcp::resolver::query q(hostname, boost::lexical_cast(port)); + m_host_resolver.async_resolve(q, m_ses.m_strand.wrap( + bind(&torrent::on_name_lookup, shared_from_this(), _1, _2, url, a))); + } + catch (std::exception& exc) + { + assert(false); + }; + + void torrent::on_name_lookup(asio::error_code const& e, tcp::resolver::iterator host + , std::string url, tcp::endpoint proxy) try + { + session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); + + INVARIANT_CHECK; + +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + (*m_ses.m_logger) << time_now_string() << " completed resolve: " << url << "\n"; +#endif + + std::set::iterator i = m_resolving_web_seeds.find(url); + if (i != m_resolving_web_seeds.end()) m_resolving_web_seeds.erase(i); + + if (e || host == tcp::resolver::iterator()) + { + if (m_ses.m_alerts.should_post(alert::warning)) + { + std::stringstream msg; + msg << "HTTP seed hostname lookup failed: " << e.message(); + m_ses.m_alerts.post_alert( + url_seed_alert(get_handle(), url, msg.str())); + } +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + (*m_ses.m_logger) << " ** HOSTNAME LOOKUP FAILED!**: " << url << "\n"; +#endif + + // the name lookup failed for the http host. Don't try + // this host again + remove_url_seed(url); + return; + } + + if (m_ses.is_aborted()) return; + + tcp::endpoint a(host->endpoint()); + + if (m_ses.m_ip_filter.access(a.address()) & ip_filter::blocked) + { + if (m_ses.m_alerts.should_post(alert::info)) + { + m_ses.m_alerts.post_alert(peer_blocked_alert(a.address() + , "web seed (" + url + ") blocked by IP filter")); + } + return; + } + + peer_iterator conn = m_connections.find(a); + if (conn != m_connections.end()) + { + if (dynamic_cast(conn->second) == 0 + || conn->second->is_disconnecting()) conn->second->disconnect(); + else return; + } + + boost::shared_ptr s + = instantiate_connection(m_ses.m_io_service, m_ses.web_seed_proxy()); + if (m_ses.web_seed_proxy().type == proxy_settings::http + || m_ses.web_seed_proxy().type == proxy_settings::http_pw) + { + // the web seed connection will talk immediately to + // the proxy, without requiring CONNECT support + s->get().set_no_connect(true); + } + boost::intrusive_ptr c(new web_peer_connection( + m_ses, shared_from_this(), s, a, url, 0)); + +#ifndef NDEBUG + 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(c.get())); + if (pp) c->add_extension(pp); + } +#endif + + try + { + assert(m_connections.find(a) == m_connections.end()); + + // add the newly connected peer to this torrent's peer list + m_connections.insert( + std::make_pair(a, boost::get_pointer(c))); + m_ses.m_connections.insert(std::make_pair(s, c)); + + m_ses.m_half_open.enqueue( + bind(&peer_connection::connect, c, _1) + , bind(&peer_connection::timed_out, c) + , seconds(settings().peer_connect_timeout)); + } + catch (std::exception& e) + { +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + (*m_ses.m_logger) << " ** HOSTNAME LOOKUP FAILED!**: " << e.what() << "\n"; +#endif + + // TODO: post an error alert! + std::map::iterator i = m_connections.find(a); + if (i != m_connections.end()) m_connections.erase(i); + m_ses.connection_failed(s, a, e.what()); + c->disconnect(); + } + } + catch (std::exception& exc) + { +#ifndef NDEBUG + std::cerr << exc.what() << std::endl; +#endif + assert(false); + }; + +#ifndef TORRENT_DISABLE_RESOLVE_COUNTRIES + void torrent::resolve_peer_country(boost::intrusive_ptr const& p) const + { + if (m_resolving_country + || p->has_country() + || p->is_connecting() + || p->is_queued() + || p->in_handshake()) return; + + m_resolving_country = true; + tcp::resolver::query q(boost::lexical_cast(p->remote().address()) + + ".zz.countries.nerd.dk", "0"); + m_host_resolver.async_resolve(q, m_ses.m_strand.wrap( + 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(asio::error_code const& error, tcp::resolver::iterator i + , intrusive_ptr p) const + { + session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); + + INVARIANT_CHECK; + + m_resolving_country = false; + + // must be ordered in increasing order + 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 || i == tcp::resolver::iterator()) + { + // this is used to indicate that we shouldn't + // try to resolve it again + p->set_country("--"); + return; + } + + while (i != tcp::resolver::iterator() + && !i->endpoint().address().is_v4()) ++i; + if (i != tcp::resolver::iterator()) + { + // country is an ISO 3166 country code + int country = i->endpoint().address().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* i = + std::lower_bound(country_map, country_map + size, tmp + , bind(&country_entry::code, _1) < bind(&country_entry::code, _2)); + if (i == country_map + size + || i->code != country) + { + // unknown country! + p->set_country("!!"); +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + (*m_ses.m_logger) << "IP " << p->remote().address() << " was mapped to unknown country: " << country << "\n"; +#endif + return; + } + + p->set_country(i->name); + } + } +#endif + + peer_connection* torrent::connect_to_peer(policy::peer* peerinfo) + { + INVARIANT_CHECK; + + assert(peerinfo); + assert(peerinfo->connection == 0); +#ifndef NDEBUG + // this asserts that we don't have duplicates in the policy's peer list + peer_iterator i_ = m_connections.find(peerinfo->ip); + assert(i_ == m_connections.end() + || i_->second->is_disconnecting() + || dynamic_cast(i_->second) == 0 + || m_ses.settings().allow_multiple_connections_per_ip); +#endif + + assert(want_more_peers()); + + tcp::endpoint const& a(peerinfo->ip); + assert((m_ses.m_ip_filter.access(a.address()) & ip_filter::blocked) == 0); + + boost::shared_ptr s + = instantiate_connection(m_ses.m_io_service, m_ses.peer_proxy()); + boost::intrusive_ptr c(new bt_peer_connection( + m_ses, shared_from_this(), s, a, peerinfo)); + +#ifndef NDEBUG + 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(c.get())); + if (pp) c->add_extension(pp); + } +#endif + + try + { + // add the newly connected peer to this torrent's peer list + m_connections.insert( + std::make_pair(a, boost::get_pointer(c))); + m_ses.m_connections.insert(std::make_pair(s, c)); + + m_ses.m_half_open.enqueue( + bind(&peer_connection::connect, c, _1) + , bind(&peer_connection::timed_out, c) + , seconds(settings().peer_connect_timeout)); + } + catch (std::exception& e) + { + // TODO: post an error alert! + std::map::iterator i = m_connections.find(a); + if (i != m_connections.end()) m_connections.erase(i); + m_ses.connection_failed(s, a, e.what()); + c->disconnect(); + throw; + } + if (c->is_disconnecting()) throw protocol_error("failed to connect"); + return c.get(); + } + + void torrent::set_metadata(entry const& metadata) + { + m_torrent_file.parse_info_section(metadata); + + boost::mutex::scoped_lock(m_checker.m_mutex); + + boost::shared_ptr d( + new aux::piece_checker_data); + d->torrent_ptr = shared_from_this(); + d->save_path = m_save_path; + d->info_hash = m_torrent_file.info_hash(); + // add the torrent to the queue to be checked + m_checker.m_torrents.push_back(d); + typedef session_impl::torrent_map torrent_map; + torrent_map::iterator i = m_ses.m_torrents.find( + m_torrent_file.info_hash()); + assert(i != m_ses.m_torrents.end()); + m_ses.m_torrents.erase(i); + // and notify the thread that it got another + // job in its queue + m_checker.m_cond.notify_one(); + + if (m_ses.m_alerts.should_post(alert::info)) + { + m_ses.m_alerts.post_alert(metadata_received_alert( + get_handle(), "metadata successfully received from swarm")); + } + } + + void torrent::attach_peer(peer_connection* p) + { + INVARIANT_CHECK; + + assert(p != 0); + assert(!p->is_local()); + + std::map::iterator c + = m_connections.find(p->remote()); + if (c != m_connections.end()) + { + // we already have a peer_connection to this ip. + // It may currently be waiting for completing a + // connection attempt that might fail. So, + // prioritize this current connection since + // it has already succeeded. + if (!c->second->is_connecting()) + { + throw protocol_error("already connected to peer"); + } + c->second->disconnect(); + } + + if (m_ses.m_connections.find(p->get_socket()) + == m_ses.m_connections.end()) + { + throw protocol_error("peer is not properly constructed"); + } + + if (m_ses.is_aborted()) + { + throw protocol_error("session is closing"); + } + + peer_iterator ci = m_connections.insert( + std::make_pair(p->remote(), p)).first; + try + { + // if new_connection throws, we have to remove the + // it from the list. + +#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(p)); + if (pp) p->add_extension(pp); + } +#endif + m_policy->new_connection(*ci->second); + } + catch (std::exception& e) + { + m_connections.erase(ci); + throw; + } + assert(p->remote() == p->get_socket()->remote_endpoint()); + +#ifndef NDEBUG + m_policy->check_invariant(); +#endif + } + + bool torrent::want_more_peers() const + { + return int(m_connections.size()) < m_connections_quota.given + && m_ses.m_half_open.free_slots(); + } + + void torrent::disconnect_all() + { + session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); + + INVARIANT_CHECK; + + while (!m_connections.empty()) + { + peer_connection& p = *m_connections.begin()->second; + assert(p.associated_torrent().lock().get() == this); + +#if defined(TORRENT_VERBOSE_LOGGING) + if (m_abort) + (*p.m_logger) << "*** CLOSING CONNECTION 'aborting'\n"; + else + (*p.m_logger) << "*** CLOSING CONNECTION 'pausing'\n"; +#endif +#ifndef NDEBUG + std::size_t size = m_connections.size(); +#endif + p.disconnect(); + assert(m_connections.size() <= size); + } + } + + bool torrent::request_bandwidth_from_session(int channel) const + { + int max_assignable = m_bandwidth_limit[channel].max_assignable(); + return max_assignable > max_bandwidth_block_size + || (m_bandwidth_limit[channel].throttle() < max_bandwidth_block_size + && max_assignable == m_bandwidth_limit[channel].throttle()); + } + + int torrent::bandwidth_throttle(int channel) const + { + return m_bandwidth_limit[channel].throttle(); + } + + void torrent::request_bandwidth(int channel + , boost::intrusive_ptr p + , bool non_prioritized) + { + if (request_bandwidth_from_session(channel)) + { + if (channel == peer_connection::upload_channel) + m_ses.m_ul_bandwidth_manager.request_bandwidth(p, non_prioritized); + else if (channel == peer_connection::download_channel) + m_ses.m_dl_bandwidth_manager.request_bandwidth(p, non_prioritized); + + m_bandwidth_limit[channel].assign(max_bandwidth_block_size); + } + else + { + m_bandwidth_queue[channel].push_back(bw_queue_entry(p, non_prioritized)); + } + } + + void torrent::expire_bandwidth(int channel, int amount) + { + session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); + + assert(amount >= -1); + if (amount == -1) amount = max_bandwidth_block_size; + m_bandwidth_limit[channel].expire(amount); + + while (!m_bandwidth_queue[channel].empty() + && request_bandwidth_from_session(channel)) + { + bw_queue_entry qe = m_bandwidth_queue[channel].front(); + m_bandwidth_queue[channel].pop_front(); + if (channel == peer_connection::upload_channel) + m_ses.m_ul_bandwidth_manager.request_bandwidth(qe.peer, qe.non_prioritized); + else if (channel == peer_connection::download_channel) + m_ses.m_dl_bandwidth_manager.request_bandwidth(qe.peer, qe.non_prioritized); + m_bandwidth_limit[channel].assign(max_bandwidth_block_size); + } + } + + void torrent::assign_bandwidth(int channel, int amount) + { + session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); + + assert(amount >= 0); + if (amount < max_bandwidth_block_size) + expire_bandwidth(channel, max_bandwidth_block_size - amount); + } + + // called when torrent is finished (all interested pieces downloaded) + void torrent::finished() + { + INVARIANT_CHECK; + + if (alerts().should_post(alert::info)) + { + alerts().post_alert(torrent_finished_alert( + get_handle() + , "torrent has finished downloading")); + } + + // disconnect all seeds + // TODO: should disconnect all peers that have the pieces we have + // not just seeds + std::vector seeds; + for (peer_iterator i = m_connections.begin(); + i != m_connections.end(); ++i) + { + assert(i->second->associated_torrent().lock().get() == this); + if (i->second->is_seed()) + { +#if defined(TORRENT_VERBOSE_LOGGING) + (*i->second->m_logger) << "*** SEED, CLOSING CONNECTION\n"; +#endif + seeds.push_back(i->second); + } + } + std::for_each(seeds.begin(), seeds.end() + , bind(&peer_connection::disconnect, _1)); + + m_storage->release_files(); + } + + // called when torrent is complete (all pieces downloaded) + void torrent::completed() + { + INVARIANT_CHECK; + + // make the next tracker request + // be a completed-event + m_event = tracker_request::completed; + force_tracker_request(); + } + + // this will move the tracker with the given index + // to a prioritized position in the list (move it towards + // the begining) and return the new index to the tracker. + int torrent::prioritize_tracker(int index) + { + INVARIANT_CHECK; + + assert(index >= 0); + if (index >= (int)m_trackers.size()) return (int)m_trackers.size()-1; + + while (index > 0 && m_trackers[index].tier == m_trackers[index-1].tier) + { + std::swap(m_trackers[index].url, m_trackers[index-1].url); + --index; + } + return index; + } + + void torrent::try_next_tracker() + { + INVARIANT_CHECK; + + ++m_currently_trying_tracker; + + if ((unsigned)m_currently_trying_tracker >= m_trackers.size()) + { + int delay = tracker_retry_delay_min + + std::min(m_failed_trackers, (int)tracker_failed_max) + * (tracker_retry_delay_max - tracker_retry_delay_min) + / tracker_failed_max; + + ++m_failed_trackers; + // if we've looped the tracker list, wait a bit before retrying + m_currently_trying_tracker = 0; + m_next_request = time_now() + seconds(delay); + +#ifndef TORRENT_DISABLE_DHT + // only start the announce if we want to announce with the dht + if (should_announce_dht()) + { + // force the DHT to reannounce + m_last_dht_announce = time_now() - minutes(15); + boost::weak_ptr self(shared_from_this()); + m_announce_timer.expires_from_now(seconds(1)); + m_announce_timer.async_wait(m_ses.m_strand.wrap( + bind(&torrent::on_announce_disp, self, _1))); + } +#endif + + } + else + { + // don't delay before trying the next tracker + m_next_request = time_now(); + } + + } + + bool torrent::check_fastresume(aux::piece_checker_data& data) + { + INVARIANT_CHECK; + + if (!m_storage.get()) + { + // this means we have received the metadata through the + // metadata extension, and we have to initialize + init(); + } + + assert(m_storage.get()); + bool done = true; + try + { + done = m_storage->check_fastresume(data, m_have_pieces, m_num_pieces + , m_compact_mode); + } + catch (std::exception& e) + { + // probably means file permission failure or invalid filename + std::fill(m_have_pieces.begin(), m_have_pieces.end(), false); + m_num_pieces = 0; + + if (m_ses.m_alerts.should_post(alert::fatal)) + { + m_ses.m_alerts.post_alert( + file_error_alert( + get_handle() + , e.what())); + } + pause(); + } +#ifndef NDEBUG + m_initial_done = boost::get<0>(bytes_done()); +#endif + return done; + } + + std::pair torrent::check_files() + { + INVARIANT_CHECK; + + assert(m_storage.get()); + + std::pair progress(true, 1.f); + try + { + progress = m_storage->check_files(m_have_pieces, m_num_pieces + , m_ses.m_mutex); + } + catch (std::exception& e) + { + // probably means file permission failure or invalid filename + std::fill(m_have_pieces.begin(), m_have_pieces.end(), false); + m_num_pieces = 0; + + if (m_ses.m_alerts.should_post(alert::fatal)) + { + m_ses.m_alerts.post_alert( + file_error_alert( + get_handle() + , e.what())); + } + pause(); + } + +#ifndef NDEBUG + m_initial_done = boost::get<0>(bytes_done()); +#endif + return progress; + } + + void torrent::files_checked(std::vector const& + unfinished_pieces) + { + session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); + + INVARIANT_CHECK; + + if (!is_seed()) + { + m_picker->files_checked(m_have_pieces, unfinished_pieces); + if (m_sequenced_download_threshold > 0) + picker().set_sequenced_download_threshold(m_sequenced_download_threshold); + } + +#ifndef TORRENT_DISABLE_EXTENSIONS + for (extension_list_t::iterator i = m_extensions.begin() + , end(m_extensions.end()); i != end; ++i) + { + try { (*i)->on_files_checked(); } catch (std::exception&) {} + } +#endif + + if (is_seed()) + { + m_picker.reset(); + m_torrent_file.seed_free(); + } + + if (!m_connections_initialized) + { + m_connections_initialized = true; + // all peer connections have to initialize themselves now that the metadata + // is available + typedef std::map conn_map; + for (conn_map::iterator i = m_connections.begin() + , end(m_connections.end()); i != end;) + { + try + { + i->second->init(); + i->second->on_metadata(); + ++i; + } + catch (std::exception& e) + { + // the connection failed, close it + conn_map::iterator j = i; + ++j; + m_ses.connection_failed(i->second->get_socket() + , i->first, e.what()); + i = j; + } + } + } +#ifndef NDEBUG + m_initial_done = boost::get<0>(bytes_done()); +#endif + } + + alert_manager& torrent::alerts() const + { + return m_ses.m_alerts; + } + + boost::filesystem::path torrent::save_path() const + { + return m_save_path; + } + + bool torrent::move_storage(boost::filesystem::path const& save_path) + { + INVARIANT_CHECK; + + bool ret = true; + if (m_storage.get()) + { + ret = m_storage->move_storage(save_path); + m_save_path = m_storage->save_path(); + } + else + { + m_save_path = save_path; + } + return ret; + } + + piece_manager& torrent::filesystem() + { + INVARIANT_CHECK; + + assert(m_storage.get()); + return *m_storage; + } + + + torrent_handle torrent::get_handle() const + { + INVARIANT_CHECK; + + return torrent_handle(&m_ses, &m_checker, m_torrent_file.info_hash()); + } + + session_settings const& torrent::settings() const + { +// INVARIANT_CHECK; + + return m_ses.settings(); + } + +#ifndef NDEBUG + void torrent::check_invariant() const + { +// size_type download = m_stat.total_payload_download(); +// size_type done = boost::get<0>(bytes_done()); +// assert(download >= done - m_initial_done); + for (const_peer_iterator i = begin(); i != end(); ++i) + { + peer_connection const& p = *i->second; + torrent* associated_torrent = p.associated_torrent().lock().get(); + if (associated_torrent != this) + assert(false); + } + + if (valid_metadata()) + { + assert(int(m_have_pieces.size()) == m_torrent_file.num_pieces()); + } + else + { + assert(m_have_pieces.empty()); + } + + size_type total_done = quantized_bytes_done(); + if (m_torrent_file.is_valid()) + { + if (is_seed()) + assert(total_done == m_torrent_file.total_size()); + else + assert(total_done != m_torrent_file.total_size()); + } + else + { + assert(total_done == 0); + } + +// This check is very expensive. + assert(m_num_pieces + == std::count(m_have_pieces.begin(), m_have_pieces.end(), true)); + assert(!valid_metadata() || m_block_size > 0); + assert(!valid_metadata() || (m_torrent_file.piece_length() % m_block_size) == 0); +// if (is_seed()) assert(m_picker.get() == 0); + } +#endif + + void torrent::set_sequenced_download_threshold(int threshold) + { + if (has_picker()) + { + picker().set_sequenced_download_threshold(threshold); + } + else + { + m_sequenced_download_threshold = threshold; + } + } + + + void torrent::set_max_uploads(int limit) + { + assert(limit >= -1); + if (limit == -1) limit = std::numeric_limits::max(); + m_uploads_quota.max = std::max(m_uploads_quota.min, limit); + } + + void torrent::set_max_connections(int limit) + { + assert(limit >= -1); + if (limit == -1) limit = std::numeric_limits::max(); + m_connections_quota.max = std::max(m_connections_quota.min, limit); + } + + void torrent::set_peer_upload_limit(tcp::endpoint ip, int limit) + { + assert(limit >= -1); + peer_connection* p = connection_for(ip); + if (p == 0) return; + p->set_upload_limit(limit); + } + + void torrent::set_peer_download_limit(tcp::endpoint ip, int limit) + { + assert(limit >= -1); + peer_connection* p = connection_for(ip); + if (p == 0) return; + p->set_download_limit(limit); + } + + void torrent::set_upload_limit(int limit) + { + assert(limit >= -1); + if (limit == -1) limit = std::numeric_limits::max(); + if (limit < num_peers() * 10) limit = num_peers() * 10; + m_bandwidth_limit[peer_connection::upload_channel].throttle(limit); + } + + int torrent::upload_limit() const + { + int limit = m_bandwidth_limit[peer_connection::upload_channel].throttle(); + if (limit == std::numeric_limits::max()) limit = -1; + return limit; + } + + void torrent::set_download_limit(int limit) + { + assert(limit >= -1); + if (limit == -1) limit = std::numeric_limits::max(); + if (limit < num_peers() * 10) limit = num_peers() * 10; + m_bandwidth_limit[peer_connection::download_channel].throttle(limit); + } + + int torrent::download_limit() const + { + int limit = m_bandwidth_limit[peer_connection::download_channel].throttle(); + if (limit == std::numeric_limits::max()) limit = -1; + return limit; + } + + void torrent::pause() + { + INVARIANT_CHECK; + + if (m_paused) return; + +#ifndef TORRENT_DISABLE_EXTENSIONS + for (extension_list_t::iterator i = m_extensions.begin() + , end(m_extensions.end()); i != end; ++i) + { + try { if ((*i)->on_pause()) return; } catch (std::exception&) {} + } +#endif + + disconnect_all(); + m_paused = true; + // tell the tracker that we stopped + m_event = tracker_request::stopped; + m_just_paused = true; + // this will make the storage close all + // files and flush all cached data + if (m_storage.get()) m_storage->release_files(); + } + + void torrent::resume() + { + INVARIANT_CHECK; + + if (!m_paused) return; + +#ifndef TORRENT_DISABLE_EXTENSIONS + for (extension_list_t::iterator i = m_extensions.begin() + , end(m_extensions.end()); i != end; ++i) + { + try { if ((*i)->on_resume()) return; } catch (std::exception&) {} + } +#endif + + m_paused = false; + m_uploads_quota.min = 2; + m_connections_quota.min = 2; + m_uploads_quota.max = std::numeric_limits::max(); + m_connections_quota.max = std::numeric_limits::max(); + + // tell the tracker that we're back + m_event = tracker_request::started; + force_tracker_request(); + + // make pulse be called as soon as possible + m_time_scaler = 0; + } + + void torrent::second_tick(stat& accumulator, float tick_interval) + { + INVARIANT_CHECK; + + m_connections_quota.used = (int)m_connections.size(); + m_uploads_quota.used = m_policy->num_uploads(); + +#ifndef TORRENT_DISABLE_EXTENSIONS + for (extension_list_t::iterator i = m_extensions.begin() + , end(m_extensions.end()); i != end; ++i) + { + try { (*i)->tick(); } catch (std::exception&) {} + } +#endif + + if (m_paused) + { + // let the stats fade out to 0 + m_stat.second_tick(tick_interval); + m_connections_quota.min = 0; + m_connections_quota.max = 0; + m_uploads_quota.min = 0; + m_uploads_quota.max = 0; + return; + } + + // ---- WEB SEEDS ---- + + // if we have everything we want we don't need to connect to any web-seed + if (!is_finished() && !m_web_seeds.empty()) + { + // keep trying web-seeds if there are any + // first find out which web seeds we are connected to + std::set web_seeds; + for (peer_iterator i = m_connections.begin(); + i != m_connections.end(); ++i) + { + web_peer_connection* p + = dynamic_cast(i->second); + if (!p) continue; + web_seeds.insert(p->url()); + } + + for (std::set::iterator i = m_resolving_web_seeds.begin() + , end(m_resolving_web_seeds.end()); i != end; ++i) + web_seeds.insert(web_seeds.begin(), *i); + + // from the list of available web seeds, subtract the ones we are + // already connected to. + std::vector not_connected_web_seeds; + std::set_difference(m_web_seeds.begin(), m_web_seeds.end(), web_seeds.begin() + , web_seeds.end(), std::back_inserter(not_connected_web_seeds)); + + // connect to all of those that we aren't connected to + std::for_each(not_connected_web_seeds.begin(), not_connected_web_seeds.end() + , bind(&torrent::connect_to_url_seed, this, _1)); + } + + for (peer_iterator i = m_connections.begin(); + i != m_connections.end();) + { + peer_connection* p = i->second; + ++i; + m_stat += p->statistics(); + // updates the peer connection's ul/dl bandwidth + // resource requests + try + { + p->second_tick(tick_interval); + } + catch (std::exception& e) + { +#ifdef TORRENT_VERBOSE_LOGGING + (*p->m_logger) << "**ERROR**: " << e.what() << "\n"; +#endif + p->set_failed(); + p->disconnect(); + } + } + accumulator += m_stat; + m_stat.second_tick(tick_interval); + } + + bool torrent::try_connect_peer() + { + assert(want_more_peers()); + return m_policy->connect_one_peer(); + } + + void torrent::distribute_resources(float tick_interval) + { + INVARIANT_CHECK; + + m_time_scaler--; + if (m_time_scaler <= 0) + { + m_time_scaler = 10; + m_policy->pulse(); + } + } + + bool torrent::verify_piece(int piece_index) + { +// INVARIANT_CHECK; + + assert(m_storage.get()); + assert(piece_index >= 0); + assert(piece_index < m_torrent_file.num_pieces()); + assert(piece_index < (int)m_have_pieces.size()); + + int size = static_cast(m_torrent_file.piece_size(piece_index)); + std::vector buffer(size); + assert(size > 0); + m_storage->read(&buffer[0], piece_index, 0, size); + + hasher h; + h.update(&buffer[0], size); + sha1_hash digest = h.final(); + + if (m_torrent_file.hash_for_piece(piece_index) != digest) + return false; + + return true; + } + + const tcp::endpoint& torrent::current_tracker() const + { + return m_tracker_address; + } + + bool torrent::is_allocating() const + { return m_storage.get() && m_storage->is_allocating(); } + + void torrent::file_progress(std::vector& fp) const + { + assert(valid_metadata()); + + fp.clear(); + fp.resize(m_torrent_file.num_files(), 0.f); + + for (int i = 0; i < m_torrent_file.num_files(); ++i) + { + peer_request ret = m_torrent_file.map_file(i, 0, 0); + size_type size = m_torrent_file.file_at(i).size; + +// zero sized files are considered +// 100% done all the time + if (size == 0) + { + fp[i] = 1.f; + continue; + } + + size_type done = 0; + while (size > 0) + { + size_type bytes_step = std::min(m_torrent_file.piece_size(ret.piece) + - ret.start, size); + if (m_have_pieces[ret.piece]) done += bytes_step; + ++ret.piece; + ret.start = 0; + size -= bytes_step; + } + assert(size == 0); + + fp[i] = static_cast(done) / m_torrent_file.file_at(i).size; + } + } + + torrent_status torrent::status() const + { + INVARIANT_CHECK; + + assert(std::accumulate( + m_have_pieces.begin() + , m_have_pieces.end() + , 0) == m_num_pieces); + + torrent_status st; + + st.num_peers = (int)std::count_if(m_connections.begin(), m_connections.end(), + boost::bind(std::logical_not(), boost::bind(&peer_connection::is_connecting, + boost::bind(&std::map::value_type::second, _1)))); + + st.num_complete = m_complete; + st.num_incomplete = m_incomplete; + st.paused = m_paused; + boost::tie(st.total_done, st.total_wanted_done) = bytes_done(); + + // payload transfer + st.total_payload_download = m_stat.total_payload_download(); + st.total_payload_upload = m_stat.total_payload_upload(); + + // total transfer + st.total_download = m_stat.total_payload_download() + + m_stat.total_protocol_download(); + st.total_upload = m_stat.total_payload_upload() + + m_stat.total_protocol_upload(); + + // failed bytes + st.total_failed_bytes = m_total_failed_bytes; + st.total_redundant_bytes = m_total_redundant_bytes; + + // transfer rate + st.download_rate = m_stat.download_rate(); + st.upload_rate = m_stat.upload_rate(); + st.download_payload_rate = m_stat.download_payload_rate(); + st.upload_payload_rate = m_stat.upload_payload_rate(); + + st.next_announce = boost::posix_time::seconds( + total_seconds(next_announce() - time_now())); + if (st.next_announce.is_negative()) + st.next_announce = boost::posix_time::seconds(0); + + st.announce_interval = boost::posix_time::seconds(m_duration); + + if (m_last_working_tracker >= 0) + { + st.current_tracker + = m_trackers[m_last_working_tracker].url; + } + + // if we don't have any metadata, stop here + + if (!valid_metadata()) + { + if (m_got_tracker_response == false) + st.state = torrent_status::connecting_to_tracker; + else + st.state = torrent_status::downloading_metadata; + +// TODO: add a progress member to the torrent that will be used in this case +// and that may be set by a plugin +// if (m_metadata_size == 0) st.progress = 0.f; +// else st.progress = std::min(1.f, m_metadata_progress / (float)m_metadata_size); + st.progress = 0.f; + + st.block_size = 0; + + return st; + } + + st.block_size = block_size(); + + // fill in status that depends on metadata + + st.total_wanted = m_torrent_file.total_size(); + + if (m_picker.get() && (m_picker->num_filtered() > 0 + || m_picker->num_have_filtered() > 0)) + { + int filtered_pieces = m_picker->num_filtered() + + m_picker->num_have_filtered(); + int last_piece_index = m_torrent_file.num_pieces() - 1; + if (m_picker->piece_priority(last_piece_index) == 0) + { + st.total_wanted -= m_torrent_file.piece_size(last_piece_index); + --filtered_pieces; + } + + st.total_wanted -= filtered_pieces * m_torrent_file.piece_length(); + } + + assert(st.total_wanted >= st.total_wanted_done); + + if (st.total_wanted == 0) st.progress = 1.f; + else st.progress = st.total_wanted_done + / static_cast(st.total_wanted); + + st.pieces = &m_have_pieces; + st.num_pieces = m_num_pieces; + + if (m_got_tracker_response == false) + { + st.state = torrent_status::connecting_to_tracker; + } + else if (is_seed()) + { + assert(st.total_done == m_torrent_file.total_size()); + st.state = torrent_status::seeding; + } + else if (st.total_wanted_done == st.total_wanted) + { + assert(st.total_done != m_torrent_file.total_size()); + st.state = torrent_status::finished; + } + else + { + st.state = torrent_status::downloading; + } + + st.num_seeds = num_seeds(); + if (m_picker.get()) + st.distributed_copies = m_picker->distributed_copies(); + else + st.distributed_copies = -1; + return st; + } + + int torrent::num_seeds() const + { + INVARIANT_CHECK; + + return (int)std::count_if(m_connections.begin(), m_connections.end(), + boost::bind(&peer_connection::is_seed, + boost::bind(&std::map::value_type::second, _1))); + } + + void torrent::tracker_request_timed_out( + tracker_request const&) + { + session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); + + INVARIANT_CHECK; + +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + debug_log("*** tracker timed out"); +#endif + + if (m_ses.m_alerts.should_post(alert::warning)) + { + std::stringstream s; + s << "tracker: \"" + << m_trackers[m_currently_trying_tracker].url + << "\" timed out"; + m_ses.m_alerts.post_alert(tracker_alert(get_handle() + , m_failed_trackers + 1, 0, s.str())); + } + try_next_tracker(); + } + + // TODO: with some response codes, we should just consider + // the tracker as a failure and not retry + // it anymore + void torrent::tracker_request_error(tracker_request const& + , int response_code, const std::string& str) + { + session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); + + INVARIANT_CHECK; + +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + debug_log(std::string("*** tracker error: ") + str); +#endif + if (m_ses.m_alerts.should_post(alert::warning)) + { + std::stringstream s; + s << "tracker: \"" + << m_trackers[m_currently_trying_tracker].url + << "\" " << str; + m_ses.m_alerts.post_alert(tracker_alert(get_handle() + , m_failed_trackers + 1, response_code, s.str())); + } + + try_next_tracker(); + } + + +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + void torrent::debug_log(const std::string& line) + { + (*m_ses.m_logger) << line << "\n"; + } +#endif + +} + diff --git a/encryption/libtorrent/src/torrent_handle.cpp b/encryption/libtorrent/src/torrent_handle.cpp new file mode 100755 index 000000000..da571ab63 --- /dev/null +++ b/encryption/libtorrent/src/torrent_handle.cpp @@ -0,0 +1,774 @@ +/* + +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 "libtorrent/pch.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/peer_id.hpp" +#include "libtorrent/bt_peer_connection.hpp" +#include "libtorrent/torrent_info.hpp" +#include "libtorrent/tracker_manager.hpp" +#include "libtorrent/bencode.hpp" +#include "libtorrent/hasher.hpp" +#include "libtorrent/entry.hpp" +#include "libtorrent/session.hpp" +#include "libtorrent/aux_/session_impl.hpp" +#include "libtorrent/invariant_check.hpp" + +#if defined(_MSC_VER) && _MSC_VER < 1300 +namespace std +{ + using ::srand; + using ::isalnum; +}; +#endif + +using boost::bind; +using boost::mutex; +using libtorrent::aux::session_impl; + +namespace libtorrent +{ + namespace + { + void throw_invalid_handle() + { + throw invalid_handle(); + } + + template + Ret call_member( + session_impl* ses + , aux::checker_impl* chk + , sha1_hash const& hash + , F f) + { + if (ses == 0) throw_invalid_handle(); + + if (chk) + { + mutex::scoped_lock l(chk->m_mutex); + aux::piece_checker_data* d = chk->find_torrent(hash); + if (d != 0) return f(*d->torrent_ptr); + } + + { + session_impl::mutex_t::scoped_lock l(ses->m_mutex); + boost::shared_ptr t = ses->find_torrent(hash).lock(); + if (t) return f(*t); + } + + // throwing directly instead of calling + // the throw_invalid_handle() function + // avoids a warning in gcc + throw invalid_handle(); + } + } + +#ifndef NDEBUG + + void torrent_handle::check_invariant() const + { + assert((m_ses == 0 && m_chk == 0) || (m_ses != 0)); + } + +#endif + + void torrent_handle::set_max_uploads(int max_uploads) const + { + INVARIANT_CHECK; + + assert(max_uploads >= 2 || max_uploads == -1); + + call_member(m_ses, m_chk, m_info_hash + , bind(&torrent::set_max_uploads, _1, max_uploads)); + } + + void torrent_handle::use_interface(const char* net_interface) const + { + INVARIANT_CHECK; + + call_member(m_ses, m_chk, m_info_hash + , bind(&torrent::use_interface, _1, net_interface)); + } + + void torrent_handle::set_max_connections(int max_connections) const + { + INVARIANT_CHECK; + + assert(max_connections >= 2 || max_connections == -1); + + call_member(m_ses, m_chk, m_info_hash + , bind(&torrent::set_max_connections, _1, max_connections)); + } + + void torrent_handle::set_peer_upload_limit(tcp::endpoint ip, int limit) const + { + INVARIANT_CHECK; + assert(limit >= -1); + + call_member(m_ses, m_chk, m_info_hash + , bind(&torrent::set_peer_upload_limit, _1, ip, limit)); + } + + void torrent_handle::set_peer_download_limit(tcp::endpoint ip, int limit) const + { + INVARIANT_CHECK; + assert(limit >= -1); + + call_member(m_ses, m_chk, m_info_hash + , bind(&torrent::set_peer_download_limit, _1, ip, limit)); + } + + void torrent_handle::set_upload_limit(int limit) const + { + INVARIANT_CHECK; + + assert(limit >= -1); + + call_member(m_ses, m_chk, m_info_hash + , bind(&torrent::set_upload_limit, _1, limit)); + } + + int torrent_handle::upload_limit() const + { + INVARIANT_CHECK; + return call_member(m_ses, m_chk, m_info_hash + , bind(&torrent::upload_limit, _1)); + } + + void torrent_handle::set_download_limit(int limit) const + { + INVARIANT_CHECK; + + assert(limit >= -1); + + call_member(m_ses, m_chk, m_info_hash + , bind(&torrent::set_download_limit, _1, limit)); + } + + int torrent_handle::download_limit() const + { + INVARIANT_CHECK; + return call_member(m_ses, m_chk, m_info_hash + , bind(&torrent::download_limit, _1)); + } + + bool torrent_handle::move_storage( + boost::filesystem::path const& save_path) const + { + INVARIANT_CHECK; + + return call_member(m_ses, m_chk, m_info_hash + , bind(&torrent::move_storage, _1, save_path)); + } + + bool torrent_handle::has_metadata() const + { + INVARIANT_CHECK; + + return call_member(m_ses, m_chk, m_info_hash + , bind(&torrent::valid_metadata, _1)); + } + + bool torrent_handle::is_seed() const + { + INVARIANT_CHECK; + + return call_member(m_ses, m_chk, m_info_hash + , bind(&torrent::is_seed, _1)); + } + + bool torrent_handle::is_paused() const + { + INVARIANT_CHECK; + + return call_member(m_ses, m_chk, m_info_hash + , bind(&torrent::is_paused, _1)); + } + + void torrent_handle::pause() const + { + INVARIANT_CHECK; + + call_member(m_ses, m_chk, m_info_hash + , bind(&torrent::pause, _1)); + } + + void torrent_handle::resume() const + { + INVARIANT_CHECK; + + call_member(m_ses, m_chk, m_info_hash + , bind(&torrent::resume, _1)); + } + + void torrent_handle::set_tracker_login(std::string const& name + , std::string const& password) const + { + INVARIANT_CHECK; + + call_member(m_ses, m_chk, m_info_hash + , bind(&torrent::set_tracker_login, _1, name, password)); + } + + void torrent_handle::file_progress(std::vector& progress) + { + INVARIANT_CHECK; + + if (m_ses == 0) throw_invalid_handle(); + + if (m_chk) + { + mutex::scoped_lock l(m_chk->m_mutex); + + aux::piece_checker_data* d = m_chk->find_torrent(m_info_hash); + if (d != 0) + { + if (!d->processing) + { + torrent_info const& info = d->torrent_ptr->torrent_file(); + progress.clear(); + progress.resize(info.num_files(), 0.f); + return; + } + d->torrent_ptr->file_progress(progress); + return; + } + } + + { + session_impl::mutex_t::scoped_lock l(m_ses->m_mutex); + boost::shared_ptr t = m_ses->find_torrent(m_info_hash).lock(); + if (t) return t->file_progress(progress); + } + + throw_invalid_handle(); + } + + torrent_status torrent_handle::status() const + { + INVARIANT_CHECK; + + if (m_ses == 0) throw_invalid_handle(); + + if (m_chk) + { + mutex::scoped_lock l(m_chk->m_mutex); + + aux::piece_checker_data* d = m_chk->find_torrent(m_info_hash); + if (d != 0) + { + torrent_status st; + + if (d->processing) + { + if (d->torrent_ptr->is_allocating()) + st.state = torrent_status::allocating; + else + st.state = torrent_status::checking_files; + } + else + st.state = torrent_status::queued_for_checking; + st.progress = d->progress; + st.paused = d->torrent_ptr->is_paused(); + return st; + } + } + + { + session_impl::mutex_t::scoped_lock l(m_ses->m_mutex); + boost::shared_ptr t = m_ses->find_torrent(m_info_hash).lock(); + if (t) return t->status(); + } + + throw_invalid_handle(); + return torrent_status(); + } + + void torrent_handle::set_sequenced_download_threshold(int threshold) const + { + INVARIANT_CHECK; + call_member(m_ses, m_chk, m_info_hash + , bind(&torrent::set_sequenced_download_threshold, _1, threshold)); + } + + std::string torrent_handle::name() const + { + INVARIANT_CHECK; + return call_member(m_ses, m_chk, m_info_hash + , bind(&torrent::name, _1)); + } + + + void torrent_handle::piece_priority(int index, int priority) const + { + INVARIANT_CHECK; + + call_member(m_ses, m_chk, m_info_hash + , bind(&torrent::set_piece_priority, _1, index, priority)); + } + + int torrent_handle::piece_priority(int index) const + { + INVARIANT_CHECK; + + return call_member(m_ses, m_chk, m_info_hash + , bind(&torrent::piece_priority, _1, index)); + } + + void torrent_handle::prioritize_pieces(std::vector const& pieces) const + { + INVARIANT_CHECK; + + call_member(m_ses, m_chk, m_info_hash + , bind(&torrent::prioritize_pieces, _1, boost::cref(pieces))); + } + + std::vector torrent_handle::piece_priorities() const + { + INVARIANT_CHECK; + std::vector ret; + call_member(m_ses, m_chk, m_info_hash + , bind(&torrent::piece_priorities, _1, boost::ref(ret))); + return ret; + } + + void torrent_handle::prioritize_files(std::vector const& files) const + { + INVARIANT_CHECK; + + call_member(m_ses, m_chk, m_info_hash + , bind(&torrent::prioritize_files, _1, boost::cref(files))); + } + +// ============ start deprecation =============== + + void torrent_handle::filter_piece(int index, bool filter) const + { + INVARIANT_CHECK; + call_member(m_ses, m_chk, m_info_hash + , bind(&torrent::filter_piece, _1, index, filter)); + } + + void torrent_handle::filter_pieces(std::vector const& pieces) const + { + INVARIANT_CHECK; + call_member(m_ses, m_chk, m_info_hash + , bind(&torrent::filter_pieces, _1, boost::cref(pieces))); + } + + bool torrent_handle::is_piece_filtered(int index) const + { + INVARIANT_CHECK; + return call_member(m_ses, m_chk, m_info_hash + , bind(&torrent::is_piece_filtered, _1, index)); + } + + std::vector torrent_handle::filtered_pieces() const + { + INVARIANT_CHECK; + std::vector ret; + call_member(m_ses, m_chk, m_info_hash + , bind(&torrent::filtered_pieces, _1, boost::ref(ret))); + return ret; + } + + void torrent_handle::filter_files(std::vector const& files) const + { + INVARIANT_CHECK; + call_member(m_ses, m_chk, m_info_hash + , bind(&torrent::filter_files, _1, files)); + } + +// ============ end deprecation =============== + + + std::vector const& torrent_handle::trackers() const + { + INVARIANT_CHECK; + + return call_member const&>(m_ses + , m_chk, m_info_hash, bind(&torrent::trackers, _1)); + } + + void torrent_handle::add_url_seed(std::string const& url) + { + INVARIANT_CHECK; + + return call_member(m_ses, m_chk, m_info_hash + , bind(&torrent::add_url_seed, _1, url)); + } + + void torrent_handle::replace_trackers( + std::vector const& urls) const + { + INVARIANT_CHECK; + + call_member(m_ses, m_chk, m_info_hash + , bind(&torrent::replace_trackers, _1, urls)); + } + + torrent_info const& torrent_handle::get_torrent_info() const + { + INVARIANT_CHECK; + + if (!has_metadata()) throw_invalid_handle(); + return call_member(m_ses, m_chk, m_info_hash + , bind(&torrent::torrent_file, _1)); + } + + bool torrent_handle::is_valid() const + { + INVARIANT_CHECK; + + if (m_ses == 0) return false; + + if (m_chk) + { + mutex::scoped_lock l(m_chk->m_mutex); + aux::piece_checker_data* d = m_chk->find_torrent(m_info_hash); + if (d != 0) return true; + } + + { + session_impl::mutex_t::scoped_lock l(m_ses->m_mutex); + boost::weak_ptr t = m_ses->find_torrent(m_info_hash); + if (!t.expired()) return true; + } + + return false; + } + + entry torrent_handle::write_resume_data() const + { + INVARIANT_CHECK; + + std::vector piece_index; + if (m_ses == 0) return entry(); + + session_impl::mutex_t::scoped_lock l(m_ses->m_mutex); + boost::shared_ptr t = m_ses->find_torrent(m_info_hash).lock(); + if (!t) return entry(); + + if (!t->valid_metadata()) return entry(); + + t->filesystem().export_piece_map(piece_index); + + entry ret(entry::dictionary_t); + + ret["file-format"] = "libtorrent resume file"; + ret["file-version"] = 1; + + ret["allocation"] = t->filesystem().compact_allocation()?"compact":"full"; + + const sha1_hash& info_hash = t->torrent_file().info_hash(); + ret["info-hash"] = std::string((char*)info_hash.begin(), (char*)info_hash.end()); + + ret["slots"] = entry(entry::list_t); + entry::list_type& slots = ret["slots"].list(); + std::copy(piece_index.begin(), piece_index.end(), std::back_inserter(slots)); + + // blocks per piece + int num_blocks_per_piece = + static_cast(t->torrent_file().piece_length()) / t->block_size(); + ret["blocks per piece"] = num_blocks_per_piece; + + // if this torrent is a seed, we won't have a piece picker + // and there will be no half-finished pieces. + if (!t->is_seed()) + { + const piece_picker& p = t->picker(); + + const std::vector& q + = p.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); + + for (int j = 0; j < num_bitmask_bytes; ++j) + { + unsigned char v = 0; + for (int k = 0; k < 8; ++k) + v |= i->info[j*8+k].finished?(1 << k):0; + bitmask.insert(bitmask.end(), v); + } + piece_struct["bitmask"] = bitmask; + + assert(t->filesystem().slot_for_piece(i->index) >= 0); + unsigned long adler + = t->filesystem().piece_crc( + t->filesystem().slot_for_piece(i->index) + , t->block_size() + , i->info); + + piece_struct["adler32"] = adler; + + // push the struct onto the unfinished-piece list + up.push_back(piece_struct); + } + } + // write local peers + + ret["peers"] = entry::list_type(); + entry::list_type& peer_list = ret["peers"].list(); + + policy& pol = t->get_policy(); + + for (policy::iterator i = pol.begin_peer() + , end(pol.end_peer()); i != end; ++i) + { + // we cannot save remote connection + // since we don't know their listen port + // unless they gave us their listen port + // through the extension handshake + // so, if the peer is not connectable (i.e. we + // don't know its listen port) or if it has + // been banned, don't save it. + if (i->type == policy::peer::not_connectable + || i->banned) continue; + + tcp::endpoint ip = i->ip; + entry peer(entry::dictionary_t); + peer["ip"] = ip.address().to_string(); + peer["port"] = ip.port(); + peer_list.push_back(peer); + } + + t->filesystem().write_resume_data(ret); + + return ret; + } + + + boost::filesystem::path torrent_handle::save_path() const + { + INVARIANT_CHECK; + + return call_member(m_ses, m_chk, m_info_hash + , bind(&torrent::save_path, _1)); + } + + void torrent_handle::connect_peer(tcp::endpoint const& adr, int source) const + { + INVARIANT_CHECK; + + if (m_ses == 0) throw_invalid_handle(); + + session_impl::mutex_t::scoped_lock l(m_ses->m_mutex); + boost::shared_ptr t = m_ses->find_torrent(m_info_hash).lock(); + + if (!t) + { + // the torrent is being checked. Add the peer to its + // peer list. The entries in there will be connected + // once the checking is complete. + mutex::scoped_lock l2(m_chk->m_mutex); + + aux::piece_checker_data* d = m_chk->find_torrent(m_info_hash); + if (d == 0) throw_invalid_handle(); + d->peers.push_back(adr); + return; + } + + peer_id id; + std::fill(id.begin(), id.end(), 0); + t->get_policy().peer_from_tracker(adr, id, source, 0); + } + + void torrent_handle::force_reannounce( + boost::posix_time::time_duration duration) const + { + INVARIANT_CHECK; + + if (m_ses == 0) throw_invalid_handle(); + + session_impl::mutex_t::scoped_lock l(m_ses->m_mutex); + boost::shared_ptr t = m_ses->find_torrent(m_info_hash).lock(); + if (!t) throw_invalid_handle(); + + t->force_tracker_request(time_now() + + seconds(duration.total_seconds())); + } + + void torrent_handle::force_reannounce() const + { + INVARIANT_CHECK; + + if (m_ses == 0) throw_invalid_handle(); + + session_impl::mutex_t::scoped_lock l(m_ses->m_mutex); + boost::shared_ptr t = m_ses->find_torrent(m_info_hash).lock(); + if (!t) throw_invalid_handle(); + + t->force_tracker_request(); + } + + void torrent_handle::set_ratio(float ratio) const + { + INVARIANT_CHECK; + + assert(ratio >= 0.f); + + if (ratio < 1.f && ratio > 0.f) + ratio = 1.f; + + call_member(m_ses, m_chk, m_info_hash + , bind(&torrent::set_ratio, _1, ratio)); + } + +#ifndef TORRENT_DISABLE_RESOLVE_COUNTRIES + void torrent_handle::resolve_countries(bool r) + { + INVARIANT_CHECK; + call_member(m_ses, m_chk, m_info_hash + , bind(&torrent::resolve_countries, _1, r)); + } + + bool torrent_handle::resolve_countries() const + { + INVARIANT_CHECK; + return call_member(m_ses, m_chk, m_info_hash + , bind(&torrent::resolving_countries, _1)); + } +#endif + + void torrent_handle::get_peer_info(std::vector& v) const + { + INVARIANT_CHECK; + + v.clear(); + if (m_ses == 0) throw_invalid_handle(); + + session_impl::mutex_t::scoped_lock l(m_ses->m_mutex); + + boost::shared_ptr t = m_ses->find_torrent(m_info_hash).lock(); + if (!t) return; + + for (torrent::const_peer_iterator i = t->begin(); + i != t->end(); ++i) + { + peer_connection* peer = i->second; + + // peers that haven't finished the handshake should + // not be included in this list + if (peer->associated_torrent().expired()) continue; + + v.push_back(peer_info()); + peer_info& p = v.back(); + + peer->get_peer_info(p); +#ifndef TORRENT_DISABLE_RESOLVE_COUNTRIES + if (t->resolving_countries()) + t->resolve_peer_country(intrusive_ptr(peer)); +#endif + } + } + + void torrent_handle::get_download_queue(std::vector& queue) const + { + INVARIANT_CHECK; + + if (m_ses == 0) throw_invalid_handle(); + + session_impl::mutex_t::scoped_lock l(m_ses->m_mutex); + boost::shared_ptr t = m_ses->find_torrent(m_info_hash).lock(); + + queue.clear(); + if (!t) return; + if (!t->valid_metadata()) return; + // if we're a seed, the piece picker has been removed + if (t->is_seed()) return; + + const piece_picker& p = t->picker(); + + const std::vector& q + = p.get_download_queue(); + + for (std::vector::const_iterator i + = q.begin(); i != q.end(); ++i) + { + partial_piece_info pi; + pi.piece_state = (partial_piece_info::state_t)i->state; + pi.blocks_in_piece = p.blocks_in_piece(i->index); + for (int j = 0; j < pi.blocks_in_piece; ++j) + { + pi.peer[j] = i->info[j].peer; + pi.num_downloads[j] = i->info[j].num_downloads; + pi.finished_blocks[j] = i->info[j].finished; + pi.requested_blocks[j] = i->info[j].requested; + } + pi.piece_index = i->index; + queue.push_back(pi); + } + } + +} + diff --git a/encryption/libtorrent/src/torrent_info.cpp b/encryption/libtorrent/src/torrent_info.cpp new file mode 100755 index 000000000..e546a1243 --- /dev/null +++ b/encryption/libtorrent/src/torrent_info.cpp @@ -0,0 +1,856 @@ +/* + +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 "libtorrent/pch.hpp" + +#include +#include +#include +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/torrent_info.hpp" +#include "libtorrent/bencode.hpp" +#include "libtorrent/hasher.hpp" +#include "libtorrent/entry.hpp" + +namespace pt = boost::posix_time; +namespace gr = boost::gregorian; + +using namespace libtorrent; +using namespace boost::filesystem; + +namespace +{ + void convert_to_utf8(std::string& str, unsigned char chr) + { + str += 0xc0 | ((chr & 0xff) >> 6); + str += 0x80 | (chr & 0x3f); + } + + void verify_encoding(file_entry& target) + { + std::string tmp_path; + std::string file_path = target.path.string(); + bool valid_encoding = true; + for (std::string::iterator i = file_path.begin() + , end(file_path.end()); i != end; ++i) + { + // valid ascii-character + if ((*i & 0x80) == 0) + { + tmp_path += *i; + continue; + } + + if (std::distance(i, end) < 2) + { + convert_to_utf8(tmp_path, *i); + valid_encoding = false; + continue; + } + + // valid 2-byte utf-8 character + if ((i[0] & 0xe0) == 0xc0 + && (i[1] & 0xc0) == 0x80) + { + tmp_path += i[0]; + tmp_path += i[1]; + i += 1; + continue; + } + + if (std::distance(i, end) < 3) + { + convert_to_utf8(tmp_path, *i); + valid_encoding = false; + continue; + } + + // valid 3-byte utf-8 character + if ((i[0] & 0xf0) == 0xe0 + && (i[1] & 0xc0) == 0x80 + && (i[2] & 0xc0) == 0x80) + { + tmp_path += i[0]; + tmp_path += i[1]; + tmp_path += i[2]; + i += 2; + continue; + } + + if (std::distance(i, end) < 4) + { + convert_to_utf8(tmp_path, *i); + valid_encoding = false; + continue; + } + + // valid 4-byte utf-8 character + if ((i[0] & 0xf0) == 0xe0 + && (i[1] & 0xc0) == 0x80 + && (i[2] & 0xc0) == 0x80 + && (i[3] & 0xc0) == 0x80) + { + tmp_path += i[0]; + tmp_path += i[1]; + tmp_path += i[2]; + tmp_path += i[3]; + i += 3; + continue; + } + + convert_to_utf8(tmp_path, *i); + valid_encoding = false; + } + // the encoding was not valid utf-8 + // save the original encoding and replace the + // commonly used path with the correctly + // encoded string + if (!valid_encoding) + { + target.orig_path.reset(new path(target.path)); + target.path = tmp_path; + } + } + + void extract_single_file(const entry& dict, file_entry& target + , std::string const& root_dir) + { + target.size = dict["length"].integer(); + target.path = root_dir; + + + // prefer the name.utf-8 + // because if it exists, it is more + // likely to be correctly encoded + + const entry::list_type* list = 0; + if (entry const* p = dict.find_key("path.utf-8")) + { + list = &p->list(); + } + else + { + list = &dict["path"].list(); + } + + for (entry::list_type::const_iterator i = list->begin(); + i != list->end(); ++i) + { + if (i->string() != "..") + target.path /= i->string(); + } + verify_encoding(target); + if (target.path.is_complete()) throw std::runtime_error("torrent contains " + "a file with an absolute path: '" + + target.path.native_file_string() + "'"); + } + + void extract_files(const entry::list_type& list, std::vector& target + , std::string const& root_dir) + { + size_type offset = 0; + for (entry::list_type::const_iterator i = list.begin(); i != list.end(); ++i) + { + target.push_back(file_entry()); + extract_single_file(*i, target.back(), root_dir); + target.back().offset = offset; + offset += target.back().size; + } + } + + void remove_dir(path& p) + { + assert(p.begin() != p.end()); + path tmp; + for (path::iterator i = boost::next(p.begin()); i != p.end(); ++i) + tmp /= *i; + p = tmp; + } +} + +namespace libtorrent +{ + + // standard constructor that parses a torrent file + torrent_info::torrent_info(const entry& torrent_file) + : m_num_pieces(0) + , m_creation_date(pt::ptime(pt::not_a_date_time)) + , m_multifile(false) + , m_private(false) + , m_extra_info(entry::dictionary_t) +#ifndef NDEBUG + , m_half_metadata(false) +#endif + { + try + { + read_torrent_info(torrent_file); + } + catch(type_error&) + { + throw invalid_torrent_file(); + } + } + + // constructor used for creating new torrents + // will not contain any hashes, comments, creation date + // just the necessary to use it with piece manager + // used for torrents with no metadata + torrent_info::torrent_info(sha1_hash const& info_hash) + : m_piece_length(0) + , m_total_size(0) + , m_num_pieces(0) + , m_info_hash(info_hash) + , m_name() + , m_creation_date(pt::second_clock::universal_time()) + , m_multifile(false) + , m_private(false) + , m_extra_info(entry::dictionary_t) +#ifndef NDEBUG + , m_half_metadata(false) +#endif + { + } + + torrent_info::torrent_info() + : m_piece_length(0) + , m_total_size(0) + , m_num_pieces(0) + , m_info_hash(0) + , m_name() + , m_creation_date(pt::second_clock::universal_time()) + , m_multifile(false) + , m_private(false) + , m_extra_info(entry::dictionary_t) +#ifndef NDEBUG + , m_half_metadata(false) +#endif + { + } + + torrent_info::~torrent_info() + {} + + void torrent_info::set_piece_size(int size) + { + // make sure the size is an even power of 2 +#ifndef NDEBUG + for (int i = 0; i < 32; ++i) + { + if (size & (1 << i)) + { + assert((size & ~(1 << i)) == 0); + break; + } + } +#endif + assert(!m_half_metadata); + m_piece_length = size; + + m_num_pieces = static_cast( + (m_total_size + m_piece_length - 1) / m_piece_length); + int old_num_pieces = static_cast(m_piece_hash.size()); + + m_piece_hash.resize(m_num_pieces); + for (int i = old_num_pieces; i < m_num_pieces; ++i) + { + m_piece_hash[i].clear(); + } + } + + void torrent_info::parse_info_section(entry const& info) + { + // encode the info-field in order to calculate it's sha1-hash + std::vector buf; + bencode(std::back_inserter(buf), info); + hasher h; + h.update(&buf[0], (int)buf.size()); + m_info_hash = h.final(); + + // extract piece length + m_piece_length = (int)info["piece length"].integer(); + if (m_piece_length <= 0) throw std::runtime_error("invalid torrent. piece length <= 0"); + + // extract file name (or the directory name if it's a multifile libtorrent) + if (entry const* e = info.find_key("name.utf-8")) + { m_name = e->string(); } + else + { m_name = info["name"].string(); } + + path tmp = m_name; + if (tmp.is_complete()) throw std::runtime_error("torrent contains " + "a file with an absolute path: '" + m_name + "'"); + if (tmp.has_branch_path()) throw std::runtime_error( + "torrent contains name with directories: '" + m_name + "'"); + + // extract file list + entry const* i = info.find_key("files"); + if (i == 0) + { + // if there's no list of files, there has to be a length + // field. + file_entry e; + e.path = m_name; + e.offset = 0; + e.size = info["length"].integer(); + m_files.push_back(e); + } + else + { + extract_files(i->list(), m_files, m_name); + m_multifile = true; + } + + // calculate total size of all pieces + m_total_size = 0; + for (std::vector::iterator i = m_files.begin(); i != m_files.end(); ++i) + m_total_size += i->size; + + // extract sha-1 hashes for all pieces + // we want this division to round upwards, that's why we have the + // extra addition + + m_num_pieces = static_cast((m_total_size + m_piece_length - 1) / m_piece_length); + m_piece_hash.resize(m_num_pieces); + const std::string& hash_string = info["pieces"].string(); + + if ((int)hash_string.length() != m_num_pieces * 20) + throw invalid_torrent_file(); + + for (int i = 0; i < m_num_pieces; ++i) + std::copy( + hash_string.begin() + i*20 + , hash_string.begin() + (i+1)*20 + , m_piece_hash[i].begin()); + + for (entry::dictionary_type::const_iterator i = info.dict().begin() + , end(info.dict().end()); i != end; ++i) + { + if (i->first == "pieces" + || i->first == "piece length" + || i->first == "length") + continue; + m_extra_info[i->first] = i->second; + } + + if (entry const* priv = info.find_key("private")) + { + if (priv->type() != entry::int_t + || priv->integer() != 0) + { + // this key exists and it's not 0. + // consider the torrent private + m_private = true; + } + } + +#ifndef NDEBUG + std::vector info_section_buf; + entry gen_info_section = create_info_metadata(); + bencode(std::back_inserter(info_section_buf), gen_info_section); + assert(hasher(&info_section_buf[0], info_section_buf.size()).final() + == m_info_hash); +#endif + } + + // extracts information from a libtorrent file and fills in the structures in + // the torrent object + void torrent_info::read_torrent_info(const entry& torrent_file) + { + // extract the url of the tracker + if (entry const* i = torrent_file.find_key("announce-list")) + { + const entry::list_type& l = i->list(); + for (entry::list_type::const_iterator j = l.begin(); j != l.end(); ++j) + { + const entry::list_type& ll = j->list(); + for (entry::list_type::const_iterator k = ll.begin(); k != ll.end(); ++k) + { + announce_entry e(k->string()); + e.tier = (int)std::distance(l.begin(), j); + m_urls.push_back(e); + } + } + + if (m_urls.size() == 0) + { + // the announce-list is empty + // fall back to look for announce + m_urls.push_back(announce_entry( + torrent_file["announce"].string())); + } + // shuffle each tier + std::vector::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); + start = stop; + current_tier = stop->tier; + } + } + std::random_shuffle(start, stop); + } + else if (entry const* i = torrent_file.find_key("announce")) + { + m_urls.push_back(announce_entry(i->string())); + } + + if (entry const* i = torrent_file.find_key("nodes")) + { + entry::list_type const& list = i->list(); + for (entry::list_type::const_iterator i(list.begin()) + , end(list.end()); i != end; ++i) + { + if (i->type() != entry::list_t) continue; + entry::list_type const& l = i->list(); + entry::list_type::const_iterator iter = l.begin(); + if (l.size() < 1) continue; + std::string const& hostname = iter->string(); + ++iter; + int port = 6881; + if (l.end() != iter) port = iter->integer(); + m_nodes.push_back(std::make_pair(hostname, port)); + } + } + + // extract creation date + try + { + m_creation_date = pt::ptime(gr::date(1970, gr::Jan, 1)) + + pt::seconds(long(torrent_file["creation date"].integer())); + } + catch (type_error) {} + + // if there are any url-seeds, extract them + try + { + entry const& url_seeds = torrent_file["url-list"]; + if (url_seeds.type() == entry::string_t) + { + m_url_seeds.push_back(url_seeds.string()); + } + else if (url_seeds.type() == entry::list_t) + { + entry::list_type const& l = url_seeds.list(); + for (entry::list_type::const_iterator i = l.begin(); + i != l.end(); ++i) + { + m_url_seeds.push_back(i->string()); + } + } + } + catch (type_error&) {} + + // extract comment + if (entry const* e = torrent_file.find_key("comment.utf-8")) + { m_comment = e->string(); } + else if (entry const* e = torrent_file.find_key("comment")) + { m_comment = e->string(); } + + if (entry const* e = torrent_file.find_key("created by.utf-8")) + { m_created_by = e->string(); } + else if (entry const* e = torrent_file.find_key("created by")) + { m_created_by = e->string(); } + + parse_info_section(torrent_file["info"]); + } + + boost::optional + torrent_info::creation_date() const + { + if (m_creation_date != pt::ptime(gr::date(pt::not_a_date_time))) + { + 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; + m_urls.push_back(e); + + using boost::bind; + std::sort(m_urls.begin(), m_urls.end(), boost::bind(std::less() + , bind(&announce_entry::tier, _1), bind(&announce_entry::tier, _2))); + } + + void torrent_info::add_file(boost::filesystem::path file, size_type size) + { + assert(file.begin() != file.end()); + + if (!file.has_branch_path()) + { + // you have already added at least one file with a + // path to the file (branch_path), which means that + // all the other files need to be in the same top + // directory as the first file. + assert(m_files.empty()); + assert(!m_multifile); + m_name = file.string(); + } + else + { +#ifndef NDEBUG + if (!m_files.empty()) + assert(m_name == *file.begin()); +#endif + m_multifile = true; + m_name = *file.begin(); + } + + file_entry e; + e.path = file; + e.size = size; + e.offset = m_files.empty() ? 0 : m_files.back().offset + + m_files.back().size; + m_files.push_back(e); + + m_total_size += size; + + if (m_piece_length == 0) + m_piece_length = 256 * 1024; + + m_num_pieces = static_cast( + (m_total_size + m_piece_length - 1) / m_piece_length); + int old_num_pieces = static_cast(m_piece_hash.size()); + + m_piece_hash.resize(m_num_pieces); + if (m_num_pieces > old_num_pieces) + std::for_each(m_piece_hash.begin() + old_num_pieces + , m_piece_hash.end(), boost::bind(&sha1_hash::clear, _1)); + } + + void torrent_info::add_url_seed(std::string const& url) + { + m_url_seeds.push_back(url); + } + + void torrent_info::set_comment(char const* str) + { + m_comment = str; + } + + void torrent_info::set_creator(char const* str) + { + m_created_by = str; + } + + entry torrent_info::create_info_metadata() const + { + namespace fs = boost::filesystem; + + // you have to add files to the torrent first + assert(!m_files.empty()); + + entry info(m_extra_info); + + if (!info.find_key("name")) + info["name"] = m_name; + + if (!m_multifile) + { + info["length"] = m_files.front().size; + } + else + { + if (!info.find_key("files")) + { + entry& files = info["files"]; + + for (std::vector::const_iterator i = m_files.begin(); + i != m_files.end(); ++i) + { + files.list().push_back(entry()); + entry& file_e = files.list().back(); + file_e["length"] = i->size; + entry& path_e = file_e["path"]; + + fs::path const* file_path; + if (i->orig_path) file_path = &(*i->orig_path); + else file_path = &i->path; + assert(file_path->has_branch_path()); + assert(*file_path->begin() == m_name); + + for (fs::path::iterator j = boost::next(file_path->begin()); + j != file_path->end(); ++j) + { + path_e.list().push_back(entry(*j)); + } + } + } + } + + info["piece length"] = piece_length(); + entry& pieces = info["pieces"]; + + std::string& p = pieces.string(); + + for (std::vector::const_iterator i = m_piece_hash.begin(); + i != m_piece_hash.end(); ++i) + { + p.append((char*)i->begin(), (char*)i->end()); + } + + return info; + } + + entry torrent_info::create_torrent() const + { + assert(m_piece_length > 0); + + namespace fs = boost::filesystem; + + if ((m_urls.empty() && m_nodes.empty()) || m_files.empty()) + { + // TODO: throw something here + // throw + return entry(); + } + + entry dict; + + if (m_private) dict["private"] = 1; + + if (!m_urls.empty()) + dict["announce"] = m_urls.front().url; + + 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().tier; + for (std::vector::const_iterator i = m_urls.begin(); + i != m_urls.end(); ++i) + { + if (i->tier != current_tier) + { + current_tier = i->tier; + trackers.list().push_back(tier); + tier.list().clear(); + } + tier.list().push_back(entry(i->url)); + } + trackers.list().push_back(tier); + dict["announce-list"] = trackers; + } + + if (!m_comment.empty()) + dict["comment"] = m_comment; + + dict["creation date"] = + (m_creation_date - pt::ptime(gr::date(1970, gr::Jan, 1))).total_seconds(); + + if (!m_created_by.empty()) + dict["created by"] = m_created_by; + + if (!m_url_seeds.empty()) + { + if (m_url_seeds.size() == 1) + { + dict["url-list"] = m_url_seeds.front(); + } + else + { + entry& list = dict["url-list"]; + for (std::vector::const_iterator i + = m_url_seeds.begin(); i != m_url_seeds.end(); ++i) + { + list.list().push_back(entry(*i)); + } + } + } + + dict["info"] = create_info_metadata(); + + entry const& info_section = dict["info"]; + std::vector buf; + bencode(std::back_inserter(buf), info_section); + m_info_hash = hasher(&buf[0], buf.size()).final(); + + return dict; + } + + void torrent_info::set_hash(int index, const sha1_hash& h) + { + assert(index >= 0); + assert(index < (int)m_piece_hash.size()); + m_piece_hash[index] = h; + } + + void torrent_info::convert_file_names() + { + assert(false); + } + + void torrent_info::seed_free() + { + std::vector().swap(m_url_seeds); + nodes_t().swap(m_nodes); + std::vector().swap(m_piece_hash); +#ifndef NDEBUG + m_half_metadata = true; +#endif + } + +// ------- start deprecation ------- + + void torrent_info::print(std::ostream& os) const + { + 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"; +// if (m_creation_date != pt::ptime(gr::date(pt::not_a_date_time))) +// os << "creation date: " << to_simple_string(m_creation_date) << "\n"; + os << "private: " << (m_private?"yes":"no") << "\n"; + os << "number of pieces: " << num_pieces() << "\n"; + os << "piece length: " << piece_length() << "\n"; + os << "files:\n"; + for (file_iterator i = begin_files(); i != end_files(); ++i) + os << " " << std::setw(11) << i->size << " " << i->path.string() << "\n"; + } + +// ------- end deprecation ------- + + size_type torrent_info::piece_size(int index) const + { + assert(index >= 0 && index < num_pieces()); + if (index == num_pieces()-1) + { + size_type size = total_size() + - (num_pieces() - 1) * piece_length(); + assert(size > 0); + assert(size <= piece_length()); + return size; + } + else + return piece_length(); + } + + void torrent_info::add_node(std::pair const& node) + { + m_nodes.push_back(node); + } + + std::vector torrent_info::map_block(int piece, size_type offset + , int size) const + { + assert(num_files() > 0); + std::vector ret; + + size_type start = piece * (size_type)m_piece_length + offset; + assert(start + size <= m_total_size); + + // find the file iterator and file offset + // TODO: make a vector that can map piece -> file index in O(1) + size_type file_offset = start; + std::vector::const_iterator file_iter; + + int counter = 0; + for (file_iter = begin_files();; ++counter, ++file_iter) + { + assert(file_iter != end_files()); + if (file_offset < file_iter->size) + { + file_slice f; + f.file_index = counter; + f.offset = file_offset; + f.size = (std::min)(file_iter->size - file_offset, (size_type)size); + size -= f.size; + file_offset += f.size; + ret.push_back(f); + } + + assert(size >= 0); + if (size <= 0) break; + + file_offset -= file_iter->size; + } + return ret; + } + + peer_request torrent_info::map_file(int file_index, size_type file_offset + , int size) const + { + assert(file_index < (int)m_files.size()); + assert(file_index >= 0); + size_type offset = file_offset + m_files[file_index].offset; + + peer_request ret; + ret.piece = offset / piece_length(); + ret.start = offset - ret.piece * piece_length(); + ret.length = size; + return ret; + } + +} diff --git a/encryption/libtorrent/src/tracker_manager.cpp b/encryption/libtorrent/src/tracker_manager.cpp new file mode 100755 index 000000000..7ee8c57ba --- /dev/null +++ b/encryption/libtorrent/src/tracker_manager.cpp @@ -0,0 +1,599 @@ +/* + +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 "libtorrent/pch.hpp" + +#include +#include +#include +#include +#include + +#include "zlib.h" + +#include + +#include "libtorrent/tracker_manager.hpp" +#include "libtorrent/http_tracker_connection.hpp" +#include "libtorrent/udp_tracker_connection.hpp" +#include "libtorrent/entry.hpp" +#include "libtorrent/bencode.hpp" +#include "libtorrent/torrent.hpp" +#include "libtorrent/peer_connection.hpp" + +using namespace libtorrent; +using boost::tuples::make_tuple; +using boost::tuples::tuple; +using boost::bind; + +namespace +{ + enum + { + minimum_tracker_response_length = 3, + http_buffer_size = 2048 + }; + + + enum + { + FTEXT = 0x01, + FHCRC = 0x02, + FEXTRA = 0x04, + FNAME = 0x08, + FCOMMENT = 0x10, + FRESERVED = 0xe0, + + GZIP_MAGIC0 = 0x1f, + GZIP_MAGIC1 = 0x8b + }; + +} + +namespace libtorrent +{ + // returns -1 if gzip header is invalid or the header size in bytes + int gzip_header(const char* buf, int size) + { + assert(buf != 0); + assert(size > 0); + + const unsigned char* buffer = reinterpret_cast(buf); + const int total_size = size; + + // The zip header cannot be shorter than 10 bytes + if (size < 10) return -1; + + // check the magic header of gzip + if ((buffer[0] != GZIP_MAGIC0) || (buffer[1] != GZIP_MAGIC1)) return -1; + + int method = buffer[2]; + int flags = buffer[3]; + + // check for reserved flag and make sure it's compressed with the correct metod + if (method != Z_DEFLATED || (flags & FRESERVED) != 0) return -1; + + // skip time, xflags, OS code + size -= 10; + buffer += 10; + + if (flags & FEXTRA) + { + int extra_len; + + if (size < 2) return -1; + + extra_len = (buffer[1] << 8) | buffer[0]; + + if (size < (extra_len+2)) return -1; + size -= (extra_len + 2); + buffer += (extra_len + 2); + } + + if (flags & FNAME) + { + while (size && *buffer) + { + --size; + ++buffer; + } + if (!size || *buffer) return -1; + + --size; + ++buffer; + } + + if (flags & FCOMMENT) + { + while (size && *buffer) + { + --size; + ++buffer; + } + if (!size || *buffer) return -1; + + --size; + ++buffer; + } + + if (flags & FHCRC) + { + if (size < 2) return -1; + + size -= 2; + buffer += 2; + } + + return total_size - size; + } + + bool inflate_gzip( + std::vector& buffer + , tracker_request const& req + , request_callback* requester + , int maximum_tracker_response_length) + { + assert(maximum_tracker_response_length > 0); + + int header_len = gzip_header(&buffer[0], (int)buffer.size()); + if (header_len < 0) + { + requester->tracker_request_error(req, 200, "invalid gzip header in tracker response"); + return true; + } + + // start off wth one kilobyte and grow + // if needed + std::vector inflate_buffer(1024); + + // initialize the zlib-stream + z_stream str; + + // subtract 8 from the end of the buffer since that's CRC32 and input size + // and those belong to the gzip file + str.avail_in = (int)buffer.size() - header_len - 8; + str.next_in = reinterpret_cast(&buffer[header_len]); + str.next_out = reinterpret_cast(&inflate_buffer[0]); + str.avail_out = (int)inflate_buffer.size(); + str.zalloc = Z_NULL; + str.zfree = Z_NULL; + str.opaque = 0; + // -15 is really important. It will make inflate() not look for a zlib header + // and just deflate the buffer + if (inflateInit2(&str, -15) != Z_OK) + { + requester->tracker_request_error(req, 200, "gzip out of memory"); + return true; + } + + // inflate and grow inflate_buffer as needed + int ret = inflate(&str, Z_SYNC_FLUSH); + while (ret == Z_OK) + { + if (str.avail_out == 0) + { + if (inflate_buffer.size() >= (unsigned)maximum_tracker_response_length) + { + inflateEnd(&str); + requester->tracker_request_error(req, 200 + , "tracker response too large"); + return true; + } + int new_size = (int)inflate_buffer.size() * 2; + if (new_size > maximum_tracker_response_length) new_size = maximum_tracker_response_length; + int old_size = (int)inflate_buffer.size(); + + inflate_buffer.resize(new_size); + str.next_out = reinterpret_cast(&inflate_buffer[old_size]); + str.avail_out = new_size - old_size; + } + + ret = inflate(&str, Z_SYNC_FLUSH); + } + + inflate_buffer.resize(inflate_buffer.size() - str.avail_out); + inflateEnd(&str); + + if (ret != Z_STREAM_END) + { + requester->tracker_request_error(req, 200, "gzip error"); + return true; + } + + // commit the resulting buffer + std::swap(buffer, inflate_buffer); + return false; + } + + std::string base64encode(const std::string& s) + { + static const char base64_table[] = + { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', + 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', + 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', + 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', + 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', + 'w', 'x', 'y', 'z', '0', '1', '2', '3', + '4', '5', '6', '7', '8', '9', '+', '/' + }; + + unsigned char inbuf[3]; + unsigned char outbuf[4]; + + std::string ret; + for (std::string::const_iterator i = s.begin(); i != s.end();) + { + // available input is 1,2 or 3 bytes + // since we read 3 bytes at a time at most + int available_input = std::min(3, (int)std::distance(i, s.end())); + + // clear input buffer + std::fill(inbuf, inbuf+3, 0); + + // read a chunk of input into inbuf + for (int j = 0; j < available_input; ++j) + { + inbuf[j] = *i; + ++i; + } + + // encode inbuf to outbuf + outbuf[0] = (inbuf[0] & 0xfc) >> 2; + outbuf[1] = ((inbuf[0] & 0x03) << 4) | ((inbuf [1] & 0xf0) >> 4); + outbuf[2] = ((inbuf[1] & 0x0f) << 2) | ((inbuf [2] & 0xc0) >> 6); + outbuf[3] = inbuf[2] & 0x3f; + + // write output + for (int j = 0; j < available_input+1; ++j) + { + ret += base64_table[outbuf[j]]; + } + + // write pad + for (int j = 0; j < 3 - available_input; ++j) + { + ret += '='; + } + } + return ret; + } + + void intrusive_ptr_add_ref(timeout_handler const* c) + { + assert(c != 0); + assert(c->m_refs >= 0); + timeout_handler::mutex_t::scoped_lock l(c->m_mutex); + ++c->m_refs; + } + + void intrusive_ptr_release(timeout_handler const* c) + { + assert(c != 0); + assert(c->m_refs > 0); + timeout_handler::mutex_t::scoped_lock l(c->m_mutex); + --c->m_refs; + if (c->m_refs == 0) + { + l.unlock(); + delete c; + } + } + + + timeout_handler::timeout_handler(asio::strand& str) + : m_strand(str) + , m_start_time(time_now()) + , m_read_time(time_now()) + , m_timeout(str.io_service()) + , m_completion_timeout(0) + , m_read_timeout(0) + , m_refs(0) + {} + + void timeout_handler::set_timeout(int completion_timeout, int read_timeout) + { + m_completion_timeout = completion_timeout; + m_read_timeout = read_timeout; + m_start_time = time_now(); + m_read_time = time_now(); + + m_timeout.expires_at(std::min( + m_read_time + seconds(m_read_timeout) + , m_start_time + seconds(m_completion_timeout))); + m_timeout.async_wait(m_strand.wrap(bind( + &timeout_handler::timeout_callback, self(), _1))); + } + + void timeout_handler::restart_read_timeout() + { + m_read_time = time_now(); + } + + void timeout_handler::cancel() + { + m_completion_timeout = 0; + m_timeout.cancel(); + } + + void timeout_handler::timeout_callback(asio::error_code const& error) try + { + if (error) return; + if (m_completion_timeout == 0) return; + + ptime now(time_now()); + time_duration receive_timeout = now - m_read_time; + time_duration completion_timeout = now - m_start_time; + + if (m_read_timeout + < total_seconds(receive_timeout) + || m_completion_timeout + < total_seconds(completion_timeout)) + { + on_timeout(); + return; + } + + m_timeout.expires_at(std::min( + m_read_time + seconds(m_read_timeout) + , m_start_time + seconds(m_completion_timeout))); + m_timeout.async_wait(m_strand.wrap( + bind(&timeout_handler::timeout_callback, self(), _1))); + } + catch (std::exception& e) + { + assert(false); + } + + tracker_connection::tracker_connection( + tracker_manager& man + , tracker_request req + , asio::strand& str + , address bind_interface_ + , boost::weak_ptr r) + : timeout_handler(str) + , m_requester(r) + , m_bind_interface(bind_interface_) + , m_man(man) + , m_req(req) + {} + + request_callback& tracker_connection::requester() + { + boost::shared_ptr r = m_requester.lock(); + assert(r); + return *r; + } + + void tracker_connection::fail(int code, char const* msg) + { + if (has_requester()) requester().tracker_request_error( + m_req, code, msg); + close(); + } + + void tracker_connection::fail_timeout() + { + if (has_requester()) requester().tracker_request_timed_out(m_req); + close(); + } + + void tracker_connection::close() + { + cancel(); + m_man.remove_request(this); + } + + void tracker_manager::remove_request(tracker_connection const* c) + { + mutex_t::scoped_lock l(m_mutex); + + tracker_connections_t::iterator i = std::find(m_connections.begin() + , m_connections.end(), boost::intrusive_ptr(c)); + if (i == m_connections.end()) return; + + m_connections.erase(i); + } + + // returns protocol, auth, hostname, port, path + tuple + parse_url_components(std::string url) + { + std::string hostname; // hostname only + std::string auth; // user:pass + std::string protocol; // should be http + int port = 80; + + // PARSE URL + std::string::iterator start = url.begin(); + // remove white spaces in front of the url + while (start != url.end() && (*start == ' ' || *start == '\t')) + ++start; + std::string::iterator end + = std::find(url.begin(), url.end(), ':'); + protocol.assign(start, end); + + if (end == url.end()) throw std::runtime_error("invalid url"); + ++end; + if (end == url.end()) throw std::runtime_error("invalid url"); + if (*end != '/') throw std::runtime_error("invalid url"); + ++end; + if (end == url.end()) throw std::runtime_error("invalid url"); + if (*end != '/') throw std::runtime_error("invalid url"); + ++end; + start = end; + + std::string::iterator at = std::find(start, url.end(), '@'); + std::string::iterator 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; + } + + std::string::iterator port_pos + = std::find(start, url.end(), ':'); + + if (port_pos < end) + { + hostname.assign(start, port_pos); + ++port_pos; + try + { + port = boost::lexical_cast(std::string(port_pos, end)); + } + catch(boost::bad_lexical_cast&) + { + throw std::runtime_error("invalid url: \"" + url + + "\", port number expected"); + } + } + else + { + hostname.assign(start, end); + } + + start = end; + return make_tuple(protocol, auth, hostname, port + , std::string(start, url.end())); + } + + void tracker_manager::queue_request( + asio::strand& str + , connection_queue& cc + , tracker_request req + , std::string const& auth + , address bind_infc + , boost::weak_ptr c) + { + mutex_t::scoped_lock l(m_mutex); + assert(req.num_want >= 0); + if (req.event == tracker_request::stopped) + req.num_want = 0; + + assert(!m_abort || req.event == tracker_request::stopped); + if (m_abort && req.event != tracker_request::stopped) + return; + + try + { + std::string protocol; + std::string hostname; + int port; + std::string request_string; + + using boost::tuples::ignore; + // TODO: should auth be used here? + boost::tie(protocol, ignore, hostname, port, request_string) + = parse_url_components(req.url); + + boost::intrusive_ptr con; + + if (protocol == "http") + { + con = new http_tracker_connection( + str + , cc + , *this + , req + , hostname + , port + , request_string + , bind_infc + , c + , m_settings + , m_proxy + , auth); + } + else if (protocol == "udp") + { + con = new udp_tracker_connection( + str + , *this + , req + , hostname + , port + , bind_infc + , c + , m_settings); + } + else + { + throw std::runtime_error("unkown protocol in tracker url"); + } + + m_connections.push_back(con); + + if (con->has_requester()) con->requester().m_manager = this; + } + catch (std::exception& e) + { + if (boost::shared_ptr r = c.lock()) + r->tracker_request_error(req, -1, e.what()); + } + } + + void tracker_manager::abort_all_requests() + { + // removes all connections from m_connections + // except those with a requester == 0 (since those are + // 'event=stopped'-requests) + mutex_t::scoped_lock l(m_mutex); + + m_abort = true; + tracker_connections_t keep_connections; + + for (tracker_connections_t::const_iterator i = + m_connections.begin(); i != m_connections.end(); ++i) + { + tracker_request const& req = (*i)->tracker_req(); + if (req.event == tracker_request::stopped) + keep_connections.push_back(*i); + } + + std::swap(m_connections, keep_connections); + } + + bool tracker_manager::empty() const + { + mutex_t::scoped_lock l(m_mutex); + return m_connections.empty(); + } + +} diff --git a/encryption/libtorrent/src/udp_tracker_connection.cpp b/encryption/libtorrent/src/udp_tracker_connection.cpp new file mode 100755 index 000000000..d992ca050 --- /dev/null +++ b/encryption/libtorrent/src/udp_tracker_connection.cpp @@ -0,0 +1,552 @@ +/* + +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 "libtorrent/pch.hpp" + +#include +#include +#include +#include +#include + +#include "zlib.h" + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/tracker_manager.hpp" +#include "libtorrent/udp_tracker_connection.hpp" +#include "libtorrent/io.hpp" + +namespace +{ + enum + { + udp_connection_retries = 4, + udp_announce_retries = 15, + udp_connect_timeout = 15, + udp_announce_timeout = 10, + udp_buffer_size = 2048 + }; +} + +using boost::bind; +using boost::lexical_cast; + +namespace libtorrent +{ + + udp_tracker_connection::udp_tracker_connection( + asio::strand& str + , tracker_manager& man + , tracker_request const& req + , std::string const& hostname + , unsigned short port + , address bind_infc + , boost::weak_ptr c + , session_settings const& stn) + : tracker_connection(man, req, str, bind_infc, c) + , m_man(man) + , m_strand(str) + , m_name_lookup(m_strand.io_service()) + , m_transaction_id(0) + , m_connection_id(0) + , m_settings(stn) + , m_attempts(0) + { + udp::resolver::query q(hostname, boost::lexical_cast(port)); + m_name_lookup.async_resolve(q + , m_strand.wrap(boost::bind( + &udp_tracker_connection::name_lookup, self(), _1, _2))); + set_timeout(m_settings.tracker_completion_timeout + , m_settings.tracker_receive_timeout); + } + + void udp_tracker_connection::name_lookup(asio::error_code const& error + , udp::resolver::iterator i) try + { + if (error == asio::error::operation_aborted) return; + if (!m_socket) return; // the operation was aborted + if (error || i == udp::resolver::iterator()) + { + fail(-1, error.message().c_str()); + return; + } + +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + if (has_requester()) requester().debug_log("udp tracker name lookup successful"); +#endif + 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. + udp::resolver::iterator target = i; + udp::resolver::iterator end; + udp::endpoint target_address = *i; + for (; target != end && target->endpoint().address().is_v4() + != bind_interface().is_v4(); ++target); + if (target == end) + { + assert(target_address.address().is_v4() != bind_interface().is_v4()); + if (has_requester()) + { + std::string tracker_address_type = target_address.address().is_v4() ? "IPv4" : "IPv6"; + std::string bind_address_type = bind_interface().is_v4() ? "IPv4" : "IPv6"; + requester().tracker_warning("the tracker only resolves to an " + + tracker_address_type + " address, and you're listening on an " + + bind_address_type + " socket. This may prevent you from receiving incoming connections."); + } + } + else + { + target_address = *target; + } + + if (has_requester()) requester().m_tracker_address = tcp::endpoint(target_address.address(), target_address.port()); + m_target = target_address; + m_socket.reset(new datagram_socket(m_name_lookup.io_service())); + m_socket->open(target_address.protocol()); + m_socket->bind(udp::endpoint(bind_interface(), 0)); + m_socket->connect(target_address); + send_udp_connect(); + } + catch (std::exception& e) + { + fail(-1, e.what()); + }; + + void udp_tracker_connection::on_timeout() + { + m_socket.reset(); + m_name_lookup.cancel(); + fail_timeout(); + } + + void udp_tracker_connection::send_udp_connect() + { +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + if (has_requester()) + { + requester().debug_log("==> UDP_TRACKER_CONNECT [" + + lexical_cast(tracker_req().info_hash) + "]"); + } +#endif + if (!m_socket) return; // the operation was aborted + + char send_buf[16]; + char* ptr = send_buf; + + if (m_transaction_id == 0) + m_transaction_id = rand() ^ (rand() << 16); + + // connection_id + detail::write_uint32(0x417, ptr); + detail::write_uint32(0x27101980, ptr); + // action (connect) + detail::write_int32(action_connect, ptr); + // transaction_id + detail::write_int32(m_transaction_id, ptr); + + m_socket->send(asio::buffer((void*)send_buf, 16), 0); + ++m_attempts; + m_buffer.resize(udp_buffer_size); + m_socket->async_receive_from(asio::buffer(m_buffer), m_sender + , boost::bind(&udp_tracker_connection::connect_response, self(), _1, _2)); + } + + void udp_tracker_connection::connect_response(asio::error_code const& error + , std::size_t bytes_transferred) try + { + if (error == asio::error::operation_aborted) return; + if (!m_socket) return; // the operation was aborted + if (error) + { + fail(-1, error.message().c_str()); + return; + } + + if (m_target != m_sender) + { + // this packet was not received from the tracker + m_socket->async_receive_from(asio::buffer(m_buffer), m_sender + , boost::bind(&udp_tracker_connection::connect_response, self(), _1, _2)); + return; + } + + if (bytes_transferred >= udp_buffer_size) + { + fail(-1, "udp response too big"); + return; + } + + if (bytes_transferred < 8) + { + fail(-1, "got a message with size < 8"); + return; + } + + restart_read_timeout(); + + const char* ptr = &m_buffer[0]; + int action = detail::read_int32(ptr); + int transaction = detail::read_int32(ptr); + + if (action == action_error) + { + fail(-1, std::string(ptr, bytes_transferred - 8).c_str()); + return; + } + + if (action != action_connect) + { + fail(-1, "invalid action in connect reply"); + return; + } + + if (m_transaction_id != transaction) + { + fail(-1, "incorrect transaction id"); + return; + } + + if (bytes_transferred < 16) + { + fail(-1, "udp_tracker_connection: " + "got a message with size < 16"); + return; + } + // reset transaction + m_transaction_id = 0; + m_attempts = 0; + m_connection_id = detail::read_int64(ptr); + +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + if (has_requester()) + { + requester().debug_log("<== UDP_TRACKER_CONNECT_RESPONSE [" + + lexical_cast(m_connection_id) + "]"); + } +#endif + + if (tracker_req().kind == tracker_request::announce_request) + send_udp_announce(); + else if (tracker_req().kind == tracker_request::scrape_request) + send_udp_scrape(); + } + catch (std::exception& e) + { + fail(-1, e.what()); + } + + void udp_tracker_connection::send_udp_announce() + { + if (m_transaction_id == 0) + m_transaction_id = rand() ^ (rand() << 16); + + if (!m_socket) return; // the operation was aborted + + std::vector buf; + std::back_insert_iterator > out(buf); + + tracker_request const& req = tracker_req(); + + // connection_id + detail::write_int64(m_connection_id, out); + // action (announce) + detail::write_int32(action_announce, out); + // transaction_id + detail::write_int32(m_transaction_id, out); + // info_hash + std::copy(req.info_hash.begin(), req.info_hash.end(), out); + // peer_id + std::copy(req.pid.begin(), req.pid.end(), out); + // downloaded + detail::write_int64(req.downloaded, out); + // left + detail::write_int64(req.left, out); + // uploaded + detail::write_int64(req.uploaded, out); + // event + detail::write_int32(req.event, out); + // ip address + detail::write_int32(0, out); + // key + detail::write_int32(req.key, out); + // num_want + detail::write_int32(req.num_want, out); + // port + detail::write_uint16(req.listen_port, out); + // extensions + detail::write_uint16(0, out); + +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + if (has_requester()) + { + requester().debug_log("==> UDP_TRACKER_ANNOUNCE [" + + lexical_cast(req.info_hash) + "]"); + } +#endif + + m_socket->send(asio::buffer(buf), 0); + ++m_attempts; + + m_socket->async_receive_from(asio::buffer(m_buffer), m_sender + , bind(&udp_tracker_connection::announce_response, self(), _1, _2)); + } + + void udp_tracker_connection::send_udp_scrape() + { + if (m_transaction_id == 0) + m_transaction_id = rand() ^ (rand() << 16); + + if (!m_socket) return; // the operation was aborted + + std::vector buf; + std::back_insert_iterator > out(buf); + + // connection_id + detail::write_int64(m_connection_id, out); + // action (scrape) + detail::write_int32(action_scrape, out); + // transaction_id + detail::write_int32(m_transaction_id, out); + // info_hash + std::copy(tracker_req().info_hash.begin(), tracker_req().info_hash.end(), out); + + m_socket->send(asio::buffer(&buf[0], buf.size()), 0); + ++m_attempts; + + m_socket->async_receive_from(asio::buffer(m_buffer), m_sender + , bind(&udp_tracker_connection::scrape_response, self(), _1, _2)); + } + + void udp_tracker_connection::announce_response(asio::error_code const& error + , std::size_t bytes_transferred) try + { + if (error == asio::error::operation_aborted) return; + if (!m_socket) return; // the operation was aborted + if (error) + { + fail(-1, error.message().c_str()); + return; + } + + if (m_target != m_sender) + { + // this packet was not received from the tracker + m_socket->async_receive_from(asio::buffer(m_buffer), m_sender + , bind(&udp_tracker_connection::connect_response, self(), _1, _2)); + return; + } + + if (bytes_transferred >= udp_buffer_size) + { + fail(-1, "udp response too big"); + return; + } + + if (bytes_transferred < 8) + { + fail(-1, "got a message with size < 8"); + return; + } + + restart_read_timeout(); + char* buf = &m_buffer[0]; + int action = detail::read_int32(buf); + int transaction = detail::read_int32(buf); + + if (transaction != m_transaction_id) + { + fail(-1, "incorrect transaction id"); + return; + } + + if (action == action_error) + { + fail(-1, std::string(buf, bytes_transferred - 8).c_str()); + return; + } + + if (action != action_announce) + { + fail(-1, "invalid action in announce response"); + return; + } + + if (bytes_transferred < 20) + { + fail(-1, "got a message with size < 20"); + return; + } + + int interval = detail::read_int32(buf); + int incomplete = detail::read_int32(buf); + int complete = detail::read_int32(buf); + int num_peers = (bytes_transferred - 20) / 6; + if ((bytes_transferred - 20) % 6 != 0) + { + fail(-1, "invalid udp tracker response length"); + return; + } + +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + if (has_requester()) + { + requester().debug_log("<== UDP_TRACKER_ANNOUNCE_RESPONSE"); + } +#endif + + if (!has_requester()) + { + m_man.remove_request(this); + return; + } + + std::vector peer_list; + for (int i = 0; i < num_peers; ++i) + { + peer_entry e; + std::stringstream s; + s << (int)detail::read_uint8(buf) << "."; + s << (int)detail::read_uint8(buf) << "."; + s << (int)detail::read_uint8(buf) << "."; + s << (int)detail::read_uint8(buf); + e.ip = s.str(); + e.port = detail::read_uint16(buf); + e.pid.clear(); + peer_list.push_back(e); + } + + requester().tracker_response(tracker_req(), peer_list, interval + , complete, incomplete); + + m_man.remove_request(this); + return; + } + catch (std::exception& e) + { + fail(-1, e.what()); + }; // msvc 7.1 seems to require this + + void udp_tracker_connection::scrape_response(asio::error_code const& error + , std::size_t bytes_transferred) try + { + if (error == asio::error::operation_aborted) return; + if (!m_socket) return; // the operation was aborted + if (error) + { + fail(-1, error.message().c_str()); + return; + } + + if (m_target != m_sender) + { + // this packet was not received from the tracker + m_socket->async_receive_from(asio::buffer(m_buffer), m_sender + , bind(&udp_tracker_connection::connect_response, self(), _1, _2)); + return; + } + + if (bytes_transferred >= udp_buffer_size) + { + fail(-1, "udp response too big"); + return; + } + + if (bytes_transferred < 8) + { + fail(-1, "got a message with size < 8"); + return; + } + + restart_read_timeout(); + char* buf = &m_buffer[0]; + int action = detail::read_int32(buf); + int transaction = detail::read_int32(buf); + + if (transaction != m_transaction_id) + { + fail(-1, "incorrect transaction id"); + return; + } + + if (action == action_error) + { + fail(-1, std::string(buf, bytes_transferred - 8).c_str()); + return; + } + + if (action != action_scrape) + { + fail(-1, "invalid action in announce response"); + return; + } + + if (bytes_transferred < 20) + { + fail(-1, "got a message with size < 20"); + return; + } + + int complete = detail::read_int32(buf); + /*int downloaded = */detail::read_int32(buf); + int incomplete = detail::read_int32(buf); + + if (!has_requester()) + { + m_man.remove_request(this); + return; + } + + std::vector peer_list; + requester().tracker_response(tracker_req(), peer_list, 0 + , complete, incomplete); + + m_man.remove_request(this); + } + catch (std::exception& e) + { + fail(-1, e.what()); + } + +} + diff --git a/encryption/libtorrent/src/upnp.cpp b/encryption/libtorrent/src/upnp.cpp new file mode 100644 index 000000000..c60725e9d --- /dev/null +++ b/encryption/libtorrent/src/upnp.cpp @@ -0,0 +1,1038 @@ +/* + +Copyright (c) 2007, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/pch.hpp" + +#include "libtorrent/socket.hpp" +#include "libtorrent/upnp.hpp" +#include "libtorrent/io.hpp" +#include "libtorrent/http_tracker_connection.hpp" +#include "libtorrent/xml_parse.hpp" +#include "libtorrent/connection_queue.hpp" + +#include +#include +#include +#include +#include +#include + +#if (defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)) && !defined(TORRENT_UPNP_LOGGING) +#define TORRENT_UPNP_LOGGING +#endif + +using boost::bind; +using namespace libtorrent; + +address_v4 upnp::upnp_multicast_address; +udp::endpoint upnp::upnp_multicast_endpoint; + +namespace libtorrent +{ + bool is_local(address const& a) + { + if (a.is_v6()) return false; + address_v4 a4 = a.to_v4(); + return ((a4.to_ulong() & 0xff000000) == 0x0a000000 + || (a4.to_ulong() & 0xfff00000) == 0xac100000 + || (a4.to_ulong() & 0xffff0000) == 0xc0a80000); + } + + address_v4 guess_local_address(asio::io_service& ios) + { + // 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(asio::ip::host_name(), "0")); + for (;i != udp::resolver_iterator(); ++i) + { + // ignore the loopback + if (i->endpoint().address() == address_v4((127 << 24) + 1)) continue; + // ignore addresses that are not on a local network + if (!is_local(i->endpoint().address())) continue; + // ignore non-IPv4 addresses + if (i->endpoint().address().is_v4()) break; + } + if (i == udp::resolver_iterator()) return address_v4::any(); + return i->endpoint().address().to_v4(); + } +} + +upnp::upnp(io_service& ios, connection_queue& cc + , address const& listen_interface, std::string const& user_agent + , portmap_callback_t const& cb) + : m_udp_local_port(0) + , m_tcp_local_port(0) + , m_user_agent(user_agent) + , m_callback(cb) + , m_retry_count(0) + , m_socket(ios) + , m_broadcast_timer(ios) + , m_refresh_timer(ios) + , m_strand(ios) + , m_disabled(false) + , m_closing(false) + , m_cc(cc) +{ + // UPnP multicast address and port + upnp_multicast_address = address_v4::from_string("239.255.255.250"); + upnp_multicast_endpoint = udp::endpoint(upnp_multicast_address, 1900); + +#ifdef TORRENT_UPNP_LOGGING + m_log.open("upnp.log", std::ios::in | std::ios::out | std::ios::trunc); +#endif + rebind(listen_interface); +} + +upnp::~upnp() +{ +} + +void upnp::rebind(address const& listen_interface) try +{ + address_v4 bind_to = address_v4::any(); + if (listen_interface.is_v4() && listen_interface != address_v4::any()) + { + m_local_ip = listen_interface.to_v4(); + bind_to = listen_interface.to_v4(); + if (!is_local(m_local_ip)) + { + // the local address seems to be an external + // internet address. Assume it is not behind a NAT + throw std::runtime_error("local IP is not on a local network"); + } + } + else + { + m_local_ip = guess_local_address(m_socket.io_service()); + bind_to = address_v4::any(); + } + + if (!is_local(m_local_ip)) + { + throw std::runtime_error("local host is probably not on a NATed " + "network. disabling UPnP"); + } + +#ifdef TORRENT_UPNP_LOGGING + m_log << time_now_string() + << " local ip: " << m_local_ip.to_string() + << " bind to: " << bind_to.to_string() << std::endl; +#endif + + // the local interface hasn't changed + if (m_socket.is_open() + && m_socket.local_endpoint().address() == m_local_ip) + return; + + m_socket.close(); + + using namespace asio::ip::multicast; + + m_socket.open(udp::v4()); + m_socket.set_option(datagram_socket::reuse_address(true)); + m_socket.bind(udp::endpoint(bind_to, 0)); + + m_socket.set_option(join_group(upnp_multicast_address)); + m_socket.set_option(outbound_interface(bind_to)); + m_socket.set_option(hops(255)); + m_disabled = false; + + m_retry_count = 0; + discover_device(); +} +catch (std::exception& e) +{ + disable(); + std::stringstream msg; + msg << "UPnP portmapping disabled: " << e.what(); + m_callback(0, 0, msg.str()); +}; + +void upnp::discover_device() try +{ + 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"; + + m_socket.async_receive_from(asio::buffer(m_receive_buffer + , sizeof(m_receive_buffer)), m_remote, m_strand.wrap(bind( + &upnp::on_reply, this, _1, _2))); + + asio::error_code ec; +#ifdef TORRENT_DEBUG_UPNP + // simulate packet loss + if (m_retry_count & 1) +#endif + m_socket.send_to(asio::buffer(msearch, sizeof(msearch) - 1) + , upnp_multicast_endpoint, 0, ec); + + if (ec) + { + disable(); + return; + } + + ++m_retry_count; + m_broadcast_timer.expires_from_now(milliseconds(250 * m_retry_count)); + m_broadcast_timer.async_wait(m_strand.wrap(bind(&upnp::resend_request + , this, _1))); + +#ifdef TORRENT_UPNP_LOGGING + m_log << time_now_string() + << " ==> Broadcasting search for rootdevice" << std::endl; +#endif +} +catch (std::exception&) +{ + disable(); +} + +void upnp::set_mappings(int tcp, int udp) +{ +#ifdef TORRENT_UPNP_LOGGING + m_log << time_now_string() + << " *** set mappings " << tcp << " " << udp; + if (m_disabled) m_log << " DISABLED"; + m_log << std::endl; +#endif + + if (m_disabled) return; + if (udp != 0) m_udp_local_port = udp; + if (tcp != 0) m_tcp_local_port = tcp; + + for (std::set::iterator i = m_devices.begin() + , end(m_devices.end()); i != end; ++i) + { + rootdevice& d = const_cast(*i); + if (d.mapping[0].local_port != m_tcp_local_port) + { + if (d.mapping[0].external_port == 0) + d.mapping[0].external_port = m_tcp_local_port; + d.mapping[0].local_port = m_tcp_local_port; + d.mapping[0].need_update = true; + } + if (d.mapping[1].local_port != m_udp_local_port) + { + if (d.mapping[1].external_port == 0) + d.mapping[1].external_port = m_udp_local_port; + d.mapping[1].local_port = m_udp_local_port; + d.mapping[1].need_update = true; + } + if (d.service_namespace + && (d.mapping[0].need_update || d.mapping[1].need_update)) + map_port(d, 0); + } +} + +void upnp::resend_request(asio::error_code const& e) +#ifndef NDEBUG +try +#endif +{ + if (e) return; + if (m_retry_count < 9 + && (m_devices.empty() || m_retry_count < 4)) + { + discover_device(); + return; + } + + if (m_devices.empty()) + { +#ifdef TORRENT_UPNP_LOGGING + m_log << time_now_string() + << " *** Got no response in 9 retries. Giving up, " + "disabling UPnP." << std::endl; +#endif + disable(); + 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); + try + { + d.upnp_connection.reset(new http_connection(m_socket.io_service() + , m_cc, m_strand.wrap(bind(&upnp::on_upnp_xml, this, _1, _2 + , boost::ref(d))))); + d.upnp_connection->get(d.url); + } + catch (std::exception& e) + { +#ifdef TORRENT_UPNP_LOGGING + m_log << time_now_string() + << " *** Connection failed to: " << d.url + << " " << e.what() << std::endl; +#endif + d.disabled = true; + } + } + } +} +#ifndef NDEBUG +catch (std::exception&) +{ + assert(false); +} +#endif + +void upnp::on_reply(asio::error_code const& e + , std::size_t bytes_transferred) +#ifndef NDEBUG +try +#endif +{ + using namespace libtorrent::detail; + if (e) return; + + // 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 +*/ + http_parser p; + try + { + p.incoming(buffer::const_interval(m_receive_buffer + , m_receive_buffer + bytes_transferred)); + } + catch (std::exception& e) + { +#ifdef TORRENT_UPNP_LOGGING + m_log << time_now_string() + << " <== Rootdevice responded with incorrect HTTP packet: " + << e.what() << ". Ignoring device" << std::endl; +#endif + return; + } + + if (p.status_code() != 200) + { +#ifdef TORRENT_UPNP_LOGGING + m_log << time_now_string() + << " <== Rootdevice responded with HTTP status: " << p.status_code() + << ". Ignoring device" << std::endl; +#endif + return; + } + + if (!p.header_finished()) + { +#ifdef TORRENT_UPNP_LOGGING + m_log << time_now_string() + << " <== Rootdevice responded with incomplete HTTP " + "packet. Ignoring device" << std::endl; +#endif + return; + } + + std::string url = p.header("location"); + if (url.empty()) + { +#ifdef TORRENT_UPNP_LOGGING + m_log << time_now_string() + << " <== Rootdevice response is missing a location header. " + "Ignoring device" << std::endl; +#endif + 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); + + // ignore the auth here. It will be re-parsed + // by the http connection later + + if (protocol != "http") + { +#ifdef TORRENT_UPNP_LOGGING + m_log << time_now_string() + << " <== Rootdevice uses unsupported protocol: '" << protocol + << "'. Ignoring device" << std::endl; +#endif + return; + } + + if (d.port == 0) + { +#ifdef TORRENT_UPNP_LOGGING + m_log << time_now_string() + << " <== Rootdevice responded with a url with port 0. " + "Ignoring device" << std::endl; +#endif + return; + } +#ifdef TORRENT_UPNP_LOGGING + m_log << time_now_string() + << " <== Found rootdevice: " << d.url << std::endl; +#endif + + if (m_tcp_local_port != 0) + { + d.mapping[0].need_update = true; + d.mapping[0].local_port = m_tcp_local_port; +#ifdef TORRENT_UPNP_LOGGING + m_log << time_now_string() << " *** Mapping 0 will be updated" << std::endl; +#endif + } + if (m_udp_local_port != 0) + { + d.mapping[1].need_update = true; + d.mapping[1].local_port = m_udp_local_port; +#ifdef TORRENT_UPNP_LOGGING + m_log << time_now_string() << " *** Mapping 1 will be updated" << std::endl; +#endif + } + boost::tie(i, boost::tuples::ignore) = m_devices.insert(d); + } + + + // since we're using udp, send the query 4 times + // just to make sure we find all devices + if (m_retry_count >= 4 && !m_devices.empty()) + { + m_broadcast_timer.cancel(); + + 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); + try + { + d.upnp_connection.reset(new http_connection(m_socket.io_service() + , m_cc, m_strand.wrap(bind(&upnp::on_upnp_xml, this, _1, _2 + , boost::ref(d))))); + d.upnp_connection->get(d.url); + } + catch (std::exception& e) + { +#ifdef TORRENT_UPNP_LOGGING + m_log << time_now_string() + << " *** Connection failed to: " << d.url + << " " << e.what() << std::endl; +#endif + d.disabled = true; + } + } + } + } +} +#ifndef NDEBUG +catch (std::exception&) +{ + assert(false); +}; +#endif + +void upnp::post(rootdevice& d, std::stringstream const& soap + , std::string const& soap_action) +{ + std::stringstream header; + + header << "POST " << d.control_url << " HTTP/1.1\r\n" + "Host: " << d.hostname << ":" << d.port << "\r\n" + "Content-Type: text/xml; charset=\"utf-8\"\r\n" + "Content-Length: " << soap.str().size() << "\r\n" + "Soapaction: \"" << d.service_namespace << "#" << soap_action << "\"\r\n\r\n" << soap.str(); + + d.upnp_connection->sendbuffer = header.str(); + d.upnp_connection->start(d.hostname, boost::lexical_cast(d.port) + , seconds(10)); +} + +void upnp::map_port(rootdevice& d, int i) +{ + if (d.upnp_connection) return; + + if (!d.mapping[i].need_update) + { +#ifdef TORRENT_UPNP_LOGGING + m_log << time_now_string() << " *** mapping (" << i + << ") does not need update, skipping" << std::endl; +#endif + if (i < num_mappings - 1) + map_port(d, i + 1); + return; + } + d.mapping[i].need_update = false; + assert(!d.upnp_connection); + assert(d.service_namespace); + + d.upnp_connection.reset(new http_connection(m_socket.io_service() + , m_cc, m_strand.wrap(bind(&upnp::on_upnp_map_response, this, _1, _2 + , boost::ref(d), i)))); + + std::string soap_action = "AddPortMapping"; + + std::stringstream soap; + + soap << "\n" + "" + ""; + + soap << "" + "" << d.mapping[i].external_port << "" + "" << (d.mapping[i].protocol ? "UDP" : "TCP") << "" + "" << d.mapping[i].local_port << "" + "" << m_local_ip.to_string() << "" + "1" + "" << m_user_agent << "" + "" << d.lease_duration << ""; + soap << ""; + + post(d, soap, soap_action); +#ifdef TORRENT_UPNP_LOGGING + m_log << time_now_string() + << " ==> AddPortMapping: " << soap.str() << std::endl; +#endif + +} + +// requires the mutex to be locked +void upnp::unmap_port(rootdevice& d, int i) +{ + if (d.mapping[i].external_port == 0) + { + if (i < num_mappings - 1) + { + unmap_port(d, i + 1); + } + else + { + m_devices.erase(d); + } + return; + } + d.upnp_connection.reset(new http_connection(m_socket.io_service() + , m_cc, m_strand.wrap(bind(&upnp::on_upnp_unmap_response, this, _1, _2 + , boost::ref(d), i)))); + + std::string soap_action = "DeletePortMapping"; + + std::stringstream soap; + + soap << "\n" + "" + ""; + + soap << "" + "" << d.mapping[i].external_port << "" + "" << (d.mapping[i].protocol ? "UDP" : "TCP") << ""; + soap << ""; + + post(d, soap, soap_action); +#ifdef TORRENT_UPNP_LOGGING + m_log << time_now_string() + << " ==> DeletePortMapping: " << soap.str() << std::endl; +#endif +} + +namespace +{ + struct parse_state + { + parse_state(): found_service(false), exit(false) {} + void reset(char const* st) + { + found_service = false; + exit = false; + service_type = st; + } + bool found_service; + bool exit; + std::string top_tag; + std::string control_url; + char const* service_type; + }; + + void find_control_url(int type, char const* string, parse_state& state) + { + if (state.exit) return; + + if (type == xml_start_tag) + { + if ((!state.top_tag.empty() && state.top_tag == "service") + || !strcmp(string, "service")) + { + state.top_tag = string; + } + } + else if (type == xml_end_tag) + { + if (!strcmp(string, "service")) + { + state.top_tag.clear(); + if (state.found_service) state.exit = true; + } + else if (!state.top_tag.empty() && state.top_tag != "service") + state.top_tag = "service"; + } + else if (type == xml_string) + { + if (state.top_tag == "serviceType") + { + if (!strcmp(string, state.service_type)) + state.found_service = true; + } + else if (state.top_tag == "controlURL") + { + state.control_url = string; + if (state.found_service) state.exit = true; + } + } + } + +} + +void upnp::on_upnp_xml(asio::error_code const& e + , libtorrent::http_parser const& p, rootdevice& d) try +{ + if (d.upnp_connection) + { + d.upnp_connection->close(); + d.upnp_connection.reset(); + } + + if (e) + { +#ifdef TORRENT_UPNP_LOGGING + m_log << time_now_string() + << " <== error while fetching control url: " << e.message() << std::endl; +#endif + return; + } + + if (!p.header_finished()) + { +#ifdef TORRENT_UPNP_LOGGING + m_log << time_now_string() + << " <== incomplete http message" << std::endl; +#endif + return; + } + + parse_state s; + s.reset("urn:schemas-upnp-org:service:WANIPConnection:1"); + xml_parse((char*)p.get_body().begin, (char*)p.get_body().end + , m_strand.wrap(bind(&find_control_url, _1, _2, boost::ref(s)))); + if (s.found_service) + { + d.service_namespace = s.service_type; + } + else + { + // we didn't find the WAN IP connection, look for + // a PPP connection + s.reset("urn:schemas-upnp-org:service:WANPPPConnection:1"); + xml_parse((char*)p.get_body().begin, (char*)p.get_body().end + , m_strand.wrap(bind(&find_control_url, _1, _2, boost::ref(s)))); + if (s.found_service) + { + d.service_namespace = s.service_type; + } + else + { +#ifdef TORRENT_UPNP_LOGGING + m_log << time_now_string() + << " <== Rootdevice response, did not find a port mapping interface" << std::endl; +#endif + return; + } + } + +#ifdef TORRENT_UPNP_LOGGING + m_log << time_now_string() + << " <== Rootdevice response, found control URL: " << s.control_url + << " namespace: " << d.service_namespace << std::endl; +#endif + + d.control_url = s.control_url; + + map_port(d, 0); +} +catch (std::exception&) +{ + disable(); +}; + +void upnp::disable() +{ + m_disabled = true; + m_devices.clear(); + m_broadcast_timer.cancel(); + m_refresh_timer.cancel(); + m_socket.close(); +} + +namespace +{ + 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; + }; + + void find_error_code(int type, char const* string, error_code_parse_state& state) + { + if (state.exit) return; + if (type == xml_start_tag && !strcmp("errorCode", string)) + { + state.in_error_code = true; + } + else if (type == xml_string && state.in_error_code) + { + state.error_code = std::atoi(string); + state.exit = true; + } + } +} + +namespace +{ + struct error_code_t + { + int code; + char const* msg; + }; + + error_code_t error_codes[] = + { + {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 "} + }; + +} + +void upnp::on_upnp_map_response(asio::error_code const& e + , libtorrent::http_parser const& p, rootdevice& d, int mapping) try +{ + if (d.upnp_connection) + { + d.upnp_connection->close(); + d.upnp_connection.reset(); + } + + if (e) + { +#ifdef TORRENT_UPNP_LOGGING + m_log << time_now_string() + << " <== error while adding portmap: " << e.message() << std::endl; +#endif + m_devices.erase(d); + return; + } + + if (m_closing) return; + +// error code response may look like this: +// +// +// +// s:Client +// UPnPError +// +// +// 402 +// Invalid Args +// +// +// +// +// + + if (!p.header_finished()) + { +#ifdef TORRENT_UPNP_LOGGING + m_log << time_now_string() + << " <== incomplete http message" << std::endl; +#endif + m_devices.erase(d); + return; + } + + error_code_parse_state s; + xml_parse((char*)p.get_body().begin, (char*)p.get_body().end + , m_strand.wrap(bind(&find_error_code, _1, _2, boost::ref(s)))); + +#ifdef TORRENT_UPNP_LOGGING + if (s.error_code != -1) + { + m_log << time_now_string() + << " <== got error message: " << s.error_code << std::endl; + } +#endif + + if (s.error_code == 725) + { + // only permanent leases supported + d.lease_duration = 0; + d.mapping[mapping].need_update = true; + map_port(d, mapping); + return; + } + else if (s.error_code == 718) + { + // conflict in mapping, try next external port + ++d.mapping[mapping].external_port; + d.mapping[mapping].need_update = true; + map_port(d, mapping); + return; + } + else if (s.error_code != -1) + { + int num_errors = sizeof(error_codes) / sizeof(error_codes[0]); + error_code_t* end = error_codes + num_errors; + error_code_t tmp = {s.error_code, 0}; + error_code_t* e = std::lower_bound(error_codes, end, tmp + , bind(&error_code_t::code, _1) < bind(&error_code_t::code, _2)); + std::string error_string = "UPnP mapping error "; + error_string += boost::lexical_cast(s.error_code); + if (e != end && e->code == s.error_code) + { + error_string += ": "; + error_string += e->msg; + } + m_callback(0, 0, error_string); + } + +#ifdef TORRENT_UPNP_LOGGING + m_log << time_now_string() + << " <== map response: " << std::string(p.get_body().begin, p.get_body().end) + << std::endl; +#endif + + if (s.error_code == -1) + { + int tcp = 0; + int udp = 0; + + if (mapping == 0) + tcp = d.mapping[mapping].external_port; + else + udp = d.mapping[mapping].external_port; + + m_callback(tcp, udp, ""); + if (d.lease_duration > 0) + { + d.mapping[mapping].expires = time_now() + + seconds(int(d.lease_duration * 0.75f)); + ptime next_expire = m_refresh_timer.expires_at(); + if (next_expire < time_now() + || next_expire > d.mapping[mapping].expires) + { + m_refresh_timer.expires_at(d.mapping[mapping].expires); + m_refresh_timer.async_wait(m_strand.wrap(bind(&upnp::on_expire, this, _1))); + } + } + else + { + d.mapping[mapping].expires = max_time(); + } + } + + for (int i = 0; i < num_mappings; ++i) + { + if (d.mapping[i].need_update) + { + map_port(d, i); + return; + } + } +} +catch (std::exception&) +{ + disable(); +}; + +void upnp::on_upnp_unmap_response(asio::error_code const& e + , libtorrent::http_parser const& p, rootdevice& d, int mapping) try +{ + if (d.upnp_connection) + { + d.upnp_connection->close(); + d.upnp_connection.reset(); + } + + if (e) + { +#ifdef TORRENT_UPNP_LOGGING + m_log << time_now_string() + << " <== error while deleting portmap: " << e.message() << std::endl; +#endif + } + + if (!p.header_finished()) + { +#ifdef TORRENT_UPNP_LOGGING + m_log << time_now_string() + << " <== incomplete http message" << std::endl; +#endif + return; + } + +#ifdef TORRENT_UPNP_LOGGING + m_log << time_now_string() + << " <== unmap response: " << std::string(p.get_body().begin, p.get_body().end) + << std::endl; +#endif + + // ignore errors and continue with the next mapping for this device + if (mapping < num_mappings - 1) + { + unmap_port(d, mapping + 1); + return; + } + + // the main thread is likely to be waiting for + // all the unmap operations to complete + m_devices.erase(d); +} +catch (std::exception&) +{ + disable(); +}; + +void upnp::on_expire(asio::error_code const& e) try +{ + if (e) return; + + ptime now = time_now(); + ptime next_expire = max_time(); + + for (std::set::iterator i = m_devices.begin() + , end(m_devices.end()); i != end; ++i) + { + rootdevice& d = const_cast(*i); + 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(); + map_port(d, m); + } + else if (d.mapping[m].expires < next_expire) + { + next_expire = d.mapping[m].expires; + } + } + } + if (next_expire != max_time()) + { + m_refresh_timer.expires_at(next_expire); + m_refresh_timer.async_wait(m_strand.wrap(bind(&upnp::on_expire, this, _1))); + } +} +catch (std::exception&) +{ + disable(); +}; + +void upnp::close() +{ + m_refresh_timer.cancel(); + m_broadcast_timer.cancel(); + m_closing = true; + m_socket.close(); + + if (m_disabled) + { + m_devices.clear(); + return; + } + + for (std::set::iterator i = m_devices.begin() + , end(m_devices.end()); i != end;) + { + rootdevice& d = const_cast(*i); + if (d.control_url.empty()) + { + m_devices.erase(i++); + continue; + } + ++i; + unmap_port(d, 0); + } +} + diff --git a/encryption/libtorrent/src/ut_pex.cpp b/encryption/libtorrent/src/ut_pex.cpp new file mode 100644 index 000000000..41df77475 --- /dev/null +++ b/encryption/libtorrent/src/ut_pex.cpp @@ -0,0 +1,359 @@ +/* + +Copyright (c) 2006, 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/pch.hpp" + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/peer_connection.hpp" +#include "libtorrent/bt_peer_connection.hpp" +#include "libtorrent/bencode.hpp" +#include "libtorrent/torrent.hpp" +#include "libtorrent/extensions.hpp" + +#include "libtorrent/extensions/ut_pex.hpp" + +namespace libtorrent { namespace +{ + 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 peers that we haven't connected to + // (that have connected to us) + if (!p.is_local()) return false; + // don't send out peers that we haven't successfully connected to + if (p.is_connecting()) return false; + // ut pex does not support IPv6 + if (!p.remote().address().is_v4()) return false; + return true; + } + + struct ut_pex_plugin: torrent_plugin + { + ut_pex_plugin(torrent& t): m_torrent(t), m_1_minute(0) {} + + virtual boost::shared_ptr new_connection(peer_connection* pc); + + std::vector& get_ut_pex_msg() + { + return m_ut_pex_msg; + } + + // 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() + { + if (++m_1_minute < 60) return; + + m_1_minute = 0; + + 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); + + std::set dropped; + m_old_peers.swap(dropped); + + int num_added = 0; + for (torrent::peer_iterator i = m_torrent.begin() + , end(m_torrent.end()); i != end; ++i) + { + if (!send_peer(*i->second)) continue; + + m_old_peers.insert(i->first); + + std::set::iterator di = dropped.find(i->first); + if (di == dropped.end()) + { + // don't write too big of a package + if (num_added >= max_peer_entries) break; + + // only send proper bittorrent peers + bt_peer_connection* p = dynamic_cast(i->second); + if (!p) continue; + + // i->first was added since the last time + detail::write_endpoint(i->first, pla_out); + // no supported flags to set yet + // 0x01 - peer supports encryption + // 0x02 - peer is a seed + int flags = p->is_seed() ? 2 : 0; +#ifndef TORRENT_DISABLE_ENCRYPTION + flags |= p->supports_encryption() ? 1 : 0; +#endif + detail::write_uint8(flags, plf_out); + ++num_added; + } + 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()) continue; + detail::write_endpoint(*i, pld_out); + } + + m_ut_pex_msg.clear(); + bencode(std::back_inserter(m_ut_pex_msg), pex); + } + + private: + torrent& m_torrent; + + std::set m_old_peers; + int m_1_minute; + std::vector m_ut_pex_msg; + }; + + + struct ut_pex_peer_plugin : 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_1_minute(0) + , m_message_index(0) + , m_first_time(true) + {} + + virtual void add_handshake(entry& h) + { + entry& messages = h["m"]; + messages[extension_name] = extension_index; + } + + virtual bool on_extension_handshake(entry const& h) + { + entry const& messages = h["m"]; + + if (entry const* index = messages.find_key(extension_name)) + { + m_message_index = index->integer(); + return true; + } + else + { + m_message_index = 0; + return false; + } + } + + virtual bool on_extended(int length, int msg, buffer::const_interval body) + try + { + if (msg != extension_index) return false; + if (m_message_index == 0) return false; + + if (length > 500 * 1024) + throw protocol_error("ut peer exchange message larger than 500 kB"); + + if (body.left() < length) return true; + + entry pex_msg = bdecode(body.begin, body.end); + std::string const& peers = pex_msg["added"].string(); + std::string const& peer_flags = pex_msg["added.f"].string(); + + int num_peers = peers.length() / 6; + char const* in = peers.c_str(); + char const* fin = peer_flags.c_str(); + + if (int(peer_flags.size()) != num_peers) + return true; + + peer_id pid(0); + policy& p = m_torrent.get_policy(); + for (int i = 0; i < num_peers; ++i) + { + tcp::endpoint adr = detail::read_v4_endpoint(in); + char flags = detail::read_uint8(fin); + p.peer_from_tracker(adr, pid, peer_info::pex, flags); + } + return true; + } + catch (std::exception&) + { + return true; + } + + // the peers second tick + // every minute we send a pex message + virtual void tick() + { + if (!m_message_index) return; // no handshake yet + if (++m_1_minute <= 60) return; + + if (m_first_time) + { + send_ut_peer_list(); + m_first_time = false; + } + else + { + send_ut_peer_diff(); + } + m_1_minute = 0; + } + + private: + + void send_ut_peer_diff() + { + std::vector const& pex_msg = m_tp.get_ut_pex_msg(); + + buffer::interval i = m_pc.allocate_send_buffer(6 + pex_msg.size()); + + detail::write_uint32(1 + 1 + pex_msg.size(), i.begin); + detail::write_uint8(bt_peer_connection::msg_extended, i.begin); + detail::write_uint8(m_message_index, i.begin); + std::copy(pex_msg.begin(), pex_msg.end(), i.begin); + i.begin += pex_msg.size(); + + assert(i.begin == i.end); + m_pc.setup_send(); + } + + 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); + + int num_added = 0; + for (torrent::peer_iterator i = m_torrent.begin() + , end(m_torrent.end()); i != end; ++i) + { + if (!send_peer(*i->second)) continue; + + // don't write too big of a package + if (num_added >= max_peer_entries) break; + + // only send proper bittorrent peers + bt_peer_connection* p = dynamic_cast(i->second); + if (!p) continue; + + // i->first was added since the last time + detail::write_endpoint(i->first, pla_out); + // no supported flags to set yet + // 0x01 - peer supports encryption + // 0x02 - peer is a seed + int flags = p->is_seed() ? 2 : 0; +#ifndef TORRENT_DISABLE_ENCRYPTION + flags |= p->supports_encryption() ? 1 : 0; +#endif + detail::write_uint8(flags, plf_out); + ++num_added; + } + std::vector pex_msg; + bencode(std::back_inserter(pex_msg), pex); + + buffer::interval i = m_pc.allocate_send_buffer(6 + pex_msg.size()); + + detail::write_uint32(1 + 1 + pex_msg.size(), i.begin); + detail::write_uint8(bt_peer_connection::msg_extended, i.begin); + detail::write_uint8(m_message_index, i.begin); + std::copy(pex_msg.begin(), pex_msg.end(), i.begin); + i.begin += pex_msg.size(); + + assert(i.begin == i.end); + m_pc.setup_send(); + } + + torrent& m_torrent; + peer_connection& m_pc; + ut_pex_plugin& m_tp; + int m_1_minute; + 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 full + // message should be sent. + bool m_first_time; + }; + + boost::shared_ptr ut_pex_plugin::new_connection(peer_connection* pc) + { + bt_peer_connection* c = dynamic_cast(pc); + if (!c) return boost::shared_ptr(); + return boost::shared_ptr(new ut_pex_peer_plugin(m_torrent + , *pc, *this)); + } +}} + +namespace libtorrent +{ + + boost::shared_ptr create_ut_pex_plugin(torrent* t) + { + if (t->torrent_file().priv()) + { + return boost::shared_ptr(); + } + return boost::shared_ptr(new ut_pex_plugin(*t)); + } + +} + + diff --git a/encryption/libtorrent/src/web_peer_connection.cpp b/encryption/libtorrent/src/web_peer_connection.cpp new file mode 100755 index 000000000..5a8acf512 --- /dev/null +++ b/encryption/libtorrent/src/web_peer_connection.cpp @@ -0,0 +1,639 @@ +/* + +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 "libtorrent/pch.hpp" + +#include +#include +#include +#include +#include +#include + +#include "libtorrent/web_peer_connection.hpp" +#include "libtorrent/session.hpp" +#include "libtorrent/identify_client.hpp" +#include "libtorrent/entry.hpp" +#include "libtorrent/bencode.hpp" +#include "libtorrent/alert_types.hpp" +#include "libtorrent/invariant_check.hpp" +#include "libtorrent/io.hpp" +#include "libtorrent/version.hpp" +#include "libtorrent/aux_/session_impl.hpp" + +using boost::bind; +using boost::shared_ptr; +using libtorrent::aux::session_impl; + +namespace libtorrent +{ + web_peer_connection::web_peer_connection( + session_impl& ses + , boost::weak_ptr t + , boost::shared_ptr s + , tcp::endpoint const& remote + , std::string const& url + , policy::peer* peerinfo) + : peer_connection(ses, t, s, remote, peerinfo) + , m_url(url) + , m_first_request(true) + { + INVARIANT_CHECK; + + // we always prefer downloading entire + // pieces from web seeds + prefer_whole_pieces(true); + // we want large blocks as well, so + // we can request more bytes at once + request_large_blocks(true); + // we only want left-over bandwidth + set_non_prioritized(true); + shared_ptr tor = t.lock(); + 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 + m_max_out_request_queue = ses.settings().urlseed_pipeline_size + * blocks_per_piece; + + // since this is a web seed, change the timeout + // according to the settings. + set_timeout(ses.settings().urlseed_timeout); +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << "*** web_peer_connection\n"; +#endif + + std::string protocol; + boost::tie(protocol, m_auth, m_host, m_port, m_path) + = parse_url_components(url); + + if (!m_auth.empty()) + m_auth = base64encode(m_auth); + + m_server_string = "URL seed @ "; + m_server_string += m_host; + } + + web_peer_connection::~web_peer_connection() + {} + + boost::optional + web_peer_connection::downloading_piece_progress() const + { + if (m_requests.empty()) + return boost::optional(); + + boost::shared_ptr t = associated_torrent().lock(); + assert(t); + + piece_block_progress ret; + + ret.piece_index = m_requests.front().piece; + if (!m_piece.empty()) + { + ret.bytes_downloaded = int(m_piece.size()); + } + else + { + if (!m_parser.header_finished()) + { + ret.bytes_downloaded = 0; + } + else + { + int receive_buffer_size = receive_buffer().left() - m_parser.body_start(); + ret.bytes_downloaded = receive_buffer_size % t->block_size(); + } + } + ret.block_index = (m_requests.front().start + ret.bytes_downloaded) / 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 web_peer_connection::on_connected() + { + boost::shared_ptr t = associated_torrent().lock(); + assert(t); + + // this is always a seed + incoming_bitfield(std::vector( + t->torrent_file().num_pieces(), true)); + // it is always possible to request pieces + incoming_unchoke(); + + reset_recv_buffer(t->block_size() + 1024); + } + + void web_peer_connection::write_request(peer_request const& r) + { + INVARIANT_CHECK; + + boost::shared_ptr t = associated_torrent().lock(); + assert(t); + + assert(t->valid_metadata()); + + bool single_file_request = false; + if (!m_path.empty() && m_path[m_path.size() - 1] != '/') + single_file_request = true; + + torrent_info const& info = t->torrent_file(); + + std::string request; + request.reserve(400); + + int size = r.length; + const int block_size = t->block_size(); + while (size > 0) + { + int request_size = std::min(block_size, size); + peer_request pr = {r.piece, r.start + r.length - size + , request_size}; + m_requests.push_back(pr); + size -= request_size; + } + + proxy_settings const& ps = m_ses.web_seed_proxy(); + bool using_proxy = ps.type == proxy_settings::http + || ps.type == proxy_settings::http_pw; + + if (single_file_request) + { + 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"; + request += "Host: "; + request += m_host; + if (m_first_request) + { + request += "\r\nUser-Agent: "; + request += m_ses.settings().user_agent; + } + if (!m_auth.empty()) + { + request += "\r\nAuthorization: Basic "; + request += m_auth; + } + if (ps.type == proxy_settings::http_pw) + { + request += "\r\nProxy-Authorization: Basic "; + request += base64encode(ps.username + ":" + ps.password); + } + if (using_proxy) + { + request += "\r\nProxy-Connection: keep-alive"; + } + request += "\r\nRange: bytes="; + request += boost::lexical_cast(r.piece + * info.piece_length() + r.start); + request += "-"; + request += boost::lexical_cast(r.piece + * info.piece_length() + r.start + r.length - 1); + if (m_first_request || using_proxy) + request += "\r\nConnection: keep-alive"; + request += "\r\n\r\n"; + m_first_request = false; + m_file_requests.push_back(0); + } + else + { + std::vector files = info.map_block(r.piece, r.start + , r.length); + + for (std::vector::iterator i = files.begin(); + i != files.end(); ++i) + { + file_slice const& f = *i; + + request += "GET "; + if (using_proxy) + { + request += m_url; + std::string path = info.file_at(f.file_index).path.string(); + request += escape_path(path.c_str(), path.length()); + } + else + { + std::string path = m_path; + path += info.file_at(f.file_index).path.string(); + request += escape_path(path.c_str(), path.length()); + } + request += " HTTP/1.1\r\n"; + request += "Host: "; + request += m_host; + if (m_first_request) + { + request += "\r\nUser-Agent: "; + request += m_ses.settings().user_agent; + } + if (!m_auth.empty()) + { + request += "\r\nAuthorization: Basic "; + request += m_auth; + } + if (ps.type == proxy_settings::http_pw) + { + request += "\r\nProxy-Authorization: Basic "; + request += base64encode(ps.username + ":" + ps.password); + } + if (using_proxy) + { + request += "\r\nProxy-Connection: keep-alive"; + } + request += "\r\nRange: bytes="; + request += boost::lexical_cast(f.offset); + request += "-"; + request += boost::lexical_cast(f.offset + f.size - 1); + if (m_first_request || using_proxy) + request += "\r\nConnection: keep-alive"; + request += "\r\n\r\n"; + m_first_request = false; + assert(f.file_index >= 0); + m_file_requests.push_back(f.file_index); + } + } + +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << request << "\n"; +#endif + + send_buffer(request.c_str(), request.c_str() + request.size()); + } + + // -------------------------- + // RECEIVE DATA + // -------------------------- + + namespace + { + bool range_contains(peer_request const& range, peer_request const& req) + { + return range.start <= req.start + && range.start + range.length >= req.start + req.length; + } + } + + // throws exception when the client should be disconnected + void web_peer_connection::on_receive(asio::error_code const& error + , std::size_t bytes_transferred) + { + INVARIANT_CHECK; + + if (error) return; + + boost::shared_ptr t = associated_torrent().lock(); + assert(t); + + incoming_piece_fragment(); + + for (;;) + { + buffer::const_interval recv_buffer = receive_buffer(); + + int payload; + int protocol; + bool header_finished = m_parser.header_finished(); + if (!header_finished) + { + boost::tie(payload, protocol) = m_parser.incoming(recv_buffer); + m_statistics.received_bytes(payload, protocol); + + assert(recv_buffer.left() == 0 || *recv_buffer.begin == 'H'); + + assert(recv_buffer.left() <= packet_size()); + + // this means the entire status line hasn't been received yet + if (m_parser.status_code() == -1) break; + + // if the status code is not one of the accepted ones, abort + if (m_parser.status_code() != 206 // partial content + && m_parser.status_code() != 200 // OK + && !(m_parser.status_code() >= 300 // redirect + && m_parser.status_code() < 400)) + { + // we should not try this server again. + t->remove_url_seed(m_url); + std::string error_msg = boost::lexical_cast(m_parser.status_code()) + + " " + m_parser.message(); + if (m_ses.m_alerts.should_post(alert::warning)) + { + session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); + m_ses.m_alerts.post_alert(url_seed_alert(t->get_handle(), url() + , error_msg)); + } + throw std::runtime_error(error_msg); + } + if (!m_parser.header_finished()) break; + + m_body_start = m_parser.body_start(); + m_received_body = 0; + } + else + { + m_statistics.received_bytes(bytes_transferred, 0); + } + + // we just completed reading the header + if (!header_finished) + { + if (m_parser.status_code() >= 300 && m_parser.status_code() < 400) + { + // this means we got a redirection request + // look for the location header + std::string location = m_parser.header("location"); + + if (location.empty()) + { + // we should not try this server again. + t->remove_url_seed(m_url); + throw std::runtime_error("got HTTP redirection status without location header"); + } + + bool single_file_request = false; + if (!m_path.empty() && m_path[m_path.size() - 1] != '/') + single_file_request = true; + + // add the redirected url and remove the current one + if (!single_file_request) + { + assert(!m_file_requests.empty()); + int file_index = m_file_requests.front(); + + torrent_info const& info = t->torrent_file(); + std::string path = info.file_at(file_index).path.string(); + path = escape_path(path.c_str(), path.length()); + size_t i = location.rfind(path); + if (i == std::string::npos) + { + t->remove_url_seed(m_url); + throw std::runtime_error("got invalid HTTP redirection location (\"" + location + "\") " + "expected it to end with: " + path); + } + location.resize(i); + } + t->add_url_seed(location); + t->remove_url_seed(m_url); + throw std::runtime_error("redirecting to " + location); + } + + std::string 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_body_start = m_parser.body_start(); + m_received_body = 0; + } + + recv_buffer.begin += m_body_start; + // we only received the header, no data + if (recv_buffer.left() == 0) break; + + size_type range_start; + size_type range_end; + if (m_parser.status_code() == 206) + { + std::stringstream range_str(m_parser.header("content-range")); + char dummy; + std::string bytes; + range_str >> bytes >> range_start >> dummy >> range_end; + if (!range_str) + { + // we should not try this server again. + t->remove_url_seed(m_url); + throw std::runtime_error("invalid range in HTTP response: " + range_str.str()); + } + // the http range is inclusive + range_end++; + } + else + { + range_start = 0; + range_end = m_parser.header("content-length"); + if (range_end == -1) + { + // we should not try this server again. + t->remove_url_seed(m_url); + throw std::runtime_error("no content-length in HTTP response"); + } + } + + torrent_info const& info = t->torrent_file(); + + if (m_requests.empty() || m_file_requests.empty()) + throw std::runtime_error("unexpected HTTP response"); + + int file_index = m_file_requests.front(); + peer_request in_range = info.map_file(file_index, range_start + , range_end - range_start); + + peer_request front_request = m_requests.front(); + + if (in_range.piece != front_request.piece + || in_range.start > front_request.start + int(m_piece.size())) + { + throw std::runtime_error("invalid range in HTTP response"); + } + + // skip the http header and the blocks we've already read. The + // http_body.begin is now in sync with the request at the front + // of the request queue + assert(in_range.start - int(m_piece.size()) <= front_request.start); + + // the http response body consists of 3 parts + // 1. the middle of a block or the ending of a block + // 2. a number of whole blocks + // 3. the start of a block + // in that order, these parts are parsed. + + bool range_overlaps_request = in_range.start + in_range.length + > front_request.start + int(m_piece.size()); + + // if the request is contained in the range (i.e. the entire request + // fits in the range) we should not start a partial piece, since we soon + // will receive enough to call incoming_piece() and pass the read buffer + // directly (in the next loop below). + if (range_overlaps_request && !range_contains(in_range, front_request)) + { + // the start of the next block to receive is stored + // in m_piece. We need to append the rest of that + // block from the http receive buffer and then + // (if it completed) call incoming_piece() with + // m_piece as buffer. + + int piece_size = int(m_piece.size()); + int copy_size = std::min(std::min(front_request.length - piece_size + , recv_buffer.left()), int(range_end - range_start - m_received_body)); + m_piece.resize(piece_size + copy_size); + assert(copy_size > 0); + std::memcpy(&m_piece[0] + piece_size, recv_buffer.begin, copy_size); + assert(int(m_piece.size()) <= front_request.length); + recv_buffer.begin += copy_size; + m_received_body += copy_size; + m_body_start += copy_size; + assert(m_received_body <= range_end - range_start); + assert(int(m_piece.size()) <= front_request.length); + if (int(m_piece.size()) == front_request.length) + { + // each call to incoming_piece() may result in us becoming + // a seed. If we become a seed, all seeds we're connected to + // will be disconnected, including this web seed. We need to + // check for the disconnect condition after the call. + + m_requests.pop_front(); + incoming_piece(front_request, &m_piece[0]); + if (associated_torrent().expired()) return; + cut_receive_buffer(m_body_start, t->block_size() + 1024); + m_body_start = 0; + recv_buffer = receive_buffer(); + assert(m_received_body <= range_end - range_start); + m_piece.clear(); + assert(m_piece.empty()); + } + } + + // report all received blocks to the bittorrent engine + while (!m_requests.empty() + && range_contains(in_range, m_requests.front()) + && recv_buffer.left() >= m_requests.front().length) + { + peer_request r = m_requests.front(); + m_requests.pop_front(); + assert(recv_buffer.left() >= r.length); + + incoming_piece(r, recv_buffer.begin); + if (associated_torrent().expired()) return; + m_received_body += r.length; + assert(receive_buffer().begin + m_body_start == recv_buffer.begin); + assert(m_received_body <= range_end - range_start); + cut_receive_buffer(r.length + m_body_start, t->block_size() + 1024); + m_body_start = 0; + recv_buffer = receive_buffer(); + } + + if (!m_requests.empty()) + { + range_overlaps_request = in_range.start + in_range.length + > m_requests.front().start + int(m_piece.size()); + + if (in_range.start + in_range.length < m_requests.front().start + m_requests.front().length + && (m_received_body + recv_buffer.left() >= range_end - range_start)) + { + int piece_size = int(m_piece.size()); + int copy_size = std::min(std::min(m_requests.front().length - piece_size + , recv_buffer.left()), int(range_end - range_start - m_received_body)); + assert(copy_size >= 0); + if (copy_size > 0) + { + m_piece.resize(piece_size + copy_size); + std::memcpy(&m_piece[0] + piece_size, recv_buffer.begin, copy_size); + recv_buffer.begin += copy_size; + m_received_body += copy_size; + m_body_start += copy_size; + } + assert(m_received_body == range_end - range_start); + } + } + + assert(m_received_body <= range_end - range_start); + if (m_received_body == range_end - range_start) + { + cut_receive_buffer(recv_buffer.begin - receive_buffer().begin + , t->block_size() + 1024); + recv_buffer = receive_buffer(); + m_file_requests.pop_front(); + m_parser.reset(); + m_body_start = 0; + m_received_body = 0; + continue; + } + break; + } + } + + void web_peer_connection::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_peer_interested()) p.flags |= peer_info::remote_interested; + if (has_peer_choked()) p.flags |= peer_info::remote_choked; + if (is_local()) p.flags |= peer_info::local_connection; + if (!is_connecting() && m_server_string.empty()) + p.flags |= peer_info::handshake; + if (is_connecting() && !is_queued()) p.flags |= peer_info::connecting; + if (is_queued()) p.flags |= peer_info::queued; + + p.client = m_server_string; + p.connection_type = peer_info::web_seed; + } + + bool web_peer_connection::in_handshake() const + { + return m_server_string.empty(); + } + + // throws exception when the client should be disconnected + void web_peer_connection::on_sent(asio::error_code const& error + , std::size_t bytes_transferred) + { + INVARIANT_CHECK; + + if (error) return; + m_statistics.sent_bytes(0, bytes_transferred); + } + + +#ifndef NDEBUG + void web_peer_connection::check_invariant() const + { +/* + assert(m_num_pieces == std::count( + m_have_piece.begin() + , m_have_piece.end() + , true)); +*/ } +#endif + +} + diff --git a/encryption/msgfmt.py b/encryption/msgfmt.py new file mode 100644 index 000000000..1406ac27e --- /dev/null +++ b/encryption/msgfmt.py @@ -0,0 +1,219 @@ +# -*- coding: iso-8859-1 -*- +# Written by Martin v. Lwis +# Plural forms support added by alexander smishlajev +""" +Generate binary message catalog from textual translation description. + +This program converts a textual Uniforum-style message catalog (.po file) into +a binary GNU catalog (.mo file). This is essentially the same function as the +GNU msgfmt program, however, it is a simpler implementation. + +Usage: msgfmt.py [OPTIONS] filename.po + +Options: + -o file + --output-file=file + Specify the output file to write to. If omitted, output will go to a + file named filename.mo (based off the input file name). + + -h + --help + Print this message and exit. + + -V + --version + Display version information and exit. +""" + +import sys +import os +import getopt +import struct +import array + +__version__ = "1.1" + +MESSAGES = {} + + +def usage (ecode, msg=''): + """ + Print usage and msg and exit with given code. + """ + print >> sys.stderr, __doc__ + if msg: + print >> sys.stderr, msg + sys.exit(ecode) + + +def add (msgid, transtr, fuzzy): + """ + Add a non-fuzzy translation to the dictionary. + """ + global MESSAGES + if not fuzzy and transtr and not transtr.startswith('\0'): + MESSAGES[msgid] = transtr + + +def generate (): + """ + Return the generated output. + """ + global MESSAGES + keys = MESSAGES.keys() + # the keys are sorted in the .mo file + keys.sort() + offsets = [] + ids = strs = '' + for _id in keys: + # For each string, we need size and file offset. Each string is NUL + # terminated; the NUL does not count into the size. + offsets.append((len(ids), len(_id), len(strs), len(MESSAGES[_id]))) + ids += _id + '\0' + strs += MESSAGES[_id] + '\0' + output = '' + # The header is 7 32-bit unsigned integers. We don't use hash tables, so + # the keys start right after the index tables. + # translated string. + keystart = 7*4+16*len(keys) + # and the values start after the keys + valuestart = keystart + len(ids) + koffsets = [] + voffsets = [] + # The string table first has the list of keys, then the list of values. + # Each entry has first the size of the string, then the file offset. + for o1, l1, o2, l2 in offsets: + koffsets += [l1, o1+keystart] + voffsets += [l2, o2+valuestart] + offsets = koffsets + voffsets + output = struct.pack("Iiiiiii", + 0x950412deL, # Magic + 0, # Version + len(keys), # # of entries + 7*4, # start of key index + 7*4+len(keys)*8, # start of value index + 0, 0) # size and offset of hash table + output += array.array("i", offsets).tostring() + output += ids + output += strs + return output + + +def make (filename, outfile): + ID = 1 + STR = 2 + global MESSAGES + MESSAGES = {} + + # Compute .mo name from .po name and arguments + if filename.endswith('.po'): + infile = filename + else: + infile = filename + '.po' + if outfile is None: + outfile = os.path.splitext(infile)[0] + '.mo' + + try: + lines = open(infile).readlines() + except IOError, msg: + print >> sys.stderr, msg + sys.exit(1) + + section = None + fuzzy = 0 + + # Parse the catalog + msgid = msgstr = '' + lno = 0 + for l in lines: + lno += 1 + # If we get a comment line after a msgstr, this is a new entry + if l[0] == '#' and section == STR: + add(msgid, msgstr, fuzzy) + section = None + fuzzy = 0 + # Record a fuzzy mark + if l[:2] == '#,' and (l.find('fuzzy') >= 0): + fuzzy = 1 + # Skip comments + if l[0] == '#': + continue + # Start of msgid_plural section, separate from singular form with \0 + if l.startswith('msgid_plural'): + msgid += '\0' + l = l[12:] + # Now we are in a msgid section, output previous section + elif l.startswith('msgid'): + if section == STR: + add(msgid, msgstr, fuzzy) + section = ID + l = l[5:] + msgid = msgstr = '' + # Now we are in a msgstr section + elif l.startswith('msgstr'): + section = STR + l = l[6:] + # Check for plural forms + if l.startswith('['): + # Separate plural forms with \0 + if not l.startswith('[0]'): + msgstr += '\0' + # Ignore the index - must come in sequence + l = l[l.index(']') + 1:] + # Skip empty lines + l = l.strip() + if not l: + continue + # XXX: Does this always follow Python escape semantics? + l = eval(l) + if section == ID: + msgid += l + elif section == STR: + msgstr += l + else: + print >> sys.stderr, 'Syntax error on %s:%d' % (infile, lno), \ + 'before:' + print >> sys.stderr, l + sys.exit(1) + # Add last entry + if section == STR: + add(msgid, msgstr, fuzzy) + + # Compute output + output = generate() + + try: + open(outfile,"wb").write(output) + except IOError,msg: + print >> sys.stderr, msg + + +def main (): + try: + opts, args = getopt.getopt(sys.argv[1:], 'hVo:', + ['help', 'version', 'output-file=']) + except getopt.error, msg: + usage(1, msg) + + outfile = None + # parse options + for opt, arg in opts: + if opt in ('-h', '--help'): + usage(0) + elif opt in ('-V', '--version'): + print >> sys.stderr, "msgfmt.py", __version__ + sys.exit(0) + elif opt in ('-o', '--output-file'): + outfile = arg + # do it + if not args: + print >> sys.stderr, 'No input file given' + print >> sys.stderr, "Try `msgfmt --help' for more information." + return + + for filename in args: + make(filename, outfile) + + +if __name__ == '__main__': + main() diff --git a/encryption/msgfmt.pyc b/encryption/msgfmt.pyc new file mode 100644 index 000000000..d0af62c04 Binary files /dev/null and b/encryption/msgfmt.pyc differ diff --git a/encryption/pixmaps/deluge-about.png b/encryption/pixmaps/deluge-about.png new file mode 100644 index 000000000..63aef4fbb Binary files /dev/null and b/encryption/pixmaps/deluge-about.png differ diff --git a/encryption/pixmaps/deluge128.png b/encryption/pixmaps/deluge128.png new file mode 100644 index 000000000..840b96704 Binary files /dev/null and b/encryption/pixmaps/deluge128.png differ diff --git a/encryption/pixmaps/deluge192.png b/encryption/pixmaps/deluge192.png new file mode 100644 index 000000000..9491409d6 Binary files /dev/null and b/encryption/pixmaps/deluge192.png differ diff --git a/encryption/pixmaps/deluge22.png b/encryption/pixmaps/deluge22.png new file mode 100644 index 000000000..29319b3f3 Binary files /dev/null and b/encryption/pixmaps/deluge22.png differ diff --git a/encryption/pixmaps/deluge256.png b/encryption/pixmaps/deluge256.png new file mode 100644 index 000000000..a57e7687b Binary files /dev/null and b/encryption/pixmaps/deluge256.png differ diff --git a/encryption/pixmaps/deluge32.png b/encryption/pixmaps/deluge32.png new file mode 100644 index 000000000..723372764 Binary files /dev/null and b/encryption/pixmaps/deluge32.png differ diff --git a/encryption/plugins/ExamplePlugin/example-plugin.png b/encryption/plugins/ExamplePlugin/example-plugin.png new file mode 100644 index 000000000..1f2b3b392 Binary files /dev/null and b/encryption/plugins/ExamplePlugin/example-plugin.png differ diff --git a/encryption/plugins/ExamplePlugin/example.glade b/encryption/plugins/ExamplePlugin/example.glade new file mode 100644 index 000000000..6a95761ec --- /dev/null +++ b/encryption/plugins/ExamplePlugin/example.glade @@ -0,0 +1,94 @@ + + + + + + 5 + Example Plugin + 313 + 167 + False + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_ENTER_NOTIFY_MASK + 2 + + + True + 2 + 2 + + + True + + + 1 + 2 + + + + + True + + + 1 + 2 + 1 + 2 + + + + + True + First Value + + + + + True + Second Value + + + 1 + 2 + + + + + 1 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_ENTER_NOTIFY_MASK + GTK_BUTTONBOX_END + + + True + gtk-cancel + True + + + + + True + gtk-ok + True + 1 + + + 1 + + + + + False + GTK_PACK_END + + + + + + diff --git a/encryption/plugins/ExamplePlugin/plugin.py b/encryption/plugins/ExamplePlugin/plugin.py new file mode 100644 index 000000000..2651f54c7 --- /dev/null +++ b/encryption/plugins/ExamplePlugin/plugin.py @@ -0,0 +1,101 @@ +# An example plugin for use with Deluge + +# This plugin is intended to be used with Deluge's default GTK interface +class plugin_Example: # The plugin's class + ## Your plugin's contructor should follow this format + ## path = A string containing the path to the plugin + ## deluge_core = The active instance of the Deluge Manager + ## deluge_interface = The active instance of the Deluge Interface + def __init__(self, path, deluge_core, deluge_interface): + # Save the path, interface, and core so they can be used later + self.path = path + self.core = deluge_core + self.interface = deluge_interface + # Classes must be imported as they are needed from within + # the plugin's functions + import common, gtk, gtk.glade, dgtk, pref + # Create an options file and try to load existing Values + self.config_file = common.CONFIG_DIR + "/example.conf" + self.config = pref.Preferences() + try: + self.config.load_from_file(self.config_file) + except IOError: + # File does not exist + pass + + # Extract the configuration dialog from the gladefile + self.glade = gtk.glade.XML(path + "/example.glade") + self.dialog = self.glade.get_widget("dialog") + self.dialog.set_icon_from_file(self.path + "/example-plugin.png") + # Access the interface's toolbar + self.toolbar = self.interface.toolbar + # Make a toolbar button + icon = gtk.Image() + icon.set_from_file(self.path + "/example-plugin.png") # Toolbar items should be 22x22 pixel images + self.button = gtk.ToolButton(icon_widget=icon, label="Example Plugin") + self.button.connect("clicked", self.clicked) # Connect the signal handler for the button + self.toolbar.add(self.button) # Add button to toolbar + self.button.show_all() # Show the button + + + ## unload is called when the plugin is removed or Deluge is shut down + def unload(self): + self.toolbar.remove(self.button) # Remove the button from the toolbar + self.config.save_to_file(self.config_file) + + ## update will be called every UPDATE_INTERVAL (usually about 1 second) + def update(self): + # As this plugin doesn't need to do anything every interval, this + # function will remain empty + pass + + ## This will be only called if your plugin is configurable + def configure(self): + entry1 = self.glade.get_widget("entry1") + entry2 = self.glade.get_widget("entry2") + try: + entry1.set_text(self.config.get("option1")) + entry2.set_text(self.config.get("option2")) + except KeyError: + entry1.set_text("") + entry2.set_text("") + self.dialog.show() + response = self.dialog.run() + self.dialog.hide() + if response: + self.config.set("option1", entry1.get_text()) + self.config.set("option2", entry2.get_text()) + + + + ## This will be called whenever self.button is clicked + def clicked(self, button): + # Build a dialog from scratch rather than from a glade file + import gtk + dialog = gtk.Dialog(title="Example Plugin", parent=self.interface.window, + buttons=(gtk.STOCK_OK, 0)) + dialog.set_icon_from_file(self.path + "/example-plugin.png") + try: + text = "This is a popup notification from Example Plugin\n" + \ + "Your value for option1 is %s\n"%self.config.get("option1") + \ + "and option2 is %s"%self.config.get("option2") + except KeyError: + text = "This is a popup notification from Example Plugin\n" + \ + "If you had set options by configuring this plugin,\n" + \ + "they would appear here" + label = gtk.Label(text) + dialog.vbox.pack_start(label) + + dialog.show_all() + dialog.run() + dialog.hide() + dialog.destroy() + + +register_plugin("Example Plugin", # The name of the plugin + plugin_Example, # The plugin's class + "Zach Tibbitts", # The author's Name + "0.5.0", # The plugin's version number + "An example plugin", # A description of the plugin + config=True, # If the plugin can be configured + ) diff --git a/encryption/plugins/HelloWorld/plugin.py b/encryption/plugins/HelloWorld/plugin.py new file mode 100644 index 000000000..f112d326a --- /dev/null +++ b/encryption/plugins/HelloWorld/plugin.py @@ -0,0 +1,19 @@ +# A simple plugin to display Hello, World + +class plugin_Hello: + def __init__(self, path, deluge_core, deluge_interface): + self.path = path + self.core = deluge_core + self.interface = deluge_interface + + def unload(self): + pass + + def update(self): + print "Hello, World!" + +register_plugin("Hello World", + plugin_Hello, + "Zach Tibbitts", + "1.0", + 'Displays "Hello, World"') diff --git a/encryption/plugins/NetworkGraph/plugin.py b/encryption/plugins/NetworkGraph/plugin.py new file mode 100644 index 000000000..29a3ba52b --- /dev/null +++ b/encryption/plugins/NetworkGraph/plugin.py @@ -0,0 +1,169 @@ +# netgraph plugin + +class plugin_NetGraph: + def __init__(self, path, deluge_core, deluge_interface): + import gtk + self.parent = deluge_interface + self.location = path + self.core = deluge_core + + self.image = gtk.Image() + + self.viewPort = gtk.Viewport() + self.viewPort.add(self.image) + + self.scrolledWindow = gtk.ScrolledWindow() + self.scrolledWindow.add(self.viewPort) + self.scrolledWindow.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) + + self.topWidget = self.scrolledWindow + + self.parentNotebook = self.parent.notebook +# print "Parent NOTEBOOK:", self.parentNotebook + self.parentNotebook.append_page(self.topWidget, gtk.Label("Graph")) +# print "My INDEX in parentNoteBook:", self.index + + self.image.show() + self.viewPort.show() + self.scrolledWindow.show() + + self.length = 60 + + self.width = -1 + self.height = -1 + + import pango + + self.pangoContext = self.parent.window.get_pango_context() + self.pangoLayout = pango.Layout(self.pangoContext) + + self.savedUpSpeeds = [] + self.savedDownSpeeds = [] + + self.bootupRuns = 3 # This ensures that we pass the resizing phase, with scrollbars, etc. + # So the first time it is viewed, we are all ready + + def unload(self): # Shutdown is called when the plugin is deactivated + numPages = self.parentNotebook.get_n_pages() + for page in range(numPages): + if self.parentNotebook.get_nth_page(page) == self.topWidget: + self.parentNotebook.remove_page(page) + break + + def configure(self): + pass + + def update(self): + import gtk + session_info = self.core.get_state() + self.savedUpSpeeds.insert(0, session_info['upload_rate']) + if len(self.savedUpSpeeds) > self.length: + self.savedUpSpeeds.pop() + self.savedDownSpeeds.insert(0, session_info['download_rate']) + if len(self.savedDownSpeeds) > self.length: + self.savedDownSpeeds.pop() + + if not self.parentNotebook.get_nth_page(self.parentNotebook.get_current_page()) == \ + self.topWidget and not self.bootupRuns > 0: + return + + self.bootupRuns = max(self.bootupRuns - 1, 0) + + extraWidth = self.scrolledWindow.get_vscrollbar().get_allocation().width * 1.5 + extraHeight = self.scrolledWindow.get_hscrollbar().get_allocation().height * 1.5 + allocation = self.scrolledWindow.get_allocation() + allocation.width = int(allocation.width) - extraWidth + allocation.height = int(allocation.height) - extraHeight + + # Don't try to allocate a size too small, or you might crash + if allocation.width < 2 or allocation.height < 2: + return + +# savedDownSpeeds = [1,2,3,2,1] +# savedUpSpeeds = [5,8,0,0,1,2] + +# allocation = self.image.get_allocation() +# allocation.width = 300 +# allocation.height = 200 + + if not allocation.width == self.width or not allocation.height == self.height: +# print "New Pixmap!" + self.width = allocation.width + self.height = allocation.height + + self.networkPixmap = gtk.gdk.Pixmap(None, self.width, self.height, 24) + self.image.set_from_pixmap(self.networkPixmap, None) + self.ctx = self.networkPixmap.cairo_create() + + self.networkPixmap.draw_rectangle(self.image.get_style().white_gc,True, 0, 0, self.width, self.height) + + maxSpeed = max(max(self.savedDownSpeeds),max(self.savedUpSpeeds)) + + if maxSpeed == 0: + return + + maxSpeed = maxSpeed*1.1 # Give some extra room on top + + self.drawSpeedPoly(self.savedDownSpeeds, (0.5,1, 0.5, 1.0), maxSpeed, True) + self.drawSpeedPoly(self.savedDownSpeeds, (0, 0.75,0, 1.0), maxSpeed, False) + + self.drawSpeedPoly(self.savedUpSpeeds, (0.33,0.33,1.0, 0.5), maxSpeed, True) + self.drawSpeedPoly(self.savedUpSpeeds, (0, 0, 1.0, 0.75), maxSpeed, False) + + meanUpSpeed = sum(self.savedUpSpeeds) /len(self.savedUpSpeeds) + meanDownSpeed = sum(self.savedDownSpeeds)/len(self.savedDownSpeeds) + shownSpeed = max(meanUpSpeed, meanDownSpeed) + + import common + + self.pangoLayout.set_text(common.frate(shownSpeed)) + self.networkPixmap.draw_layout(self.image.get_style().black_gc, + 4, + int(self.height - 1 - (self.height*shownSpeed/maxSpeed)), + self.pangoLayout) + + self.networkPixmap.draw_line(self.image.get_style().black_gc, + 0, int(self.height - (self.height*shownSpeed/maxSpeed)), + self.width, int(self.height - (self.height*shownSpeed/maxSpeed))) + + self.networkPixmap.draw_rectangle(self.image.get_style().black_gc,False, 0, 0, self.width-1, self.height-1) + + self.image.queue_draw() + + def tracePath(self, speeds, maxSpeed): + lineWidth = 4 + + self.ctx.set_line_width(lineWidth) + + self.ctx.move_to(self.width + lineWidth,self.height + lineWidth) + self.ctx.line_to(self.width + lineWidth,int(self.height-(self.height*speeds[0]/maxSpeed))) + + for i in range(len(speeds)): + self.ctx.line_to(int(self.width-1-((i*self.width)/(self.length-1))), + int(self.height-1-(self.height*speeds[i]/maxSpeed))) + + self.ctx.line_to(int(self.width-1-(((len(speeds)-1)*self.width)/(self.length-1))), + int(self.height)-1 + lineWidth) + + self.ctx.close_path() + + def drawSpeedPoly(self, speeds, color, maxSpeed, fill): + + self.tracePath(speeds, maxSpeed) + self.ctx.set_source_rgba(color[0],color[1],color[2], color[3]) + + if fill: + self.ctx.fill() + else: + self.ctx.stroke() + + +### Register plugin with Deluge + +register_plugin("Network Activity Graph", # The name of the plugin + plugin_NetGraph, # The plugin's class + "Alon Zakai, Zach Tibbitts", # Authors + "0.2", # The plugin's version number + "Network Activity Graph plugin\n\nWritten by Kripkenstein", # A description of the plugin + config=False, # If the plugin can be configured + ) diff --git a/encryption/plugins/NetworkHealth/plugin.py b/encryption/plugins/NetworkHealth/plugin.py new file mode 100644 index 000000000..a1094ebe9 --- /dev/null +++ b/encryption/plugins/NetworkHealth/plugin.py @@ -0,0 +1,39 @@ +class plugin_NetworkHealth: + def __init__(self, path, deluge_core, deluge_interface): + self.parent = deluge_interface # Using this, you can access the Deluge client + self.core = deluge_core + self.location = path + + self.counter = 30 + self.maxCount = self.counter + + def config(self): + pass + + def unload(self): + pass + + def update(self): + session_info = self.core.get_state() + if not session_info['has_incoming_connections'] and \ + session_info['num_peers'] > 1: + message = "[No incoming connections]" + self.counter = self.counter - 1 + if self.counter < 0: + # self.parent.addMessage("No incoming connections: you may be behind a firewall or router. Perhaps you need to forward the relevant ports.", "W") + self.counter = self.maxCount*2 + self.maxCount = self.counter + else: + message = "[Health: OK]" + self.counter = self.maxCount + + self.parent.statusbar_temp_msg = self.parent.statusbar_temp_msg + ' ' + message + +### Register plugin with Deluge +register_plugin("Network Health Monitor", # The name of the plugin + plugin_NetworkHealth, # The plugin's class + "Alon Zakai, Zach Tibbitts", # Authors + "0.2", # The plugin's version number + "Network Health Monitor plugin\n\nWritten by Kripkenstein", # A description of the plugin + config=False # If the plugin can be configured\ + ) diff --git a/encryption/plugins/TorrentCreator/plugin.py b/encryption/plugins/TorrentCreator/plugin.py new file mode 100644 index 000000000..9722055a4 --- /dev/null +++ b/encryption/plugins/TorrentCreator/plugin.py @@ -0,0 +1,95 @@ +# -*- coding: utf-8 -*- + +# Copyright (C) 2007 - regulate@gmail.com +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + +import gtk, gtk.glade + +class plugin_tcreator: + + + def __init__(self, path, deluge_core, deluge_interface): + import gtk, gtk.glade + self.path = path + self.interface = deluge_interface + self.core = deluge_core + self.dialog = None + self.glade = None + # get a toolbar to attach to + self.toolbar = self.interface.toolbar + # create a button + icon = gtk.image_new_from_stock(gtk.STOCK_EXECUTE, gtk.ICON_SIZE_MENU) + self.button = gtk.ToolButton(icon_widget=icon) + self.button.set_label('create torrent') + self.button.connect("clicked", self.clicked) + self.toolbar.add(self.button) + self.button.show_all() + + def unload(self): + self.toolbar.remove(self.button) + + def update(self): + pass + + def clicked(self, button): + import gtk, gtk.glade + ret = 0 + self.glade = gtk.glade.XML(self.path + "/tcreator.glade", domain='deluge') + self.dialog = self.glade.get_widget('tcreator') + self.dialog.set_title('Deluge Torrent Creator') + self.dialog.connect('destroy', self.destroy) + self.dialog.show_all() + ret = self.dialog.run() + + if ret == 1: + self.verify_settings() + self.destroy() + + if ret == 0: + self.destroy() + + def destroy(self, *args): + self.dialog.hide() + self.dialog = None + + def verify_settings(self): + import os, gtk, gtk.glade + self.dest = self.glade.get_widget("dest_file").get_text() + (dir, fn) = os.path.split(self.dest) + src_dir = self.glade.get_widget("src_dir").get_filename() + trackers = self.glade.get_widget("trackers").get_buffer() + (start, end) = trackers.get_bounds() + trackers = trackers.get_text(start, end) + comments = self.glade.get_widget("comments").get_buffer() + (start, end) = comments.get_bounds() + comments = comments.get_text(start, end) + size_table = {1:256, 2:512, 3:1024} + size = self.glade.get_widget("piece_size").get_active() + author = self.glade.get_widget("author").get_text() + + if 0 + + + + + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 5 + Deluge Torrent Creator + GTK_WIN_POS_CENTER_ON_PARENT + 400 + 500 + True + GDK_WINDOW_TYPE_HINT_DIALOG + False + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 2 + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + GTK_RESIZE_QUEUE + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 0 + GTK_SHADOW_NONE + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 12 + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 3 + 2 + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 10 + + + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + + + + + 1 + 2 + 1 + 2 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + Choose Source Directory: + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER + Select A Directory + + + 1 + 2 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + Choose Destination File: + + + 1 + 2 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + Piece Size: + + + 2 + 3 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 256KB +512KB +1024KB + + + 1 + 2 + 2 + 3 + + + + + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + Create Torrent + True + GTK_JUSTIFY_CENTER + + + label_item + + + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 0 + GTK_SHADOW_NONE + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 10 + 10 + 10 + 10 + + + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + + + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + Trackers: + True + + + label_item + + + + + 1 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 0 + GTK_SHADOW_NONE + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 10 + 10 + 10 + 10 + + + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + + + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + Comments: + + + label_item + + + + + 2 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + Author: + + + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 10 + + + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + + + + + 1 + + + + + 3 + + + + + + + + + + 1 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + GTK_BUTTONBOX_END + + + True + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + gtk-close + True + + + + + True + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + gtk-ok + True + 1 + + + 1 + + + + + False + GTK_PACK_END + + + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + + diff --git a/encryption/plugins/TorrentSearch/plugin.py b/encryption/plugins/TorrentSearch/plugin.py new file mode 100644 index 000000000..7a3be8a1c --- /dev/null +++ b/encryption/plugins/TorrentSearch/plugin.py @@ -0,0 +1,161 @@ + + +class plugin_Search: + def __init__(self, path, deluge_core, deluge_interface): + import common, gtk, gtk.glade, dgtk, pref + self.core = deluge_core + self.interface = deluge_interface + self.conf_file = common.CONFIG_DIR + "/search.conf" + if not os.path.isfile(self.conf_file): + f = open(self.conf_file, mode='w') + f.flush() + f.close() + glade = gtk.glade.XML(path + "/searchdlg.glade") + self.dlg = glade.get_widget("search_dialog") + self.dlg.set_icon_from_file(common.get_pixmap("deluge32.png")) + self.view = glade.get_widget("search_view") + model = gtk.ListStore(str, str) + self.view.set_model(model) + dgtk.add_text_column(self.view, "Name", 0) + dgtk.add_text_column(self.view, "Search String", 1) + self.field_name = glade.get_widget("field_name") + self.field_search = glade.get_widget("field_search") + self.button_add = glade.get_widget("button_addsearch") + self.button_del = glade.get_widget("button_delsearch") + dic = { "add_clicked" : self.add_clicked, + "del_clicked" : self.del_clicked, + "row_clicked" : self.row_clicked, + "text_changed" : self.text_changed } + glade.signal_autoconnect(dic) + self.view.get_selection().set_select_function(self.row_clicked) + ### Note: All other plugins should use self.interface.toolbar + ### when adding items to the toolbar + self.se = '' + self.toolbar = self.interface.wtree.get_widget("tb_right") + self.engines = pref.Preferences(self.conf_file) + self.search_entry = gtk.Entry() + self.search_item = gtk.ToolItem() + self.search_item.add(self.search_entry) + self.search_icon = gtk.Image() + self.search_icon.set_from_stock(gtk.STOCK_FIND, gtk.ICON_SIZE_MENU) + self.menu_button = gtk.MenuToolButton(self.search_icon, "Choose an Engine") + self.menu_button.set_is_important(True) + self.menu_button.connect("clicked", self.torrent_search) + self.menu = gtk.Menu() + self.manage_item = gtk.ImageMenuItem("Manage Engines") + self.image = gtk.Image() + self.image.set_from_stock(gtk.STOCK_PREFERENCES, gtk.ICON_SIZE_MENU) + self.manage_item.set_image(self.image) + self.manage_item.connect("activate", self.configure) + self.menu.add(self.manage_item) + self.menu_button.set_menu(self.menu) + self.toolbar.insert(self.search_item, -1) + self.toolbar.insert(self.menu_button, -1) + self.populate_search_menu() + self.toolbar.show_all() + self.search_item.show_all() + self.menu_button.show_all() + self.menu.show_all() + + def unload(self): + self.engines.save_to_file(self.conf_file) + self.toolbar.remove(self.search_item) + self.toolbar.remove(self.menu_button) + + def text_changed(self, args): + a = (self.field_name.get_text() != "") + b = (self.field_search.get_text() != "") + if(a and b): + self.button_add.set_sensitive(1) + else: + self.button_add.set_sensitive(0) + + def add_clicked(self, args): + self.view.get_model().append([self.field_name.get_text(), + self.field_search.get_text()]) + self.field_name.set_text("") + self.field_search.set_text("") + + def del_clicked(self, args): + (model, selection) = self.view.get_selection().get_selected() + model.remove(selection) + self.button_del.set_sensitive(0) + + def row_clicked(self, args): + self.button_del.set_sensitive(1) + return True + + def configure(self, widget=None): + import common, gtk, gtk.glade + self.dlg.show_all() + model = self.view.get_model() + model.clear() + for name in self.engines.keys(): + self.view.get_model().append( (name, self.engines.get(name)) ) + self.button_add.set_sensitive(0) + self.button_del.set_sensitive(0) + result = self.dlg.run() + self.dlg.hide_all() + if result == 1: + self.engines.clear() + the_iter = model.get_iter_first() + while the_iter is not None: + self.engines.set(model.get_value(the_iter, 0), model.get_value(the_iter, 1)) + the_iter = model.iter_next(the_iter) + self.engines.save_to_file(self.conf_file) + self.populate_search_menu() + + + + def update(self): + pass + + def torrent_search(self, widget=None): + import common + print "Searching with engine", self.se + url = self.engines.get(self.se) + entry = self.search_entry.get_text() + print 'URL =', url + print 'Entry =', entry + entry = entry.replace(' ', '+') + print 'URL =', url + print 'Entry =', entry + url = url.replace('${query}', entry) + print 'URL =', url + print 'Entry =', entry + common.open_url_in_browser(None, url) + + def populate_search_menu(self): + import gtk + self.menu_button.set_label("Choose an Engine") + for child in self.menu.get_children(): + self.menu.remove(child) + group = None + i = 0 + for engine in self.engines.keys(): + rmi = gtk.RadioMenuItem(None, engine) + rmi.eng_name = engine + rmi.connect("activate", self.select_search, rmi.eng_name) + if (group != None): + rmi.set_group(group) + else: + group = rmi + rmi.set_active(1) + self.menu.insert(rmi, i) + i = i + 1 + rmi.show() + self.menu.insert(self.manage_item, i) + self.menu.show() + + def select_search(self, menuitem, engine_string): + self.menu_button.set_label("Search " + engine_string) + self.se = engine_string + + +register_plugin("Torrent Search", + plugin_Search, + "Zach Tibbitts", + "0.5", + "A searchbar for torrent search engines", + config=True + ) diff --git a/encryption/plugins/TorrentSearch/searchdlg.glade b/encryption/plugins/TorrentSearch/searchdlg.glade new file mode 100644 index 000000000..e30703a9a --- /dev/null +++ b/encryption/plugins/TorrentSearch/searchdlg.glade @@ -0,0 +1,183 @@ + + + + + + 5 + Manage Search Plugins + False + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_ENTER_NOTIFY_MASK + 2 + + + True + 4 + 4 + + + True + gtk-add + True + + + + + 2 + 3 + 1 + 2 + GTK_FILL + GTK_FILL + + + + + True + GTK_POLICY_AUTOMATIC + + + True + + + + + 4 + + + + + True + + + + 1 + 2 + 1 + 2 + GTK_FILL + + + + + True + + + + 1 + 4 + 2 + 3 + GTK_FILL + + + + + True + Name: + + + 1 + 2 + GTK_FILL + GTK_FILL + + + + + True + URL: + + + 2 + 3 + GTK_FILL + GTK_FILL + + + + + True + gtk-remove + True + + + + 3 + 4 + 1 + 2 + GTK_FILL + GTK_FILL + + + + + True + True + + + True + Add a new search engine by entering a Name and a URL. For Name, enter the name of the search engine to be used. For URL, enter the url of the seach page. The user's search query will replace any instance of ${query} in the URL. +For example, a Google search would be: +Name: Google +URL: http://www.google.com/search?q=${query} + True + + + + + True + Help + + + label_item + + + + + 4 + 3 + 4 + + + + + 1 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_ENTER_NOTIFY_MASK + GTK_BUTTONBOX_END + + + True + gtk-cancel + True + + + + + True + gtk-ok + True + 1 + + + 1 + + + + + False + GTK_PACK_END + + + + + + diff --git a/encryption/po/POTFILES.in b/encryption/po/POTFILES.in new file mode 100644 index 000000000..41e8c396d --- /dev/null +++ b/encryption/po/POTFILES.in @@ -0,0 +1,9 @@ +glade/delugegtk.glade +glade/dgtkpopups.glade +glade/dgtkpref.glade +glade/preferences_dialog.glade +glade/torrent_menu.glade +src/common.py +src/core.py +src/interface.py +src/dialogs.py \ No newline at end of file diff --git a/encryption/po/bg.po b/encryption/po/bg.po new file mode 100644 index 000000000..ab0fd19e6 --- /dev/null +++ b/encryption/po/bg.po @@ -0,0 +1,465 @@ +# Bulgarian translation for deluge +# Copyright (c) 2006 Rosetta Contributors and Canonical Ltd 2006 +# This file is distributed under the same license as the deluge package. +# FIRST AUTHOR , 2006. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: deluge\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2007-03-06 16:19-0500\n" +"PO-Revision-Date: 2006-11-15 09:08+0000\n" +"Last-Translator: Rostislav Raykov \n" +"Language-Team: Bulgarian \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Rosetta-Export-Date: 2007-03-18 15:58+0000\n" + +#: glade/delugegtk.glade:93 +msgid "Estimated Time Remaining:" +msgstr "" + +#: glade/delugegtk.glade:131 +msgid "Peers:" +msgstr "" + +#: glade/delugegtk.glade:145 +msgid "Seeders:" +msgstr "" + +#: glade/delugegtk.glade:157 +msgid "Total Size:" +msgstr "" + +#: glade/delugegtk.glade:169 +msgid "Total Downloaded:" +msgstr "" + +#: glade/delugegtk.glade:181 +msgid "Downloaded this session:" +msgstr "" + +#: glade/delugegtk.glade:229 +msgid "Tracker:" +msgstr "" + +#: glade/delugegtk.glade:241 +msgid "Tracker Response:" +msgstr "" + +#: glade/delugegtk.glade:253 +msgid "Tracker Status:" +msgstr "" + +#: glade/delugegtk.glade:277 +msgid "Next Announce:" +msgstr "" + +#: glade/delugegtk.glade:303 +msgid "Pieces:" +msgstr "" + +#: glade/delugegtk.glade:317 +msgid "Total Uploaded:" +msgstr "" + +#: glade/delugegtk.glade:355 +msgid "Share Ratio:" +msgstr "" + +#: glade/delugegtk.glade:369 +msgid "Uploaded This Session:" +msgstr "" + +#: glade/delugegtk.glade:431 +msgid "Use compact storage allocation:" +msgstr "" + +#: glade/delugegtk.glade:455 +msgid "Download Rate:" +msgstr "" + +#: glade/delugegtk.glade:479 +msgid "Upload Rate:" +msgstr "" + +#: glade/delugegtk.glade:522 glade/delugegtk.glade:746 +msgid "Details" +msgstr "" + +#: glade/delugegtk.glade:549 glade/delugegtk.glade:790 +#: glade/dgtkpopups.glade:37 src/delugegtk.py:352 +msgid "Peers" +msgstr "" + +#: glade/delugegtk.glade:578 +msgid "Files" +msgstr "" + +#: glade/delugegtk.glade:613 +msgid "_File" +msgstr "_Файл" + +#: glade/delugegtk.glade:620 glade/delugegtk.glade:901 src/delugegtk.py:174 +msgid "Add Torrent" +msgstr "Добавяне на торент" + +#: glade/delugegtk.glade:636 +msgid "Add Torrent from URL" +msgstr "" + +#: glade/delugegtk.glade:644 glade/delugegtk.glade:914 +#: glade/dgtkpopups.glade:81 +msgid "Remove Torrent" +msgstr "Премахване на торент" + +#: glade/delugegtk.glade:658 +msgid "Clear Completed" +msgstr "" + +#: glade/delugegtk.glade:692 +msgid "_Edit" +msgstr "_Редактиране" + +#: glade/delugegtk.glade:709 glade/dgtkpopups.glade:221 +#: glade/dgtkpref.glade:697 src/delugegtk.py:177 +msgid "Plugins" +msgstr "" + +#: glade/delugegtk.glade:729 +msgid "_View" +msgstr "" + +#: glade/delugegtk.glade:737 +msgid "Toolbar" +msgstr "" + +#: glade/delugegtk.glade:755 +msgid "Columns" +msgstr "" + +#: glade/delugegtk.glade:763 glade/dgtkpopups.glade:10 src/delugegtk.py:349 +#: src/delugegtk.py:479 +msgid "Size" +msgstr "Размер" + +#: glade/delugegtk.glade:772 glade/dgtkpopups.glade:19 src/delugegtk.py:350 +msgid "Status" +msgstr "" + +#: glade/delugegtk.glade:781 glade/dgtkpopups.glade:28 src/delugegtk.py:351 +msgid "Seeders" +msgstr "" + +#: glade/delugegtk.glade:799 src/delugegtk.py:353 src/delugegtk.py:477 +#: src/delugegtk.py:692 src/delugegtk.py:704 +msgid "Download" +msgstr "Изтегляне" + +#: glade/delugegtk.glade:808 src/delugegtk.py:354 src/delugegtk.py:693 +#: src/delugegtk.py:705 +msgid "Upload" +msgstr "Качване" + +#: glade/delugegtk.glade:817 glade/dgtkpopups.glade:64 src/delugegtk.py:355 +msgid "Time Remaining" +msgstr "" + +#: glade/delugegtk.glade:826 glade/dgtkpopups.glade:73 +msgid "Share Ratio" +msgstr "Коефицент на споделяне" + +#: glade/delugegtk.glade:843 +msgid "_Help" +msgstr "_Помощ" + +#: glade/delugegtk.glade:927 +msgid "Clear Finished Torrents" +msgstr "Изчистване на приключилите торенти" + +#: glade/delugegtk.glade:949 +msgid "Start / Pause" +msgstr "" + +#: glade/delugegtk.glade:962 +msgid "Queue Torrent Up" +msgstr "Нагоре в опашката" + +#: glade/delugegtk.glade:975 +msgid "Queue Torrent Down" +msgstr "Надолу в опашката" + +#: glade/dgtkpopups.glade:46 +msgid "Download Speed" +msgstr "" + +#: glade/dgtkpopups.glade:55 +msgid "Upload Speed" +msgstr "" + +#: glade/dgtkpopups.glade:97 +msgid "" +"Are you sure you want to remove the selected torrent(s) from Deluge?" +msgstr "" + +#: glade/dgtkpopups.glade:107 +msgid "Delete downloaded files" +msgstr "" + +#: glade/dgtkpopups.glade:167 +msgid "Show/Hide" +msgstr "" + +#: glade/dgtkpopups.glade:175 +msgid "Add a Torrent..." +msgstr "" + +#: glade/dgtkpopups.glade:191 src/delugegtk.py:175 +msgid "Clear Finished" +msgstr "Изчистване на приключилите" + +#: glade/dgtkpopups.glade:212 glade/dgtkpref.glade:672 +msgid "gtk-preferences" +msgstr "" + +#: glade/dgtkpopups.glade:242 +msgid "gtk-quit" +msgstr "" + +#: glade/dgtkpref.glade:8 +msgid "Preferences Dialog" +msgstr "" + +#: glade/dgtkpref.glade:53 +msgid "Enable system tray icon" +msgstr "" + +#: glade/dgtkpref.glade:65 +msgid "Minimize to tray on close" +msgstr "" + +#: glade/dgtkpref.glade:80 +msgid "Options" +msgstr "" + +#: glade/dgtkpref.glade:115 +msgid "Save all downloads to:" +msgstr "Запазване на всичко в:" + +#: glade/dgtkpref.glade:127 +msgid "Ask me where to save each download" +msgstr "" + +#: glade/dgtkpref.glade:139 +msgid "Save Location" +msgstr "" + +#: glade/dgtkpref.glade:180 +msgid "" +"Stop seeding torrents when\n" +"their share ratio reaches:" +msgstr "" + +#: glade/dgtkpref.glade:190 +msgid "Seeding" +msgstr "" + +#: glade/dgtkpref.glade:209 +msgid "Use compact storage allocation" +msgstr "" + +#: glade/dgtkpref.glade:216 +msgid "Storage" +msgstr "" + +#: glade/dgtkpref.glade:246 +msgid "General" +msgstr "" + +#: glade/dgtkpref.glade:280 +msgid "Try from:" +msgstr "" + +#: glade/dgtkpref.glade:286 +msgid "to:" +msgstr "" + +#: glade/dgtkpref.glade:320 +msgid "Active port:" +msgstr "" + +#: glade/dgtkpref.glade:332 +msgid "0000" +msgstr "" + +#: glade/dgtkpref.glade:343 +msgid "Test Port" +msgstr "" + +#: glade/dgtkpref.glade:357 +msgid "TCP Port" +msgstr "" + +#: glade/dgtkpref.glade:380 +msgid "(-1 is unlimited)" +msgstr "" + +#: glade/dgtkpref.glade:390 glade/dgtkpref.glade:511 +msgid "KB/s" +msgstr "" + +#: glade/dgtkpref.glade:436 +msgid "Maximum Download Rate:" +msgstr "Максимална скорост на изтегляне:" + +#: glade/dgtkpref.glade:447 +msgid "Upload Slots" +msgstr "" + +#: glade/dgtkpref.glade:458 +msgid "Maximum Connections" +msgstr "" + +#: glade/dgtkpref.glade:485 +msgid "Maximum Upload Rate:" +msgstr "Максимална скорост на качване:" + +#: glade/dgtkpref.glade:548 +msgid "Bandwidth " +msgstr "" + +#: glade/dgtkpref.glade:576 +msgid "Network" +msgstr "" + +#: glade/dgtkpref.glade:598 +msgid "gtk-cancel" +msgstr "" + +#: glade/dgtkpref.glade:605 +msgid "gtk-ok" +msgstr "" + +#: glade/dgtkpref.glade:625 +msgid "Plugin Manager" +msgstr "" + +#: glade/dgtkpref.glade:718 +msgid "gtk-close" +msgstr "" + +#: src/dcommon.py:41 src/delugegtk.py:330 +msgid "Infinity" +msgstr "" + +#: src/dcommon.py:105 +msgid "Error: no webbrowser found" +msgstr "" + +#: src/deluge.py:332 +msgid "File was not found" +msgstr "" + +#: src/delugegtk.py:173 +msgid "Show / Hide Window" +msgstr "" + +#: src/delugegtk.py:250 +msgid "Plugin" +msgstr "" + +#: src/delugegtk.py:252 +msgid "Enabled" +msgstr "" + +#: src/delugegtk.py:340 +msgid "Unknown" +msgstr "Непознат" + +#: src/delugegtk.py:348 +msgid "Name" +msgstr "Име" + +#: src/delugegtk.py:356 +msgid "Ratio" +msgstr "" + +#: src/delugegtk.py:465 +msgid "IP Address" +msgstr "" + +#: src/delugegtk.py:466 +msgid "Client" +msgstr "Клиент" + +#: src/delugegtk.py:467 +msgid "Percent Complete" +msgstr "" + +#: src/delugegtk.py:468 +msgid "Download Rate" +msgstr "" + +#: src/delugegtk.py:469 +msgid "Upload Rate" +msgstr "" + +#: src/delugegtk.py:478 +msgid "Filename" +msgstr "" + +#: src/delugegtk.py:480 +msgid "Offset" +msgstr "" + +#: src/delugegtk.py:668 +msgid "" +"For some reason, the previous state could not be loaded, so a blank state " +"has been loaded for you." +msgstr "" + +#: src/delugegtk.py:670 +msgid "Would you like to attempt to reload the previous session's downloads?" +msgstr "" + +#: src/delugegtk.py:679 +msgid "Choose the download directory for" +msgstr "" + +#: src/delugegtk.py:692 src/delugegtk.py:704 +msgid "Connections" +msgstr "Връзки" + +#: src/delugegtk.py:703 +msgid "Deluge Bittorrent Client" +msgstr "Битторент клиент Deluge" + +#: src/delugegtk.py:891 +msgid "Add torrent from URL" +msgstr "" + +#: src/delugegtk.py:895 +msgid "Enter the URL of the .torrent to download" +msgstr "" + +#: src/delugegtk.py:959 +msgid "Warning - all downloaded files for this torrent will be deleted!" +msgstr "" + +#: src/dgtk.py:71 +msgid "Choose a .torrent file" +msgstr "" + +#: src/dgtk.py:76 +msgid "Torrent files" +msgstr "" + +#: src/dgtk.py:80 +msgid "All files" +msgstr "Всички файлове" + +#: src/dgtk.py:97 +msgid "Choose a download directory" +msgstr "" \ No newline at end of file diff --git a/encryption/po/ca.po b/encryption/po/ca.po new file mode 100644 index 000000000..600e0a408 --- /dev/null +++ b/encryption/po/ca.po @@ -0,0 +1,864 @@ +# Catalan translation for deluge +# Copyright (c) 2006 Rosetta Contributors and Canonical Ltd 2006 +# This file is distributed under the same license as the deluge package. +# Joan Duran , 2007. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: deluge\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2007-03-06 16:19-0500\n" +"PO-Revision-Date: 2007-03-16 23:58+0000\n" +"Last-Translator: Joan Duran \n" +"Language-Team: Catalan \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Rosetta-Export-Date: 2007-03-18 15:58+0000\n" + +#: glade/delugegtk.glade:93 +msgid "Estimated Time Remaining:" +msgstr "Temps restant estimat:" + +#: glade/delugegtk.glade:131 +msgid "Peers:" +msgstr "Clients:" + +#: glade/delugegtk.glade:145 +msgid "Seeders:" +msgstr "Llavors:" + +#: glade/delugegtk.glade:157 +msgid "Total Size:" +msgstr "Mida total:" + +#: glade/delugegtk.glade:169 +msgid "Total Downloaded:" +msgstr "Total baixat:" + +#: glade/delugegtk.glade:181 +msgid "Downloaded this session:" +msgstr "S'ha baixat en aquesta sessió:" + +#: glade/delugegtk.glade:229 +msgid "Tracker:" +msgstr "Rastrejador:" + +#: glade/delugegtk.glade:241 +msgid "Tracker Response:" +msgstr "Resposta del rastrejador:" + +#: glade/delugegtk.glade:253 +msgid "Tracker Status:" +msgstr "Estat del rastrejador:" + +#: glade/delugegtk.glade:277 +msgid "Next Announce:" +msgstr "Següent comunicat:" + +#: glade/delugegtk.glade:303 +msgid "Pieces:" +msgstr "Peces:" + +#: glade/delugegtk.glade:317 +msgid "Total Uploaded:" +msgstr "Total pujat:" + +#: glade/delugegtk.glade:355 +msgid "Share Ratio:" +msgstr "Relació a compartir:" + +#: glade/delugegtk.glade:369 +msgid "Uploaded This Session:" +msgstr "S'ha pujat en aquesta sessió:" + +#: glade/delugegtk.glade:431 +msgid "Use compact storage allocation:" +msgstr "Utilitza emmagatzemament compacte:" + +#: glade/delugegtk.glade:455 +msgid "Download Rate:" +msgstr "Velocitat de baixada:" + +#: glade/delugegtk.glade:479 +msgid "Upload Rate:" +msgstr "Velocitat de pujada:" + +#: glade/delugegtk.glade:522 glade/delugegtk.glade:746 +msgid "Details" +msgstr "Detalls" + +#: glade/delugegtk.glade:549 glade/delugegtk.glade:790 +#: glade/dgtkpopups.glade:37 src/delugegtk.py:352 +msgid "Peers" +msgstr "Clients" + +#: glade/delugegtk.glade:578 +msgid "Files" +msgstr "Fitxers" + +#: glade/delugegtk.glade:613 +msgid "_File" +msgstr "_Fitxer" + +#: glade/delugegtk.glade:620 glade/delugegtk.glade:901 src/delugegtk.py:174 +msgid "Add Torrent" +msgstr "Afegeix un torrent" + +#: glade/delugegtk.glade:636 +msgid "Add Torrent from URL" +msgstr "Afegeix un torrent des d'un URL" + +#: glade/delugegtk.glade:644 glade/delugegtk.glade:914 +#: glade/dgtkpopups.glade:81 +msgid "Remove Torrent" +msgstr "Suprimeix el torrent" + +#: glade/delugegtk.glade:658 +msgid "Clear Completed" +msgstr "Esborra completat" + +#: glade/delugegtk.glade:692 +msgid "_Edit" +msgstr "_Edita" + +#: glade/delugegtk.glade:709 glade/dgtkpopups.glade:221 +#: glade/dgtkpref.glade:697 src/delugegtk.py:177 +msgid "Plugins" +msgstr "Connectors" + +#: glade/delugegtk.glade:729 +msgid "_View" +msgstr "_Visualitza" + +#: glade/delugegtk.glade:737 +msgid "Toolbar" +msgstr "Barra d'eines" + +#: glade/delugegtk.glade:755 +msgid "Columns" +msgstr "Columnes" + +#: glade/delugegtk.glade:763 glade/dgtkpopups.glade:10 src/delugegtk.py:349 +#: src/delugegtk.py:479 +msgid "Size" +msgstr "Mida" + +#: glade/delugegtk.glade:772 glade/dgtkpopups.glade:19 src/delugegtk.py:350 +msgid "Status" +msgstr "Estat" + +#: glade/delugegtk.glade:781 glade/dgtkpopups.glade:28 src/delugegtk.py:351 +msgid "Seeders" +msgstr "Llavors" + +#: glade/delugegtk.glade:799 src/delugegtk.py:353 src/delugegtk.py:477 +#: src/delugegtk.py:692 src/delugegtk.py:704 +msgid "Download" +msgstr "Baixa" + +#: glade/delugegtk.glade:808 src/delugegtk.py:354 src/delugegtk.py:693 +#: src/delugegtk.py:705 +msgid "Upload" +msgstr "Puja" + +#: glade/delugegtk.glade:817 glade/dgtkpopups.glade:64 src/delugegtk.py:355 +msgid "Time Remaining" +msgstr "Temps restant" + +#: glade/delugegtk.glade:826 glade/dgtkpopups.glade:73 +msgid "Share Ratio" +msgstr "Relació a compartir" + +#: glade/delugegtk.glade:843 +msgid "_Help" +msgstr "_Ajuda" + +#: glade/delugegtk.glade:927 +msgid "Clear Finished Torrents" +msgstr "Esborra els torrents finalitzats" + +#: glade/delugegtk.glade:949 +msgid "Start / Pause" +msgstr "Inicia / Pausa" + +#: glade/delugegtk.glade:962 +msgid "Queue Torrent Up" +msgstr "Puja el torrent de la cua" + +#: glade/delugegtk.glade:975 +msgid "Queue Torrent Down" +msgstr "Baixa el torrent de la cua" + +#: glade/dgtkpopups.glade:46 +msgid "Download Speed" +msgstr "Velocitat de baixada" + +#: glade/dgtkpopups.glade:55 +msgid "Upload Speed" +msgstr "Velocitat de pujada" + +#: glade/dgtkpopups.glade:97 +msgid "" +"Are you sure you want to remove the selected torrent(s) from Deluge?" +msgstr "" +"Esteu segur que voleu suprimir els torrents seleccionats del Deluge?" + +#: glade/dgtkpopups.glade:107 +msgid "Delete downloaded files" +msgstr "Esborra els fitxers baixats" + +#: glade/dgtkpopups.glade:167 +msgid "Show/Hide" +msgstr "Mostra/Amaga" + +#: glade/dgtkpopups.glade:175 +msgid "Add a Torrent..." +msgstr "Afegiu un torrent..." + +#: glade/dgtkpopups.glade:191 src/delugegtk.py:175 +msgid "Clear Finished" +msgstr "Esborra els finalitzats" + +#: glade/dgtkpopups.glade:212 glade/dgtkpref.glade:672 +msgid "gtk-preferences" +msgstr "gtk-preferences" + +#: glade/dgtkpopups.glade:242 +msgid "gtk-quit" +msgstr "gtk-quit" + +#: glade/dgtkpref.glade:8 +msgid "Preferences Dialog" +msgstr "Diàleg de preferències" + +#: glade/dgtkpref.glade:53 +msgid "Enable system tray icon" +msgstr "Activa l'icona de la safata del sistema" + +#: glade/dgtkpref.glade:65 +msgid "Minimize to tray on close" +msgstr "Minimitza a la safata al tancar" + +#: glade/dgtkpref.glade:80 +msgid "Options" +msgstr "Opcions" + +#: glade/dgtkpref.glade:115 +msgid "Save all downloads to:" +msgstr "Desa totes les baixades a:" + +#: glade/dgtkpref.glade:127 +msgid "Ask me where to save each download" +msgstr "Pregunta on salvar cada baixada" + +#: glade/dgtkpref.glade:139 +msgid "Save Location" +msgstr "Destí on desar" + +#: glade/dgtkpref.glade:180 +msgid "" +"Stop seeding torrents when\n" +"their share ratio reaches:" +msgstr "" +"Atura els torrents llavors quan\n" +"la relació de compartició sigui:" + +#: glade/dgtkpref.glade:190 +msgid "Seeding" +msgstr "S'està compartint" + +#: glade/dgtkpref.glade:209 +msgid "Use compact storage allocation" +msgstr "Utilitza emmagatzemament compacte" + +#: glade/dgtkpref.glade:216 +msgid "Storage" +msgstr "Emmagatzemament" + +#: glade/dgtkpref.glade:246 +msgid "General" +msgstr "General" + +#: glade/dgtkpref.glade:280 +msgid "Try from:" +msgstr "Prova des de:" + +#: glade/dgtkpref.glade:286 +msgid "to:" +msgstr "a:" + +#: glade/dgtkpref.glade:320 +msgid "Active port:" +msgstr "Port actiu:" + +#: glade/dgtkpref.glade:332 +msgid "0000" +msgstr "000" + +#: glade/dgtkpref.glade:343 +msgid "Test Port" +msgstr "Comprova el port" + +#: glade/dgtkpref.glade:357 +msgid "TCP Port" +msgstr "Port TCP" + +#: glade/dgtkpref.glade:380 +msgid "(-1 is unlimited)" +msgstr "(-1 significa il·limitat)" + +#: glade/dgtkpref.glade:390 glade/dgtkpref.glade:511 +msgid "KB/s" +msgstr "KB/s" + +#: glade/dgtkpref.glade:436 +msgid "Maximum Download Rate:" +msgstr "Velocitat màxima de baixada:" + +#: glade/dgtkpref.glade:447 +msgid "Upload Slots" +msgstr "Ranures de pujada" + +#: glade/dgtkpref.glade:458 +msgid "Maximum Connections" +msgstr "Connexions màximes" + +#: glade/dgtkpref.glade:485 +msgid "Maximum Upload Rate:" +msgstr "Velocitat màxima de pujada:" + +#: glade/dgtkpref.glade:548 +msgid "Bandwidth " +msgstr "Ampla de banda " + +#: glade/dgtkpref.glade:576 +msgid "Network" +msgstr "Xarxa" + +#: glade/dgtkpref.glade:598 +msgid "gtk-cancel" +msgstr "gtk-cancel" + +#: glade/dgtkpref.glade:605 +msgid "gtk-ok" +msgstr "gtk-ok" + +#: glade/dgtkpref.glade:625 +msgid "Plugin Manager" +msgstr "Gestor de connectors" + +#: glade/dgtkpref.glade:718 +msgid "gtk-close" +msgstr "gtk-close" + +#: src/dcommon.py:41 src/delugegtk.py:330 +msgid "Infinity" +msgstr "Infinit" + +#: src/dcommon.py:105 +msgid "Error: no webbrowser found" +msgstr "S'ha produït un error, no es troba el navegador web" + +#: src/deluge.py:332 +msgid "File was not found" +msgstr "El fitxer no s'ha trobat" + +#: src/delugegtk.py:173 +msgid "Show / Hide Window" +msgstr "Mostra / Amaga la finestra" + +#: src/delugegtk.py:250 +msgid "Plugin" +msgstr "Connector" + +#: src/delugegtk.py:252 +msgid "Enabled" +msgstr "Activat" + +#: src/delugegtk.py:340 +msgid "Unknown" +msgstr "Desconegut" + +#: src/delugegtk.py:348 +msgid "Name" +msgstr "Nom" + +#: src/delugegtk.py:356 +msgid "Ratio" +msgstr "Relació" + +#: src/delugegtk.py:465 +msgid "IP Address" +msgstr "Adreça IP" + +#: src/delugegtk.py:466 +msgid "Client" +msgstr "Client" + +#: src/delugegtk.py:467 +msgid "Percent Complete" +msgstr "Percentatge acabat" + +#: src/delugegtk.py:468 +msgid "Download Rate" +msgstr "Velocitat de baixada" + +#: src/delugegtk.py:469 +msgid "Upload Rate" +msgstr "Velocitat de pujada" + +#: src/delugegtk.py:478 +msgid "Filename" +msgstr "Nom del fitxer" + +#: src/delugegtk.py:480 +msgid "Offset" +msgstr "Desplaçament" + +#: src/delugegtk.py:668 +msgid "" +"For some reason, the previous state could not be loaded, so a blank state " +"has been loaded for you." +msgstr "" +"Per algun motiu, l'estat anterior no s'ha pogut carregar, per tant s'ha " +"carregat un estat en blanc." + +#: src/delugegtk.py:670 +msgid "Would you like to attempt to reload the previous session's downloads?" +msgstr "Voleu provar de carregar les baixades de la sessió prèvia?" + +#: src/delugegtk.py:679 +msgid "Choose the download directory for" +msgstr "Seleccioneu el directori de baixades per" + +#: src/delugegtk.py:692 src/delugegtk.py:704 +msgid "Connections" +msgstr "Connexions" + +#: src/delugegtk.py:703 +msgid "Deluge Bittorrent Client" +msgstr "Deluge - Client bittorrent" + +#: src/delugegtk.py:891 +msgid "Add torrent from URL" +msgstr "Afegiu un torrent des d'un URL" + +#: src/delugegtk.py:895 +msgid "Enter the URL of the .torrent to download" +msgstr "Introduïu el URL des d'on baixar el .torrent" + +#: src/delugegtk.py:959 +msgid "Warning - all downloaded files for this torrent will be deleted!" +msgstr "Atenció - tots els fitxers baixats d'aquest torrent seran esborrats" + +#: src/dgtk.py:71 +msgid "Choose a .torrent file" +msgstr "Seleccioneu un fitxer .torrent" + +#: src/dgtk.py:76 +msgid "Torrent files" +msgstr "Fitxers torrent" + +#: src/dgtk.py:80 +msgid "All files" +msgstr "Tots els fitxers" + +#: src/dgtk.py:97 +msgid "Choose a download directory" +msgstr "Seleccioneu un directori per a les baixades" + +#~ msgid "_Manage Plugins" +#~ msgstr "_Configura els connectors" + +#~ msgid "Maximum number of Downloads:" +#~ msgstr "Número màxim de baixades:" + +#~ msgid "Progress" +#~ msgstr "Progrés" + +#~ msgid "D/L" +#~ msgstr "Velocitat de baixada" + +#~ msgid "U/L" +#~ msgstr "Velocitat de pujada" + +#~ msgid "ETA" +#~ msgstr "Temps estimat" + +#~ msgid "Share %" +#~ msgstr "% compartit" + +#~ msgid "path" +#~ msgstr "ruta" + +#~ msgid "size" +#~ msgstr "mida" + +#~ msgid "offset" +#~ msgstr "desplaçament" + +#~ msgid "download?" +#~ msgstr "baixa?" + +#~ msgid "IP" +#~ msgstr "IP" + +#~ msgid "Are you sure you want to quit Deluge?" +#~ msgstr "Esteu segur que voleu sortir del Deluge?" + +#~ msgid "You have " +#~ msgstr "Teniu " + +#~ msgid " active torrent" +#~ msgstr " torrents actius" + +#~ msgid "s" +#~ msgstr "s" + +#~ msgid ". Pressing 'Yes' will close Deluge and stop all torrent activity." +#~ msgstr "" +#~ ". Si premeu 'Sí' tancareu el Deluge i parareu tots els torrents actius." + +#~ msgid "Loaded torrents from previous session" +#~ msgstr "Torrents carregats d'una sessió prèvia" + +#~ msgid "No torrent is selected" +#~ msgstr "Cap torrent està seleccionat" + +#~ msgid "Connections: " +#~ msgstr "Connexions: " + +#~ msgid "Download Speed:" +#~ msgstr "Velocitat de baixada:" + +#~ msgid "Upload Speed:" +#~ msgstr "Velocitat de pujada:" + +#~ msgid "Enter the URL of the .torrent file to open:" +#~ msgstr "Introduïu la URL del fitxer .torrent a obrir:" + +#~ msgid "Select Torrent" +#~ msgstr "Seleccioneu el torrent" + +#~ msgid "infinity" +#~ msgstr "infinit" + +#~ msgid "1 second" +#~ msgstr "1 segon" + +#~ msgid "seconds" +#~ msgstr "segons" + +#~ msgid "1 minute" +#~ msgstr "1 minut" + +#~ msgid "minutes" +#~ msgstr "minuts" + +#~ msgid "1 hour" +#~ msgstr "1 hora" + +#~ msgid "hours" +#~ msgstr "hores" + +#~ msgid "1 day" +#~ msgstr "1 dia" + +#~ msgid "days" +#~ msgstr "dies" + +#~ msgid "1 week" +#~ msgstr "1 setmana" + +#~ msgid "weeks" +#~ msgstr "setmanes" + +#~ msgid "No plugin is selected" +#~ msgstr "No hi ha cap connector seleccionat" + +#~ msgid "You must activate a plugin before configuring it" +#~ msgstr "Heu d'activar un connector abans de configurar-lo" + +#~ msgid "no information is listed for this plugin" +#~ msgstr "no hi ha cap informació per a aquest connector" + +#~ msgid "The torrent file does not contain valid information." +#~ msgstr "El fitxer torrent no conté informació vàlida." + +#~ msgid "A filesystem error occurred when trying to open the torrent file." +#~ msgstr "" +#~ "Hi ha hagut un error en el sistema de fitxers quan s'intentava obrir un " +#~ "fitxer torrent." + +#~ msgid "The selected torrent is already present in the system." +#~ msgstr "El torrent seleccionat ja està present en el sistema." + +#~ msgid "N-A" +#~ msgstr "N-D" + +#~ msgid "Yes" +#~ msgstr "Sí" + +#~ msgid "No" +#~ msgstr "No" + +#~ msgid " is updating its tracker" +#~ msgstr " està actualitzant el rastrejador" + +#~ msgid "Paused" +#~ msgstr "En pausa" + +#~ msgid "Queued" +#~ msgstr "A la cua" + +#~ msgid "Waiting to check" +#~ msgstr "S'està esperant per comprovar" + +#~ msgid "Checking" +#~ msgstr "S'està comprovant" + +#~ msgid "Downloading (T?)" +#~ msgstr "S'està baixant (T?)" + +#~ msgid "Downloading Meta" +#~ msgstr "S'està baixant les metadades" + +#~ msgid "Downloading" +#~ msgstr "S'està baixant" + +#~ msgid "Finished" +#~ msgstr "Finalitzat" + +#~ msgid "Allocating" +#~ msgstr "S'està destinant" + +#~ msgid "N/A" +#~ msgstr "N/D" + +#~ msgid "INF" +#~ msgstr "INF" + +#~ msgid "Invalid request" +#~ msgstr "Sol·licitud invàlida" + +#~ msgid "File error" +#~ msgstr "Fitxer erroni" + +#~ msgid "Hash failed" +#~ msgstr "El resum ha fallat" + +#~ msgid "Peer banned" +#~ msgstr "Client prohibit" + +#~ msgid "Fastresume rejected" +#~ msgstr "Resum ràpid refusat" + +#~ msgid "Event" +#~ msgstr "Esdeveniment" + +#~ msgid "translator-credits" +#~ msgstr "Joan Duran , 2007" + +#~ msgid "Mark 'Do Not Download'" +#~ msgstr "Marca 'No baixar'" + +#~ msgid "Mark 'Download'" +#~ msgstr "Marca 'Baixar'" + +#~ msgid "Name:" +#~ msgstr "Nom:" + +#~ msgid "Percentage Done:" +#~ msgstr "Percentatge realitzat:" + +#~ msgid "Add a Torrent from File..." +#~ msgstr "Afegeix un torrent des d'un fitxer..." + +#~ msgid "Add a Torrent from URL..." +#~ msgstr "Afegeix un torrent des d'una URL..." + +#~ msgid "Deluge Support _Forums" +#~ msgstr "_Fòrums de suport del Deluge" + +#~ msgid "Force Pause/Resume Torrent" +#~ msgstr "Força la pausa/Reprén el torrent" + +#~ msgid "New Torrent..." +#~ msgstr "Nou torrent..." + +#~ msgid "Open the online support forums in your web browser" +#~ msgstr "Obre els fòrums de suport al teu navegador" + +#~ msgid "Queue Torrent to Bottom" +#~ msgstr "Encua el torrent" + +#~ msgid "Remove selected Torrent" +#~ msgstr "Suprimeix el torrent seleccionat" + +#~ msgid "Show/Hide the Details Pane" +#~ msgstr "Mostra/Amaga el panell d'informació" + +#~ msgid "Translate This Application..." +#~ msgstr "Traduïu aquesta aplicació..." + +#~ msgid "Update Tracker" +#~ msgstr "Actualitza el rastrejador" + +#~ msgid "_Update Tracker" +#~ msgstr "_Actualitza el rastrejador" + +#~ msgid "gtk-about" +#~ msgstr "gtk-about" + +#~ msgid "Advanced Network Options:" +#~ msgstr "Opcions avançades de xarxa:" + +#~ msgid "Bandwidth (KB/s):" +#~ msgstr "Amplada de banda (KB/s):" + +#~ msgid "Save Location:" +#~ msgstr "Destí a desar:" + +#~ msgid "Sharing:" +#~ msgstr "S'està compartint:" + +#~ msgid "Storage:" +#~ msgstr "Emmagatzemament:" + +#~ msgid "TCP Port:" +#~ msgstr "Port TCP:" + +#~ msgid "Automatically stop seeding torrents when their share ratio reaches:" +#~ msgstr "Automàticament atura els torrents llavors quan s'hagi compartit un:" + +#~ msgid "Bandwidth" +#~ msgstr "Ampla de banda" + +#~ msgid "Deluge Preferences" +#~ msgstr "Preferències del Deluge" + +#~ msgid "Downloads" +#~ msgstr "Baixades" + +#~ msgid "Mainline DHT" +#~ msgstr "Línia principal DHT" + +#~ msgid "Maximum number of Uploads:" +#~ msgstr "Número màxim de pujades:" + +#~ msgid "Maximum number of connections:" +#~ msgstr "Número màxim de connexions:" + +#~ msgid "Other" +#~ msgstr "Altres" + +#~ msgid "Select a save location for each download" +#~ msgstr "Seleccioneu un lloc de desat per a cada baixada" + +#~ msgid "To:" +#~ msgstr "A:" + +#~ msgid "Use compact storage allocation (save space)" +#~ msgstr "Utilitza emmagatzemament compacte (estalvia espai)" + +#~ msgid "" +#~ "Warning - any change to these settings will only come into effect after a " +#~ "restart!" +#~ msgstr "" +#~ "Atenció - qualsevol canvi en aquests paràmetres entrarà en funcionament " +#~ "després de reiniciar!" + +#~ msgid "Force Pause" +#~ msgstr "Fés una pausa" + +#~ msgid "Remove" +#~ msgstr "Suprimeix" + +#~ msgid "Resume" +#~ msgstr "Continua" + +#~ msgid "Add a Torrent" +#~ msgstr "Afegiu un torrent" + +#~ msgid "Update Trackers" +#~ msgstr "Actualitza els rastrejadors" + +#, no-c-format +#~ msgid "" +#~ "CPU % must persist for following time (seconds) before either of the above:" +#~ msgstr "" +#~ "% de CPU que ha de persistir durant el següent temps (segons) abans de " +#~ "qualsevol dels anteriors:" + +#~ msgid "CPU Monitor Configuration" +#~ msgstr "Configuració del monitor de la CPU" + +#, no-c-format +#~ msgid "Fatal CPU % (Deluge will be shut down):" +#~ msgstr "Error % CPU (Deluge es tancarà):" + +#, no-c-format +#~ msgid "Warning CPU % (notification only):" +#~ msgstr "Atenció % CPU (només notificació):" + +#~ msgid "" +#~ "Notifications of the following Severity Level and above will be displayed:" +#~ msgstr "" +#~ "Les notificacions amb el següent nivell de severitat o superior es " +#~ "visualitzaran:" + +#~ msgid "Popup Notifier Configuration" +#~ msgstr "Configuració del notificador emergent" + +#~ msgid "Popup Notification" +#~ msgstr "Notificació emergent" + +#~ msgid "SafeDeluge Configuration" +#~ msgstr "Configuració SafeDeluge" + +#~ msgid "Use custom blocklist URL:" +#~ msgstr "Utilitza la llista de blocat URL personalitzada:" + +#~ msgid "Use default blocklist URL" +#~ msgstr "Utilitza la llista de blocat URL per defecte" + +#~ msgid "Search string" +#~ msgstr "Cerca una cadena" + +#, no-c-format +#~ msgid "" +#~ "Add a new search engine by entering a Name and a URL. For Name, enter the " +#~ "name of the search engine to be used. For URL, enter the url of the seach " +#~ "page. The user's search query will be added to the end of the url, or " +#~ "replace any instances of %%query%% in the URL.\n" +#~ "For example, a Google search would be:\n" +#~ "Name: Google\n" +#~ "URL: http://www.google.com/search?hl=en" +#~ msgstr "" +#~ "Afegiu un nou motor de cerca introduint el nom i l'adreça. Per al nom, " +#~ "entreu el nom del motor de cerca que s'utilitzarà. Per l'adreça entreu " +#~ "l'adreça de la pàgina de cerca. La cerca del usuari s'ha d'afegir al final " +#~ "de la direcció, o substituir qualsevol part de la %%query%% en l'adreça.\n" +#~ "Per exemple, una cerca al Google seria:\n" +#~ "Nom: Google\n" +#~ "Adreça: http://www.google.com/search?hl=ca" + +#~ msgid "Help" +#~ msgstr "Ajuda" + +#~ msgid "Manage Search Plugins" +#~ msgstr "Configura els motors de cerca" + +#~ msgid "Name:" +#~ msgstr "Nom:" + +#~ msgid "URL:" +#~ msgstr "URL:" + +#~ msgid "gtk-add" +#~ msgstr "gtk-add" + +#~ msgid "gtk-remove" +#~ msgstr "gtk-remove" \ No newline at end of file diff --git a/encryption/po/cs.po b/encryption/po/cs.po new file mode 100644 index 000000000..a669a2a39 --- /dev/null +++ b/encryption/po/cs.po @@ -0,0 +1,465 @@ +# Czech translation for deluge +# Copyright (c) 2006 Rosetta Contributors and Canonical Ltd 2006 +# This file is distributed under the same license as the deluge package. +# FIRST AUTHOR , 2006. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: deluge\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2007-03-06 16:19-0500\n" +"PO-Revision-Date: 2006-11-13 06:05+0000\n" +"Last-Translator: dwori \n" +"Language-Team: Czech \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Rosetta-Export-Date: 2007-03-18 15:58+0000\n" + +#: glade/delugegtk.glade:93 +msgid "Estimated Time Remaining:" +msgstr "" + +#: glade/delugegtk.glade:131 +msgid "Peers:" +msgstr "" + +#: glade/delugegtk.glade:145 +msgid "Seeders:" +msgstr "" + +#: glade/delugegtk.glade:157 +msgid "Total Size:" +msgstr "" + +#: glade/delugegtk.glade:169 +msgid "Total Downloaded:" +msgstr "" + +#: glade/delugegtk.glade:181 +msgid "Downloaded this session:" +msgstr "" + +#: glade/delugegtk.glade:229 +msgid "Tracker:" +msgstr "" + +#: glade/delugegtk.glade:241 +msgid "Tracker Response:" +msgstr "" + +#: glade/delugegtk.glade:253 +msgid "Tracker Status:" +msgstr "" + +#: glade/delugegtk.glade:277 +msgid "Next Announce:" +msgstr "" + +#: glade/delugegtk.glade:303 +msgid "Pieces:" +msgstr "" + +#: glade/delugegtk.glade:317 +msgid "Total Uploaded:" +msgstr "" + +#: glade/delugegtk.glade:355 +msgid "Share Ratio:" +msgstr "" + +#: glade/delugegtk.glade:369 +msgid "Uploaded This Session:" +msgstr "" + +#: glade/delugegtk.glade:431 +msgid "Use compact storage allocation:" +msgstr "" + +#: glade/delugegtk.glade:455 +msgid "Download Rate:" +msgstr "" + +#: glade/delugegtk.glade:479 +msgid "Upload Rate:" +msgstr "" + +#: glade/delugegtk.glade:522 glade/delugegtk.glade:746 +msgid "Details" +msgstr "" + +#: glade/delugegtk.glade:549 glade/delugegtk.glade:790 +#: glade/dgtkpopups.glade:37 src/delugegtk.py:352 +msgid "Peers" +msgstr "Peeru" + +#: glade/delugegtk.glade:578 +msgid "Files" +msgstr "" + +#: glade/delugegtk.glade:613 +msgid "_File" +msgstr "_Soubor" + +#: glade/delugegtk.glade:620 glade/delugegtk.glade:901 src/delugegtk.py:174 +msgid "Add Torrent" +msgstr "Přidej Torrent" + +#: glade/delugegtk.glade:636 +msgid "Add Torrent from URL" +msgstr "" + +#: glade/delugegtk.glade:644 glade/delugegtk.glade:914 +#: glade/dgtkpopups.glade:81 +msgid "Remove Torrent" +msgstr "Obnov torren" + +#: glade/delugegtk.glade:658 +msgid "Clear Completed" +msgstr "" + +#: glade/delugegtk.glade:692 +msgid "_Edit" +msgstr "_Edituj" + +#: glade/delugegtk.glade:709 glade/dgtkpopups.glade:221 +#: glade/dgtkpref.glade:697 src/delugegtk.py:177 +msgid "Plugins" +msgstr "" + +#: glade/delugegtk.glade:729 +msgid "_View" +msgstr "" + +#: glade/delugegtk.glade:737 +msgid "Toolbar" +msgstr "" + +#: glade/delugegtk.glade:755 +msgid "Columns" +msgstr "" + +#: glade/delugegtk.glade:763 glade/dgtkpopups.glade:10 src/delugegtk.py:349 +#: src/delugegtk.py:479 +msgid "Size" +msgstr "Velikost" + +#: glade/delugegtk.glade:772 glade/dgtkpopups.glade:19 src/delugegtk.py:350 +msgid "Status" +msgstr "" + +#: glade/delugegtk.glade:781 glade/dgtkpopups.glade:28 src/delugegtk.py:351 +msgid "Seeders" +msgstr "Seedu" + +#: glade/delugegtk.glade:799 src/delugegtk.py:353 src/delugegtk.py:477 +#: src/delugegtk.py:692 src/delugegtk.py:704 +msgid "Download" +msgstr "Download" + +#: glade/delugegtk.glade:808 src/delugegtk.py:354 src/delugegtk.py:693 +#: src/delugegtk.py:705 +msgid "Upload" +msgstr "Upload" + +#: glade/delugegtk.glade:817 glade/dgtkpopups.glade:64 src/delugegtk.py:355 +msgid "Time Remaining" +msgstr "" + +#: glade/delugegtk.glade:826 glade/dgtkpopups.glade:73 +msgid "Share Ratio" +msgstr "Poměr sdílení" + +#: glade/delugegtk.glade:843 +msgid "_Help" +msgstr "_Nápověda" + +#: glade/delugegtk.glade:927 +msgid "Clear Finished Torrents" +msgstr "Vyčisti dokkončené torrenty" + +#: glade/delugegtk.glade:949 +msgid "Start / Pause" +msgstr "" + +#: glade/delugegtk.glade:962 +msgid "Queue Torrent Up" +msgstr "Posuň torrent dolů" + +#: glade/delugegtk.glade:975 +msgid "Queue Torrent Down" +msgstr "Posuň torrent nahoru" + +#: glade/dgtkpopups.glade:46 +msgid "Download Speed" +msgstr "" + +#: glade/dgtkpopups.glade:55 +msgid "Upload Speed" +msgstr "" + +#: glade/dgtkpopups.glade:97 +msgid "" +"Are you sure you want to remove the selected torrent(s) from Deluge?" +msgstr "" + +#: glade/dgtkpopups.glade:107 +msgid "Delete downloaded files" +msgstr "" + +#: glade/dgtkpopups.glade:167 +msgid "Show/Hide" +msgstr "" + +#: glade/dgtkpopups.glade:175 +msgid "Add a Torrent..." +msgstr "" + +#: glade/dgtkpopups.glade:191 src/delugegtk.py:175 +msgid "Clear Finished" +msgstr "Vyčisti dokončené" + +#: glade/dgtkpopups.glade:212 glade/dgtkpref.glade:672 +msgid "gtk-preferences" +msgstr "gtk-nastaveni" + +#: glade/dgtkpopups.glade:242 +msgid "gtk-quit" +msgstr "" + +#: glade/dgtkpref.glade:8 +msgid "Preferences Dialog" +msgstr "" + +#: glade/dgtkpref.glade:53 +msgid "Enable system tray icon" +msgstr "" + +#: glade/dgtkpref.glade:65 +msgid "Minimize to tray on close" +msgstr "" + +#: glade/dgtkpref.glade:80 +msgid "Options" +msgstr "" + +#: glade/dgtkpref.glade:115 +msgid "Save all downloads to:" +msgstr "Ulož všechny stahované soubory do:" + +#: glade/dgtkpref.glade:127 +msgid "Ask me where to save each download" +msgstr "" + +#: glade/dgtkpref.glade:139 +msgid "Save Location" +msgstr "" + +#: glade/dgtkpref.glade:180 +msgid "" +"Stop seeding torrents when\n" +"their share ratio reaches:" +msgstr "" + +#: glade/dgtkpref.glade:190 +msgid "Seeding" +msgstr "Seeduje" + +#: glade/dgtkpref.glade:209 +msgid "Use compact storage allocation" +msgstr "" + +#: glade/dgtkpref.glade:216 +msgid "Storage" +msgstr "" + +#: glade/dgtkpref.glade:246 +msgid "General" +msgstr "" + +#: glade/dgtkpref.glade:280 +msgid "Try from:" +msgstr "" + +#: glade/dgtkpref.glade:286 +msgid "to:" +msgstr "" + +#: glade/dgtkpref.glade:320 +msgid "Active port:" +msgstr "" + +#: glade/dgtkpref.glade:332 +msgid "0000" +msgstr "" + +#: glade/dgtkpref.glade:343 +msgid "Test Port" +msgstr "" + +#: glade/dgtkpref.glade:357 +msgid "TCP Port" +msgstr "" + +#: glade/dgtkpref.glade:380 +msgid "(-1 is unlimited)" +msgstr "" + +#: glade/dgtkpref.glade:390 glade/dgtkpref.glade:511 +msgid "KB/s" +msgstr "" + +#: glade/dgtkpref.glade:436 +msgid "Maximum Download Rate:" +msgstr "Maximální rychlost downloadu:" + +#: glade/dgtkpref.glade:447 +msgid "Upload Slots" +msgstr "" + +#: glade/dgtkpref.glade:458 +msgid "Maximum Connections" +msgstr "" + +#: glade/dgtkpref.glade:485 +msgid "Maximum Upload Rate:" +msgstr "Maximální rychlost uploadu:" + +#: glade/dgtkpref.glade:548 +msgid "Bandwidth " +msgstr "" + +#: glade/dgtkpref.glade:576 +msgid "Network" +msgstr "" + +#: glade/dgtkpref.glade:598 +msgid "gtk-cancel" +msgstr "gtk-zrus" + +#: glade/dgtkpref.glade:605 +msgid "gtk-ok" +msgstr "gtk-ok" + +#: glade/dgtkpref.glade:625 +msgid "Plugin Manager" +msgstr "" + +#: glade/dgtkpref.glade:718 +msgid "gtk-close" +msgstr "" + +#: src/dcommon.py:41 src/delugegtk.py:330 +msgid "Infinity" +msgstr "" + +#: src/dcommon.py:105 +msgid "Error: no webbrowser found" +msgstr "" + +#: src/deluge.py:332 +msgid "File was not found" +msgstr "" + +#: src/delugegtk.py:173 +msgid "Show / Hide Window" +msgstr "" + +#: src/delugegtk.py:250 +msgid "Plugin" +msgstr "" + +#: src/delugegtk.py:252 +msgid "Enabled" +msgstr "" + +#: src/delugegtk.py:340 +msgid "Unknown" +msgstr "Neznámý" + +#: src/delugegtk.py:348 +msgid "Name" +msgstr "Název" + +#: src/delugegtk.py:356 +msgid "Ratio" +msgstr "" + +#: src/delugegtk.py:465 +msgid "IP Address" +msgstr "" + +#: src/delugegtk.py:466 +msgid "Client" +msgstr "Klient" + +#: src/delugegtk.py:467 +msgid "Percent Complete" +msgstr "" + +#: src/delugegtk.py:468 +msgid "Download Rate" +msgstr "" + +#: src/delugegtk.py:469 +msgid "Upload Rate" +msgstr "" + +#: src/delugegtk.py:478 +msgid "Filename" +msgstr "" + +#: src/delugegtk.py:480 +msgid "Offset" +msgstr "" + +#: src/delugegtk.py:668 +msgid "" +"For some reason, the previous state could not be loaded, so a blank state " +"has been loaded for you." +msgstr "" + +#: src/delugegtk.py:670 +msgid "Would you like to attempt to reload the previous session's downloads?" +msgstr "" + +#: src/delugegtk.py:679 +msgid "Choose the download directory for" +msgstr "" + +#: src/delugegtk.py:692 src/delugegtk.py:704 +msgid "Connections" +msgstr "Spojení" + +#: src/delugegtk.py:703 +msgid "Deluge Bittorrent Client" +msgstr "Deluge Bittorent Client" + +#: src/delugegtk.py:891 +msgid "Add torrent from URL" +msgstr "" + +#: src/delugegtk.py:895 +msgid "Enter the URL of the .torrent to download" +msgstr "" + +#: src/delugegtk.py:959 +msgid "Warning - all downloaded files for this torrent will be deleted!" +msgstr "" + +#: src/dgtk.py:71 +msgid "Choose a .torrent file" +msgstr "" + +#: src/dgtk.py:76 +msgid "Torrent files" +msgstr "" + +#: src/dgtk.py:80 +msgid "All files" +msgstr "Všechny soubory" + +#: src/dgtk.py:97 +msgid "Choose a download directory" +msgstr "" \ No newline at end of file diff --git a/encryption/po/da.po b/encryption/po/da.po new file mode 100644 index 000000000..ca512816a --- /dev/null +++ b/encryption/po/da.po @@ -0,0 +1,471 @@ +# Danish translation for deluge +# Copyright (c) 2006 Rosetta Contributors and Canonical Ltd 2006 +# This file is distributed under the same license as the deluge package. +# FIRST AUTHOR , 2006. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: deluge\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2007-03-06 16:19-0500\n" +"PO-Revision-Date: 2007-03-15 14:57+0000\n" +"Last-Translator: dylansmrjones \n" +"Language-Team: Danish \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Rosetta-Export-Date: 2007-03-18 15:58+0000\n" + +#: glade/delugegtk.glade:93 +msgid "Estimated Time Remaining:" +msgstr "Anslået resterende tid:" + +#: glade/delugegtk.glade:131 +msgid "Peers:" +msgstr "Lurere:" + +#: glade/delugegtk.glade:145 +msgid "Seeders:" +msgstr "Såmaskiner:" + +#: glade/delugegtk.glade:157 +msgid "Total Size:" +msgstr "Samlet størrelse:" + +#: glade/delugegtk.glade:169 +msgid "Total Downloaded:" +msgstr "Modtaget i alt:" + +#: glade/delugegtk.glade:181 +msgid "Downloaded this session:" +msgstr "Modtaget i denne session:" + +#: glade/delugegtk.glade:229 +msgid "Tracker:" +msgstr "Tracker:" + +#: glade/delugegtk.glade:241 +msgid "Tracker Response:" +msgstr "Trackersvar:" + +#: glade/delugegtk.glade:253 +msgid "Tracker Status:" +msgstr "Trackerstatus:" + +#: glade/delugegtk.glade:277 +msgid "Next Announce:" +msgstr "Næste annoncering:" + +#: glade/delugegtk.glade:303 +msgid "Pieces:" +msgstr "Stykker:" + +#: glade/delugegtk.glade:317 +msgid "Total Uploaded:" +msgstr "Sendt i alt:" + +#: glade/delugegtk.glade:355 +msgid "Share Ratio:" +msgstr "Delingsforhold:" + +#: glade/delugegtk.glade:369 +msgid "Uploaded This Session:" +msgstr "Sendt i denne session:" + +#: glade/delugegtk.glade:431 +msgid "Use compact storage allocation:" +msgstr "Brug kompakt pladstildeling:" + +#: glade/delugegtk.glade:455 +msgid "Download Rate:" +msgstr "Modtagehastighed" + +#: glade/delugegtk.glade:479 +msgid "Upload Rate:" +msgstr "Sendehastighed" + +#: glade/delugegtk.glade:522 glade/delugegtk.glade:746 +msgid "Details" +msgstr "Detaljer" + +#: glade/delugegtk.glade:549 glade/delugegtk.glade:790 +#: glade/dgtkpopups.glade:37 src/delugegtk.py:352 +msgid "Peers" +msgstr "Lurere" + +#: glade/delugegtk.glade:578 +msgid "Files" +msgstr "Filer" + +#: glade/delugegtk.glade:613 +msgid "_File" +msgstr "_Fil" + +#: glade/delugegtk.glade:620 glade/delugegtk.glade:901 src/delugegtk.py:174 +msgid "Add Torrent" +msgstr "Tilføj torrent" + +#: glade/delugegtk.glade:636 +msgid "Add Torrent from URL" +msgstr "Tilføj torrent fra link" + +#: glade/delugegtk.glade:644 glade/delugegtk.glade:914 +#: glade/dgtkpopups.glade:81 +msgid "Remove Torrent" +msgstr "Fjern torrent" + +#: glade/delugegtk.glade:658 +msgid "Clear Completed" +msgstr "Fjern fuldførte torrents" + +#: glade/delugegtk.glade:692 +msgid "_Edit" +msgstr "_Redigér" + +#: glade/delugegtk.glade:709 glade/dgtkpopups.glade:221 +#: glade/dgtkpref.glade:697 src/delugegtk.py:177 +msgid "Plugins" +msgstr "Moduler" + +#: glade/delugegtk.glade:729 +msgid "_View" +msgstr "_Vis" + +#: glade/delugegtk.glade:737 +msgid "Toolbar" +msgstr "Værktøjslinie" + +#: glade/delugegtk.glade:755 +msgid "Columns" +msgstr "Kolonner" + +#: glade/delugegtk.glade:763 glade/dgtkpopups.glade:10 src/delugegtk.py:349 +#: src/delugegtk.py:479 +msgid "Size" +msgstr "Størrelse" + +#: glade/delugegtk.glade:772 glade/dgtkpopups.glade:19 src/delugegtk.py:350 +msgid "Status" +msgstr "Status" + +#: glade/delugegtk.glade:781 glade/dgtkpopups.glade:28 src/delugegtk.py:351 +msgid "Seeders" +msgstr "Såmaskiner" + +#: glade/delugegtk.glade:799 src/delugegtk.py:353 src/delugegtk.py:477 +#: src/delugegtk.py:692 src/delugegtk.py:704 +msgid "Download" +msgstr "Modtag" + +#: glade/delugegtk.glade:808 src/delugegtk.py:354 src/delugegtk.py:693 +#: src/delugegtk.py:705 +msgid "Upload" +msgstr "Send" + +#: glade/delugegtk.glade:817 glade/dgtkpopups.glade:64 src/delugegtk.py:355 +msgid "Time Remaining" +msgstr "Resterende tid" + +#: glade/delugegtk.glade:826 glade/dgtkpopups.glade:73 +msgid "Share Ratio" +msgstr "Delingsforhold" + +#: glade/delugegtk.glade:843 +msgid "_Help" +msgstr "_Hjælp" + +#: glade/delugegtk.glade:927 +msgid "Clear Finished Torrents" +msgstr "Fjern færdige torrents" + +#: glade/delugegtk.glade:949 +msgid "Start / Pause" +msgstr "Begynd / pause" + +#: glade/delugegtk.glade:962 +msgid "Queue Torrent Up" +msgstr "Skub torrent frem i køen" + +#: glade/delugegtk.glade:975 +msgid "Queue Torrent Down" +msgstr "Skub torrent tilbage i køen" + +#: glade/dgtkpopups.glade:46 +msgid "Download Speed" +msgstr "Modtagehastighed" + +#: glade/dgtkpopups.glade:55 +msgid "Upload Speed" +msgstr "Sendehastighed" + +#: glade/dgtkpopups.glade:97 +msgid "" +"Are you sure you want to remove the selected torrent(s) from Deluge?" +msgstr "" +"Er du sikker på, du ønsker at fjerne de(n) markerede torrentfil(er) fra " +"Deluge?" + +#: glade/dgtkpopups.glade:107 +msgid "Delete downloaded files" +msgstr "Slet modtagne filer" + +#: glade/dgtkpopups.glade:167 +msgid "Show/Hide" +msgstr "Vis/skjul" + +#: glade/dgtkpopups.glade:175 +msgid "Add a Torrent..." +msgstr "Tilføj torrent..." + +#: glade/dgtkpopups.glade:191 src/delugegtk.py:175 +msgid "Clear Finished" +msgstr "Fjern færdige torrents" + +#: glade/dgtkpopups.glade:212 glade/dgtkpref.glade:672 +msgid "gtk-preferences" +msgstr "gtk-preferences" + +#: glade/dgtkpopups.glade:242 +msgid "gtk-quit" +msgstr "gtk-quit" + +#: glade/dgtkpref.glade:8 +msgid "Preferences Dialog" +msgstr "Opsætning" + +#: glade/dgtkpref.glade:53 +msgid "Enable system tray icon" +msgstr "Aktivér statusikon" + +#: glade/dgtkpref.glade:65 +msgid "Minimize to tray on close" +msgstr "Minimer til statusområdet ved lukning" + +#: glade/dgtkpref.glade:80 +msgid "Options" +msgstr "Indstillinger" + +#: glade/dgtkpref.glade:115 +msgid "Save all downloads to:" +msgstr "Gem alle modtagne filer i:" + +#: glade/dgtkpref.glade:127 +msgid "Ask me where to save each download" +msgstr "Spørg mig, hvor modtagne filer skal gemmes" + +#: glade/dgtkpref.glade:139 +msgid "Save Location" +msgstr "Gemmeplacering" + +#: glade/dgtkpref.glade:180 +msgid "" +"Stop seeding torrents when\n" +"their share ratio reaches:" +msgstr "" +"Stop med at seede torrents,\n" +"når deres delingsforhold er på:" + +#: glade/dgtkpref.glade:190 +msgid "Seeding" +msgstr "Sår" + +#: glade/dgtkpref.glade:209 +msgid "Use compact storage allocation" +msgstr "Brug kompakt pladstildeling" + +#: glade/dgtkpref.glade:216 +msgid "Storage" +msgstr "Lager" + +#: glade/dgtkpref.glade:246 +msgid "General" +msgstr "Generelt" + +#: glade/dgtkpref.glade:280 +msgid "Try from:" +msgstr "Forsøg fra:" + +#: glade/dgtkpref.glade:286 +msgid "to:" +msgstr "til:" + +#: glade/dgtkpref.glade:320 +msgid "Active port:" +msgstr "Aktiv port:" + +#: glade/dgtkpref.glade:332 +msgid "0000" +msgstr "0000" + +#: glade/dgtkpref.glade:343 +msgid "Test Port" +msgstr "Testport" + +#: glade/dgtkpref.glade:357 +msgid "TCP Port" +msgstr "TCP-port" + +#: glade/dgtkpref.glade:380 +msgid "(-1 is unlimited)" +msgstr "(-1 er ubegrænset)" + +#: glade/dgtkpref.glade:390 glade/dgtkpref.glade:511 +msgid "KB/s" +msgstr "KB/sek." + +#: glade/dgtkpref.glade:436 +msgid "Maximum Download Rate:" +msgstr "Maksimal modtagehastighed:" + +#: glade/dgtkpref.glade:447 +msgid "Upload Slots" +msgstr "Sendepladser" + +#: glade/dgtkpref.glade:458 +msgid "Maximum Connections" +msgstr "Største antal forbindelser" + +#: glade/dgtkpref.glade:485 +msgid "Maximum Upload Rate:" +msgstr "Maksimal sendehastighed:" + +#: glade/dgtkpref.glade:548 +msgid "Bandwidth " +msgstr "Båndbredde " + +#: glade/dgtkpref.glade:576 +msgid "Network" +msgstr "Netværk" + +#: glade/dgtkpref.glade:598 +msgid "gtk-cancel" +msgstr "gtk-cancel" + +#: glade/dgtkpref.glade:605 +msgid "gtk-ok" +msgstr "gtk-ok" + +#: glade/dgtkpref.glade:625 +msgid "Plugin Manager" +msgstr "Modulhåndtering" + +#: glade/dgtkpref.glade:718 +msgid "gtk-close" +msgstr "gtk-close" + +#: src/dcommon.py:41 src/delugegtk.py:330 +msgid "Infinity" +msgstr "Uendelig" + +#: src/dcommon.py:105 +msgid "Error: no webbrowser found" +msgstr "Fejl: ingen webbrowser fundet" + +#: src/deluge.py:332 +msgid "File was not found" +msgstr "Filen blev ikke fundet" + +#: src/delugegtk.py:173 +msgid "Show / Hide Window" +msgstr "Vis/skjul vindue" + +#: src/delugegtk.py:250 +msgid "Plugin" +msgstr "Modul" + +#: src/delugegtk.py:252 +msgid "Enabled" +msgstr "Slået til" + +#: src/delugegtk.py:340 +msgid "Unknown" +msgstr "Ukendt" + +#: src/delugegtk.py:348 +msgid "Name" +msgstr "Navn" + +#: src/delugegtk.py:356 +msgid "Ratio" +msgstr "Forhold" + +#: src/delugegtk.py:465 +msgid "IP Address" +msgstr "IP-adresse" + +#: src/delugegtk.py:466 +msgid "Client" +msgstr "Klient" + +#: src/delugegtk.py:467 +msgid "Percent Complete" +msgstr "Procent færdig" + +#: src/delugegtk.py:468 +msgid "Download Rate" +msgstr "Modtagehastighed" + +#: src/delugegtk.py:469 +msgid "Upload Rate" +msgstr "Sendehastighed" + +#: src/delugegtk.py:478 +msgid "Filename" +msgstr "Filnavn" + +#: src/delugegtk.py:480 +msgid "Offset" +msgstr "Justering" + +#: src/delugegtk.py:668 +msgid "" +"For some reason, the previous state could not be loaded, so a blank state " +"has been loaded for you." +msgstr "" +"Af ukendte årsager kunne den tidligere tilstand ikke indlæses. Derfor er en " +"tom tilstand blevet indlæst." + +#: src/delugegtk.py:670 +msgid "Would you like to attempt to reload the previous session's downloads?" +msgstr "Vil du forsøge at indlæse den tidligere sessions overførsler?" + +#: src/delugegtk.py:679 +msgid "Choose the download directory for" +msgstr "Vælg gem-mappen for" + +#: src/delugegtk.py:692 src/delugegtk.py:704 +msgid "Connections" +msgstr "Forbindelser" + +#: src/delugegtk.py:703 +msgid "Deluge Bittorrent Client" +msgstr "Deluge Bittorrentklient" + +#: src/delugegtk.py:891 +msgid "Add torrent from URL" +msgstr "Tilføj torrent fra link" + +#: src/delugegtk.py:895 +msgid "Enter the URL of the .torrent to download" +msgstr "Indtast linket til .torrent-filen for at hente" + +#: src/delugegtk.py:959 +msgid "Warning - all downloaded files for this torrent will be deleted!" +msgstr "Advarsel! Alle modtagne filer for denne torrent vil blive slettet!" + +#: src/dgtk.py:71 +msgid "Choose a .torrent file" +msgstr "Vælg .torrentfil" + +#: src/dgtk.py:76 +msgid "Torrent files" +msgstr "Torrent-filer" + +#: src/dgtk.py:80 +msgid "All files" +msgstr "Alle filer" + +#: src/dgtk.py:97 +msgid "Choose a download directory" +msgstr "Vælg en mappe til modtagne filer" \ No newline at end of file diff --git a/encryption/po/de.po b/encryption/po/de.po new file mode 100644 index 000000000..f57cb14c5 --- /dev/null +++ b/encryption/po/de.po @@ -0,0 +1,482 @@ +# German translation for deluge +# Copyright (c) 2006 Rosetta Contributors and Canonical Ltd 2006 +# This file is distributed under the same license as the deluge package. +# FIRST AUTHOR , 2006. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: deluge\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2007-03-06 16:19-0500\n" +"PO-Revision-Date: 2007-03-17 15:59+0000\n" +"Last-Translator: jhasse \n" +"Language-Team: German \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Rosetta-Export-Date: 2007-03-18 15:58+0000\n" + +#: glade/delugegtk.glade:93 +msgid "Estimated Time Remaining:" +msgstr "Vorraussichtliche Restzeit:" + +#: glade/delugegtk.glade:131 +msgid "Peers:" +msgstr "Peers:" + +#: glade/delugegtk.glade:145 +msgid "Seeders:" +msgstr "Seeders" + +#: glade/delugegtk.glade:157 +msgid "Total Size:" +msgstr "Gesamtgrösse:" + +#: glade/delugegtk.glade:169 +msgid "Total Downloaded:" +msgstr "Gesamt heruntergeladen:" + +#: glade/delugegtk.glade:181 +msgid "Downloaded this session:" +msgstr "In dieser Sitzung heruntergeladen:" + +#: glade/delugegtk.glade:229 +msgid "Tracker:" +msgstr "Tracker:" + +#: glade/delugegtk.glade:241 +msgid "Tracker Response:" +msgstr "Tracker Antwort:" + +#: glade/delugegtk.glade:253 +msgid "Tracker Status:" +msgstr "Tracker Status:" + +#: glade/delugegtk.glade:277 +msgid "Next Announce:" +msgstr "Nächste Ankündigung:" + +#: glade/delugegtk.glade:303 +msgid "Pieces:" +msgstr "Stücke:" + +#: glade/delugegtk.glade:317 +msgid "Total Uploaded:" +msgstr "Gesamt hochgeladen:" + +#: glade/delugegtk.glade:355 +msgid "Share Ratio:" +msgstr "Tauschverhältnis" + +#: glade/delugegtk.glade:369 +msgid "Uploaded This Session:" +msgstr "In dieser Sitzung hochgeladen:" + +#: glade/delugegtk.glade:431 +msgid "Use compact storage allocation:" +msgstr "Benutze kompakte Speicherzuweisung:" + +#: glade/delugegtk.glade:455 +msgid "Download Rate:" +msgstr "Download-Rate:" + +#: glade/delugegtk.glade:479 +msgid "Upload Rate:" +msgstr "Upload-Rate:" + +#: glade/delugegtk.glade:522 glade/delugegtk.glade:746 +msgid "Details" +msgstr "Details" + +#: glade/delugegtk.glade:549 glade/delugegtk.glade:790 +#: glade/dgtkpopups.glade:37 src/delugegtk.py:352 +msgid "Peers" +msgstr "Peers" + +#: glade/delugegtk.glade:578 +msgid "Files" +msgstr "Dateien" + +#: glade/delugegtk.glade:613 +msgid "_File" +msgstr "_Datei" + +#: glade/delugegtk.glade:620 glade/delugegtk.glade:901 src/delugegtk.py:174 +msgid "Add Torrent" +msgstr "Torrent hinzufügen" + +#: glade/delugegtk.glade:636 +msgid "Add Torrent from URL" +msgstr "Torrent aus URL hinzufügen" + +#: glade/delugegtk.glade:644 glade/delugegtk.glade:914 +#: glade/dgtkpopups.glade:81 +msgid "Remove Torrent" +msgstr "Torrent entfernen" + +#: glade/delugegtk.glade:658 +msgid "Clear Completed" +msgstr "Abgeschlossene Downloads entfernen" + +#: glade/delugegtk.glade:692 +msgid "_Edit" +msgstr "_Bearbeiten" + +#: glade/delugegtk.glade:709 glade/dgtkpopups.glade:221 +#: glade/dgtkpref.glade:697 src/delugegtk.py:177 +msgid "Plugins" +msgstr "Erweiterungen" + +#: glade/delugegtk.glade:729 +msgid "_View" +msgstr "_Ansicht" + +#: glade/delugegtk.glade:737 +msgid "Toolbar" +msgstr "Werkzeugleiste" + +#: glade/delugegtk.glade:755 +msgid "Columns" +msgstr "Spalten" + +#: glade/delugegtk.glade:763 glade/dgtkpopups.glade:10 src/delugegtk.py:349 +#: src/delugegtk.py:479 +msgid "Size" +msgstr "Größe" + +#: glade/delugegtk.glade:772 glade/dgtkpopups.glade:19 src/delugegtk.py:350 +msgid "Status" +msgstr "Status" + +#: glade/delugegtk.glade:781 glade/dgtkpopups.glade:28 src/delugegtk.py:351 +msgid "Seeders" +msgstr "Seeders" + +#: glade/delugegtk.glade:799 src/delugegtk.py:353 src/delugegtk.py:477 +#: src/delugegtk.py:692 src/delugegtk.py:704 +msgid "Download" +msgstr "Download" + +#: glade/delugegtk.glade:808 src/delugegtk.py:354 src/delugegtk.py:693 +#: src/delugegtk.py:705 +msgid "Upload" +msgstr "Upload" + +#: glade/delugegtk.glade:817 glade/dgtkpopups.glade:64 src/delugegtk.py:355 +msgid "Time Remaining" +msgstr "Verbleibende Zeit" + +#: glade/delugegtk.glade:826 glade/dgtkpopups.glade:73 +msgid "Share Ratio" +msgstr "D/U Verhältnis" + +#: glade/delugegtk.glade:843 +msgid "_Help" +msgstr "_Hilfe" + +#: glade/delugegtk.glade:927 +msgid "Clear Finished Torrents" +msgstr "Fertige Torrents entfernen" + +#: glade/delugegtk.glade:949 +msgid "Start / Pause" +msgstr "Start / Pause" + +#: glade/delugegtk.glade:962 +msgid "Queue Torrent Up" +msgstr "In Warteschlange hoch" + +#: glade/delugegtk.glade:975 +msgid "Queue Torrent Down" +msgstr "In Warteschlange hinunter" + +#: glade/dgtkpopups.glade:46 +msgid "Download Speed" +msgstr "Downloadgeschwindigkeit" + +#: glade/dgtkpopups.glade:55 +msgid "Upload Speed" +msgstr "Uploadgeschwindigkeit" + +#: glade/dgtkpopups.glade:97 +#, fuzzy +msgid "" +"Are you sure you want to remove the selected torrent(s) from Deluge?" +msgstr "" +"Sind Sie sicher, dass der/die ausgewählte/n Torrent/s aus Deluge gelöscht " +"werden sollen?" + +#: glade/dgtkpopups.glade:107 +msgid "Delete downloaded files" +msgstr "Heruntergeladene Dateien löschen" + +#: glade/dgtkpopups.glade:167 +msgid "Show/Hide" +msgstr "Ein-/Ausblenden" + +#: glade/dgtkpopups.glade:175 +msgid "Add a Torrent..." +msgstr "Torrent hinzufügen..." + +#: glade/dgtkpopups.glade:191 src/delugegtk.py:175 +msgid "Clear Finished" +msgstr "Fertige Downloads entfernen" + +#: glade/dgtkpopups.glade:212 glade/dgtkpref.glade:672 +#, fuzzy +msgid "gtk-preferences" +msgstr "GTK Einstellungen" + +#: glade/dgtkpopups.glade:242 +#, fuzzy +msgid "gtk-quit" +msgstr "GTK Beenden" + +#: glade/dgtkpref.glade:8 +msgid "Preferences Dialog" +msgstr "Einstellungsdialog" + +#: glade/dgtkpref.glade:53 +msgid "Enable system tray icon" +msgstr "Systemtrayicon aktivieren" + +#: glade/dgtkpref.glade:65 +msgid "Minimize to tray on close" +msgstr "Beim Schließen in den Tray minimieren" + +#: glade/dgtkpref.glade:80 +msgid "Options" +msgstr "Optionen" + +#: glade/dgtkpref.glade:115 +msgid "Save all downloads to:" +msgstr "Speichere alle Downloads nach:" + +#: glade/dgtkpref.glade:127 +msgid "Ask me where to save each download" +msgstr "Nach Ziel-Verzeichnis fragen" + +#: glade/dgtkpref.glade:139 +msgid "Save Location" +msgstr "Speicherort" + +#: glade/dgtkpref.glade:180 +msgid "" +"Stop seeding torrents when\n" +"their share ratio reaches:" +msgstr "" +"Das Seeden stoppen, wenn das \n" +"Down-/Uploadverhältnis folgenden Wert erreicht:" + +#: glade/dgtkpref.glade:190 +msgid "Seeding" +msgstr "Seeding" + +#: glade/dgtkpref.glade:209 +msgid "Use compact storage allocation" +msgstr "Kompakte Speicherzuweisung benutzen" + +#: glade/dgtkpref.glade:216 +msgid "Storage" +msgstr "Speicherort" + +#: glade/dgtkpref.glade:246 +msgid "General" +msgstr "Allgemeine Einstellungen" + +#: glade/dgtkpref.glade:280 +msgid "Try from:" +msgstr "Versuche von:" + +#: glade/dgtkpref.glade:286 +msgid "to:" +msgstr "nach:" + +#: glade/dgtkpref.glade:320 +msgid "Active port:" +msgstr "Aktiver Port:" + +#: glade/dgtkpref.glade:332 +msgid "0000" +msgstr "0000" + +#: glade/dgtkpref.glade:343 +msgid "Test Port" +msgstr "Test-Port" + +#: glade/dgtkpref.glade:357 +msgid "TCP Port" +msgstr "TCP-Port" + +#: glade/dgtkpref.glade:380 +msgid "(-1 is unlimited)" +msgstr "(-1 ist unbeschränkt)" + +#: glade/dgtkpref.glade:390 glade/dgtkpref.glade:511 +msgid "KB/s" +msgstr "KB/s" + +#: glade/dgtkpref.glade:436 +msgid "Maximum Download Rate:" +msgstr "Maximale Download-Geschwindigkeit:" + +#: glade/dgtkpref.glade:447 +msgid "Upload Slots" +msgstr "Uploadzuteilungen" + +#: glade/dgtkpref.glade:458 +#, fuzzy +msgid "Maximum Connections" +msgstr "Maximale Verbindungen" + +#: glade/dgtkpref.glade:485 +msgid "Maximum Upload Rate:" +msgstr "Maximale Hochlade-Geschwindigkeit:" + +#: glade/dgtkpref.glade:548 +msgid "Bandwidth " +msgstr "Bandbreite " + +#: glade/dgtkpref.glade:576 +msgid "Network" +msgstr "Netzwerk" + +#: glade/dgtkpref.glade:598 +#, fuzzy +msgid "gtk-cancel" +msgstr "gtk-abbruch" + +#: glade/dgtkpref.glade:605 +#, fuzzy +msgid "gtk-ok" +msgstr "gtk-ok" + +#: glade/dgtkpref.glade:625 +msgid "Plugin Manager" +msgstr "Erweiterungsverwaltung" + +#: glade/dgtkpref.glade:718 +msgid "gtk-close" +msgstr "gtk-schließen" + +#: src/dcommon.py:41 src/delugegtk.py:330 +msgid "Infinity" +msgstr "Unendlich" + +#: src/dcommon.py:105 +msgid "Error: no webbrowser found" +msgstr "Fehler: Kein Webbrowser gefunden" + +#: src/deluge.py:332 +#, fuzzy +msgid "File was not found" +msgstr "Datei wurde nicht gefunden" + +#: src/delugegtk.py:173 +msgid "Show / Hide Window" +msgstr "Fenster anzeigen / verbergen" + +#: src/delugegtk.py:250 +msgid "Plugin" +msgstr "Erweiterung" + +#: src/delugegtk.py:252 +msgid "Enabled" +msgstr "Aktiviert" + +#: src/delugegtk.py:340 +msgid "Unknown" +msgstr "Unbekannt" + +#: src/delugegtk.py:348 +msgid "Name" +msgstr "Name" + +#: src/delugegtk.py:356 +msgid "Ratio" +msgstr "Verhältnis" + +#: src/delugegtk.py:465 +msgid "IP Address" +msgstr "IP-Adresse" + +#: src/delugegtk.py:466 +msgid "Client" +msgstr "Client" + +#: src/delugegtk.py:467 +msgid "Percent Complete" +msgstr "Prozent komplett" + +#: src/delugegtk.py:468 +msgid "Download Rate" +msgstr "Download-Rate" + +#: src/delugegtk.py:469 +msgid "Upload Rate" +msgstr "Upload-Rate" + +#: src/delugegtk.py:478 +msgid "Filename" +msgstr "Dateiname" + +#: src/delugegtk.py:480 +msgid "Offset" +msgstr "Absatz" + +#: src/delugegtk.py:668 +#, fuzzy +msgid "" +"For some reason, the previous state could not be loaded, so a blank state " +"has been loaded for you." +msgstr "" +"Aus irgendeinem Grund konnte der vorherige Zustand nicht geladen werden, " +"deswegen wurde ein unbelegter Zustand geladen." + +#: src/delugegtk.py:670 +#, fuzzy +msgid "Would you like to attempt to reload the previous session's downloads?" +msgstr "Möchten Sie versuchen die Downlaods der letzten Sitzung zu laden?" + +#: src/delugegtk.py:679 +#, fuzzy +msgid "Choose the download directory for" +msgstr "Wählen Sie das Downloadverzeichnis für" + +#: src/delugegtk.py:692 src/delugegtk.py:704 +msgid "Connections" +msgstr "Verbindungen" + +#: src/delugegtk.py:703 +msgid "Deluge Bittorrent Client" +msgstr "Deluge Bittorrent Client" + +#: src/delugegtk.py:891 +msgid "Add torrent from URL" +msgstr "Torrent aus URL hinzufügen" + +#: src/delugegtk.py:895 +msgid "Enter the URL of the .torrent to download" +msgstr "Geben Sie die URL des zu herunterladenden Torrents ein" + +#: src/delugegtk.py:959 +msgid "Warning - all downloaded files for this torrent will be deleted!" +msgstr "" +"Warnung - Alle heruntergeladene Dateien für diesen Torrent werden gelöscht!" + +#: src/dgtk.py:71 +msgid "Choose a .torrent file" +msgstr "Wählen Sie eine .torrent Datei" + +#: src/dgtk.py:76 +msgid "Torrent files" +msgstr "Torrent-Dateien" + +#: src/dgtk.py:80 +msgid "All files" +msgstr "Alle Dateien" + +#: src/dgtk.py:97 +msgid "Choose a download directory" +msgstr "Downloadverzeichnis wählen" \ No newline at end of file diff --git a/encryption/po/deluge.pot b/encryption/po/deluge.pot new file mode 100644 index 000000000..8d5be2a48 --- /dev/null +++ b/encryption/po/deluge.pot @@ -0,0 +1,645 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2007-05-17 20:08-0500\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: 8bit\n" + +#: glade/delugegtk.glade:87 +msgid "Progress:" +msgstr "" + +#: glade/delugegtk.glade:101 +msgid "Name:" +msgstr "" + +#: glade/delugegtk.glade:141 +msgid "Estimated Time Remaining:" +msgstr "" + +#: glade/delugegtk.glade:179 +msgid "Peers:" +msgstr "" + +#: glade/delugegtk.glade:193 +msgid "Seeders:" +msgstr "" + +#: glade/delugegtk.glade:205 +msgid "Total Size:" +msgstr "" + +#: glade/delugegtk.glade:217 +msgid "Total Downloaded:" +msgstr "" + +#: glade/delugegtk.glade:229 +msgid "Downloaded this session:" +msgstr "" + +#: glade/delugegtk.glade:277 +msgid "Tracker:" +msgstr "" + +#: glade/delugegtk.glade:289 +msgid "Tracker Response:" +msgstr "" + +#: glade/delugegtk.glade:301 +msgid "Tracker Status:" +msgstr "" + +#: glade/delugegtk.glade:325 +msgid "Next Announce:" +msgstr "" + +#: glade/delugegtk.glade:351 +msgid "Pieces:" +msgstr "" + +#: glade/delugegtk.glade:365 +msgid "Total Uploaded:" +msgstr "" + +#: glade/delugegtk.glade:403 +msgid "Share Ratio:" +msgstr "" + +#: glade/delugegtk.glade:417 +msgid "Uploaded This Session:" +msgstr "" + +#: glade/delugegtk.glade:479 +msgid "Use compact storage allocation:" +msgstr "" + +#: glade/delugegtk.glade:503 +msgid "Download Rate:" +msgstr "" + +#: glade/delugegtk.glade:527 +msgid "Upload Rate:" +msgstr "" + +#: glade/delugegtk.glade:561 glade/delugegtk.glade:824 +msgid "Details" +msgstr "" + +#: glade/delugegtk.glade:588 glade/delugegtk.glade:868 +#: glade/dgtkpopups.glade:37 src/interface.py:310 +msgid "Peers" +msgstr "" + +#: glade/delugegtk.glade:617 +msgid "Files" +msgstr "" + +#: glade/delugegtk.glade:652 +msgid "Log" +msgstr "" + +#: glade/delugegtk.glade:691 +msgid "_Torrent" +msgstr "" + +#: glade/delugegtk.glade:698 glade/delugegtk.glade:979 src/interface.py:201 +msgid "Add Torrent" +msgstr "" + +#: glade/delugegtk.glade:714 +msgid "Add Torrent from URL" +msgstr "" + +#: glade/delugegtk.glade:722 glade/delugegtk.glade:992 +#: glade/dgtkpopups.glade:81 +msgid "Remove Torrent" +msgstr "" + +#: glade/delugegtk.glade:736 +msgid "Clear Completed" +msgstr "" + +#: glade/delugegtk.glade:770 +msgid "_Edit" +msgstr "" + +#: glade/delugegtk.glade:787 glade/dgtkpopups.glade:221 +#: glade/dgtkpref.glade:697 src/interface.py:204 +msgid "Plugins" +msgstr "" + +#: glade/delugegtk.glade:807 +msgid "_View" +msgstr "" + +#: glade/delugegtk.glade:815 +msgid "Toolbar" +msgstr "" + +#: glade/delugegtk.glade:833 +msgid "Columns" +msgstr "" + +#: glade/delugegtk.glade:841 glade/dgtkpopups.glade:10 src/interface.py:307 +#: src/interface.py:444 +msgid "Size" +msgstr "" + +#: glade/delugegtk.glade:850 glade/dgtkpopups.glade:19 src/interface.py:308 +msgid "Status" +msgstr "" + +#: glade/delugegtk.glade:859 glade/dgtkpopups.glade:28 src/interface.py:309 +msgid "Seeders" +msgstr "" + +#: glade/delugegtk.glade:877 glade/torrent_menu.glade:160 src/interface.py:311 +#: src/interface.py:442 src/interface.py:588 src/interface.py:600 +msgid "Download" +msgstr "" + +#: glade/delugegtk.glade:886 glade/torrent_menu.glade:130 src/interface.py:312 +#: src/interface.py:589 src/interface.py:601 +msgid "Upload" +msgstr "" + +#: glade/delugegtk.glade:895 glade/dgtkpopups.glade:64 src/interface.py:313 +msgid "Time Remaining" +msgstr "" + +#: glade/delugegtk.glade:904 glade/dgtkpopups.glade:73 +msgid "Share Ratio" +msgstr "" + +#: glade/delugegtk.glade:921 +msgid "_Help" +msgstr "" + +#: glade/delugegtk.glade:1005 +msgid "Clear Finished Torrents" +msgstr "" + +#: glade/delugegtk.glade:1027 +msgid "Start / Pause" +msgstr "" + +#: glade/delugegtk.glade:1040 +msgid "Queue Torrent Up" +msgstr "" + +#: glade/delugegtk.glade:1053 +msgid "Queue Torrent Down" +msgstr "" + +#: glade/dgtkpopups.glade:46 +msgid "Download Speed" +msgstr "" + +#: glade/dgtkpopups.glade:55 +msgid "Upload Speed" +msgstr "" + +#: glade/dgtkpopups.glade:97 +msgid "" +"Are you sure you want to remove the selected torrent(s) from Deluge?" +msgstr "" + +#: glade/dgtkpopups.glade:107 +msgid "Delete downloaded files" +msgstr "" + +#: glade/dgtkpopups.glade:167 +msgid "Show/Hide" +msgstr "" + +#: glade/dgtkpopups.glade:175 +msgid "Add a Torrent..." +msgstr "" + +#: glade/dgtkpopups.glade:191 src/interface.py:202 +msgid "Clear Finished" +msgstr "" + +#: glade/dgtkpopups.glade:212 glade/dgtkpref.glade:672 +msgid "gtk-preferences" +msgstr "" + +#: glade/dgtkpopups.glade:242 +msgid "gtk-quit" +msgstr "" + +#: glade/dgtkpref.glade:8 +msgid "Preferences Dialog" +msgstr "" + +#: glade/dgtkpref.glade:53 glade/preferences_dialog.glade:924 +msgid "Enable system tray icon" +msgstr "" + +#: glade/dgtkpref.glade:65 glade/preferences_dialog.glade:935 +msgid "Minimize to tray on close" +msgstr "" + +#: glade/dgtkpref.glade:80 +msgid "Options" +msgstr "" + +#: glade/dgtkpref.glade:115 glade/preferences_dialog.glade:73 +msgid "Save all downloads to:" +msgstr "" + +#: glade/dgtkpref.glade:127 glade/preferences_dialog.glade:85 +msgid "Ask me where to save each download" +msgstr "" + +#: glade/dgtkpref.glade:139 +msgid "Save Location" +msgstr "" + +#: glade/dgtkpref.glade:180 +msgid "" +"Stop seeding torrents when\n" +"their share ratio reaches:" +msgstr "" + +#: glade/dgtkpref.glade:190 +msgid "Seeding" +msgstr "" + +#: glade/dgtkpref.glade:209 glade/preferences_dialog.glade:188 +msgid "Use compact storage allocation" +msgstr "" + +#: glade/dgtkpref.glade:216 +msgid "Storage" +msgstr "" + +#: glade/dgtkpref.glade:246 +msgid "General" +msgstr "" + +#: glade/dgtkpref.glade:280 glade/preferences_dialog.glade:365 +msgid "Try from:" +msgstr "" + +#: glade/dgtkpref.glade:286 glade/preferences_dialog.glade:371 +msgid "to:" +msgstr "" + +#: glade/dgtkpref.glade:320 glade/preferences_dialog.glade:405 +msgid "Active port:" +msgstr "" + +#: glade/dgtkpref.glade:332 glade/preferences_dialog.glade:417 +msgid "0000" +msgstr "" + +#: glade/dgtkpref.glade:343 glade/preferences_dialog.glade:429 +msgid "Test Port" +msgstr "" + +#: glade/dgtkpref.glade:357 +msgid "TCP Port" +msgstr "" + +#: glade/dgtkpref.glade:380 glade/preferences_dialog.glade:676 +msgid "(-1 is unlimited)" +msgstr "" + +#: glade/dgtkpref.glade:390 glade/dgtkpref.glade:511 +#: glade/preferences_dialog.glade:686 glade/preferences_dialog.glade:807 +msgid "KB/s" +msgstr "" + +#: glade/dgtkpref.glade:436 glade/preferences_dialog.glade:732 +msgid "Maximum Download Rate:" +msgstr "" + +#: glade/dgtkpref.glade:447 glade/preferences_dialog.glade:743 +msgid "Upload Slots" +msgstr "" + +#: glade/dgtkpref.glade:458 glade/preferences_dialog.glade:754 +msgid "Maximum Connections" +msgstr "" + +#: glade/dgtkpref.glade:485 glade/preferences_dialog.glade:781 +msgid "Maximum Upload Rate:" +msgstr "" + +#: glade/dgtkpref.glade:548 +msgid "Bandwidth " +msgstr "" + +#: glade/dgtkpref.glade:576 glade/preferences_dialog.glade:631 +msgid "Network" +msgstr "" + +#: glade/dgtkpref.glade:598 glade/preferences_dialog.glade:1078 +msgid "gtk-cancel" +msgstr "" + +#: glade/dgtkpref.glade:605 glade/preferences_dialog.glade:1085 +msgid "gtk-ok" +msgstr "" + +#: glade/dgtkpref.glade:625 +msgid "Plugin Manager" +msgstr "" + +#: glade/dgtkpref.glade:718 +msgid "gtk-close" +msgstr "" + +#: glade/preferences_dialog.glade:8 +msgid "Deluge Preferences" +msgstr "" + +#: glade/preferences_dialog.glade:100 +msgid "Download Location" +msgstr "" + +#: glade/preferences_dialog.glade:150 +msgid "Maximum simultaneous active torrents:" +msgstr "" + +#: glade/preferences_dialog.glade:161 +msgid "Torrents" +msgstr "" + +#: glade/preferences_dialog.glade:198 +msgid "Compact Allocation" +msgstr "" + +#: glade/preferences_dialog.glade:248 +msgid "Stop seeding torrents when their share ratio reaches:" +msgstr "" + +#: glade/preferences_dialog.glade:261 +msgid "Queue torrents to bottom when they begin seeding" +msgstr "" + +#: glade/preferences_dialog.glade:276 +msgid "Seeding" +msgstr "" + +#: glade/preferences_dialog.glade:305 +msgid "Downloads" +msgstr "" + +#: glade/preferences_dialog.glade:334 +msgid "" +"Warning - Changes to these settings will only be applied the next time " +"Deluge is restarted" +msgstr "" + +#: glade/preferences_dialog.glade:446 +msgid "TCP Port" +msgstr "" + +#: glade/preferences_dialog.glade:475 +msgid "Enable UPnP support" +msgstr "" + +#: glade/preferences_dialog.glade:485 +msgid "UPnP" +msgstr "" + +#: glade/preferences_dialog.glade:535 +msgid "Max number of DHT connections:" +msgstr "" + +#: glade/preferences_dialog.glade:547 +msgid "Enable Mainline DHT" +msgstr "" + +#: glade/preferences_dialog.glade:562 +msgid "DHT" +msgstr "" + +#: glade/preferences_dialog.glade:591 +msgid "Enable Peer Exchange" +msgstr "" + +#: glade/preferences_dialog.glade:601 +msgid "PEX" +msgstr "" + +#: glade/preferences_dialog.glade:847 +msgid "Bandwidth Usage" +msgstr "" + +#: glade/preferences_dialog.glade:879 +msgid "Bandwidth" +msgstr "" + +#: glade/preferences_dialog.glade:953 +msgid "System Tray" +msgstr "" + +#: glade/preferences_dialog.glade:986 +msgid "seconds" +msgstr "" + +#: glade/preferences_dialog.glade:1015 +msgid "GUI update interval" +msgstr "" + +#: glade/preferences_dialog.glade:1026 +msgid "Performance" +msgstr "" + +#: glade/preferences_dialog.glade:1056 +msgid "Other" +msgstr "" + +#: glade/torrent_menu.glade:11 +msgid "StartPause" +msgstr "" + +#: glade/torrent_menu.glade:27 +msgid "Update Tracker" +msgstr "" + +#: glade/torrent_menu.glade:44 +msgid "Queue" +msgstr "" + +#: glade/torrent_menu.glade:54 +msgid "Top" +msgstr "" + +#: glade/torrent_menu.glade:69 +msgid "Up" +msgstr "" + +#: glade/torrent_menu.glade:85 +msgid "Down" +msgstr "" + +#: glade/torrent_menu.glade:101 +msgid "Bottom" +msgstr "" + +#: glade/torrent_menu.glade:140 +msgid "menuitem9" +msgstr "" + +#: glade/torrent_menu.glade:170 +msgid "menuitem11" +msgstr "" + +#: src/common.py:44 src/interface.py:288 +msgid "Infinity" +msgstr "" + +#: src/common.py:108 +msgid "Error: no webbrowser found" +msgstr "" + +#: src/core.py:339 +msgid "File was not found" +msgstr "" + +#: src/interface.py:200 +msgid "Show / Hide Window" +msgstr "" + +#: src/interface.py:298 +msgid "Unknown" +msgstr "" + +#: src/interface.py:306 +msgid "Name" +msgstr "" + +#: src/interface.py:314 +msgid "Ratio" +msgstr "" + +#: src/interface.py:383 +msgid "Start" +msgstr "" + +#: src/interface.py:386 +msgid "Pause" +msgstr "" + +#: src/interface.py:430 +msgid "IP Address" +msgstr "" + +#: src/interface.py:431 +msgid "Client" +msgstr "" + +#: src/interface.py:432 +msgid "Percent Complete" +msgstr "" + +#: src/interface.py:433 +msgid "Download Rate" +msgstr "" + +#: src/interface.py:434 +msgid "Upload Rate" +msgstr "" + +#: src/interface.py:443 +msgid "Filename" +msgstr "" + +#: src/interface.py:445 +msgid "Offset" +msgstr "" + +#: src/interface.py:446 +msgid "Progress" +msgstr "" + +#: src/interface.py:571 +msgid "" +"For some reason, the previous state could not be loaded, so a blank state " +"has been loaded for you." +msgstr "" + +#: src/interface.py:573 +msgid "Would you like to attempt to reload the previous session's downloads?" +msgstr "" + +#: src/interface.py:588 src/interface.py:600 +msgid "Connections" +msgstr "" + +#: src/interface.py:599 +msgid "Deluge Bittorrent Client" +msgstr "" + +#: src/interface.py:783 +msgid "" +"An error occured while trying to add the torrent. It's possible your ." +"torrent file is corrupted." +msgstr "" + +#: src/interface.py:785 +msgid "The torrent you've added seems to already be in Deluge." +msgstr "" + +#: src/interface.py:789 +msgid "There is not enough free disk space to complete your download." +msgstr "" + +#: src/interface.py:790 +msgid "Space Needed:" +msgstr "" + +#: src/interface.py:791 +msgid "Available Space:" +msgstr "" + +#: src/interface.py:804 +msgid "Add torrent from URL" +msgstr "" + +#: src/interface.py:808 +msgid "Enter the URL of the .torrent to download" +msgstr "" + +#: src/interface.py:872 +msgid "Warning - all downloaded files for this torrent will be deleted!" +msgstr "" + +#: src/dialogs.py:116 +msgid "Plugin" +msgstr "" + +#: src/dialogs.py:118 +msgid "Enabled" +msgstr "" + +#: src/dialogs.py:224 +msgid "Choose a .torrent file" +msgstr "" + +#: src/dialogs.py:229 +msgid "Torrent files" +msgstr "" + +#: src/dialogs.py:233 +msgid "All files" +msgstr "" + +#: src/dialogs.py:250 +msgid "Choose a download directory" +msgstr "" diff --git a/encryption/po/el.po b/encryption/po/el.po new file mode 100644 index 000000000..2633ed78d --- /dev/null +++ b/encryption/po/el.po @@ -0,0 +1,469 @@ +# Greek, Modern (1453-) translation for deluge +# Copyright (c) 2006 Rosetta Contributors and Canonical Ltd 2006 +# This file is distributed under the same license as the deluge package. +# FIRST AUTHOR , 2006. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: deluge\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2007-03-06 16:19-0500\n" +"PO-Revision-Date: 2007-03-11 20:52+0000\n" +"Last-Translator: Athanasios Lefteris \n" +"Language-Team: Greek, Modern (1453-) \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Rosetta-Export-Date: 2007-03-18 15:58+0000\n" + +#: glade/delugegtk.glade:93 +msgid "Estimated Time Remaining:" +msgstr "" + +#: glade/delugegtk.glade:131 +msgid "Peers:" +msgstr "" + +#: glade/delugegtk.glade:145 +msgid "Seeders:" +msgstr "" + +#: glade/delugegtk.glade:157 +msgid "Total Size:" +msgstr "Συνολικό Μέγεθος" + +#: glade/delugegtk.glade:169 +msgid "Total Downloaded:" +msgstr "Σύνολο κατεβασμένων" + +#: glade/delugegtk.glade:181 +msgid "Downloaded this session:" +msgstr "Κατέβηκαν αυτή την συνεδρία:" + +#: glade/delugegtk.glade:229 +msgid "Tracker:" +msgstr "Ιχνηλάτης" + +#: glade/delugegtk.glade:241 +msgid "Tracker Response:" +msgstr "Απάντηση Ιχνηλάτη" + +#: glade/delugegtk.glade:253 +msgid "Tracker Status:" +msgstr "Κατάσταση Ιχνηλάτη" + +#: glade/delugegtk.glade:277 +msgid "Next Announce:" +msgstr "Επόμενη ανακοίνωση" + +#: glade/delugegtk.glade:303 +msgid "Pieces:" +msgstr "Κομμάτια" + +#: glade/delugegtk.glade:317 +msgid "Total Uploaded:" +msgstr "Σύνολο Απεσταλμένων" + +#: glade/delugegtk.glade:355 +msgid "Share Ratio:" +msgstr "Αναλογία" + +#: glade/delugegtk.glade:369 +msgid "Uploaded This Session:" +msgstr "Απεσταλμένα αυτή την συνεδρία" + +#: glade/delugegtk.glade:431 +msgid "Use compact storage allocation:" +msgstr "Χρήση \"compact storage allocation\"" + +#: glade/delugegtk.glade:455 +msgid "Download Rate:" +msgstr "Ρυθμός κατεβάσματος" + +#: glade/delugegtk.glade:479 +msgid "Upload Rate:" +msgstr "Ρυθμός ανεβάσματος" + +#: glade/delugegtk.glade:522 glade/delugegtk.glade:746 +msgid "Details" +msgstr "Λεπτομέρειες" + +#: glade/delugegtk.glade:549 glade/delugegtk.glade:790 +#: glade/dgtkpopups.glade:37 src/delugegtk.py:352 +msgid "Peers" +msgstr "Συνδέσεις" + +#: glade/delugegtk.glade:578 +msgid "Files" +msgstr "Αρχεία" + +#: glade/delugegtk.glade:613 +msgid "_File" +msgstr "_Αρχείο" + +#: glade/delugegtk.glade:620 glade/delugegtk.glade:901 src/delugegtk.py:174 +msgid "Add Torrent" +msgstr "Προσθήκη Torrent" + +#: glade/delugegtk.glade:636 +msgid "Add Torrent from URL" +msgstr "Προσθήκη torrent από URL" + +#: glade/delugegtk.glade:644 glade/delugegtk.glade:914 +#: glade/dgtkpopups.glade:81 +msgid "Remove Torrent" +msgstr "Αφαίρεση torrent" + +#: glade/delugegtk.glade:658 +msgid "Clear Completed" +msgstr "Καθαρισμός ολοκληρώθηκε" + +#: glade/delugegtk.glade:692 +msgid "_Edit" +msgstr "_Επεξεργασία" + +#: glade/delugegtk.glade:709 glade/dgtkpopups.glade:221 +#: glade/dgtkpref.glade:697 src/delugegtk.py:177 +msgid "Plugins" +msgstr "Plugins" + +#: glade/delugegtk.glade:729 +msgid "_View" +msgstr "_Προβολή" + +#: glade/delugegtk.glade:737 +msgid "Toolbar" +msgstr "Γραμμή εργαλείων" + +#: glade/delugegtk.glade:755 +msgid "Columns" +msgstr "Στήλες" + +#: glade/delugegtk.glade:763 glade/dgtkpopups.glade:10 src/delugegtk.py:349 +#: src/delugegtk.py:479 +msgid "Size" +msgstr "Μέγεθος" + +#: glade/delugegtk.glade:772 glade/dgtkpopups.glade:19 src/delugegtk.py:350 +msgid "Status" +msgstr "Κατάσταση" + +#: glade/delugegtk.glade:781 glade/dgtkpopups.glade:28 src/delugegtk.py:351 +msgid "Seeders" +msgstr "Γόνοι" + +#: glade/delugegtk.glade:799 src/delugegtk.py:353 src/delugegtk.py:477 +#: src/delugegtk.py:692 src/delugegtk.py:704 +msgid "Download" +msgstr "Λαμβάνεται" + +#: glade/delugegtk.glade:808 src/delugegtk.py:354 src/delugegtk.py:693 +#: src/delugegtk.py:705 +msgid "Upload" +msgstr "Αποστέλεται" + +#: glade/delugegtk.glade:817 glade/dgtkpopups.glade:64 src/delugegtk.py:355 +msgid "Time Remaining" +msgstr "Χρόνος που απομένει" + +#: glade/delugegtk.glade:826 glade/dgtkpopups.glade:73 +msgid "Share Ratio" +msgstr "" + +#: glade/delugegtk.glade:843 +msgid "_Help" +msgstr "_Βοήθεια" + +#: glade/delugegtk.glade:927 +msgid "Clear Finished Torrents" +msgstr "Καθαρισμός ολοκληρωμένων torrents" + +#: glade/delugegtk.glade:949 +msgid "Start / Pause" +msgstr "Έναρξη / Παύση" + +#: glade/delugegtk.glade:962 +msgid "Queue Torrent Up" +msgstr "Μετακινήστε το torrent ψηλότερα στην σειρά αναμονής" + +#: glade/delugegtk.glade:975 +msgid "Queue Torrent Down" +msgstr "Μετακινήστε το torrent χαμηλότερα στην σειρά αναμονής" + +#: glade/dgtkpopups.glade:46 +msgid "Download Speed" +msgstr "Ταχύτητα κατεβάσματος" + +#: glade/dgtkpopups.glade:55 +msgid "Upload Speed" +msgstr "Ταχύτητα ανεβάσματος" + +#: glade/dgtkpopups.glade:97 +msgid "" +"Are you sure you want to remove the selected torrent(s) from Deluge?" +msgstr "" +"Είστε σίγουρος/σίγουρη ότι θέλετε να αφαιρέσετε τα επιλεγμένα torrent " +";" + +#: glade/dgtkpopups.glade:107 +msgid "Delete downloaded files" +msgstr "Διαγραφή όλων των λήψεων" + +#: glade/dgtkpopups.glade:167 +msgid "Show/Hide" +msgstr "Εμφάνιση/Απόκρυψη" + +#: glade/dgtkpopups.glade:175 +msgid "Add a Torrent..." +msgstr "Προσθήκη Torrent..." + +#: glade/dgtkpopups.glade:191 src/delugegtk.py:175 +msgid "Clear Finished" +msgstr "Καθάρισε τα ολοκληρωμένα" + +#: glade/dgtkpopups.glade:212 glade/dgtkpref.glade:672 +msgid "gtk-preferences" +msgstr "προτιμίσεις-gtk" + +#: glade/dgtkpopups.glade:242 +msgid "gtk-quit" +msgstr "gtk-quit" + +#: glade/dgtkpref.glade:8 +msgid "Preferences Dialog" +msgstr "Διάλογος προτιμήσεων" + +#: glade/dgtkpref.glade:53 +msgid "Enable system tray icon" +msgstr "Εικονίδιο στη μπάρα συστήματος" + +#: glade/dgtkpref.glade:65 +#, fuzzy +msgid "Minimize to tray on close" +msgstr "Ελαχιστοποίηση στην μπάρα κατά την έξοδο" + +#: glade/dgtkpref.glade:80 +msgid "Options" +msgstr "Επιλογές" + +#: glade/dgtkpref.glade:115 +msgid "Save all downloads to:" +msgstr "Αποθήκευση όλων των λήψεων στο:" + +#: glade/dgtkpref.glade:127 +msgid "Ask me where to save each download" +msgstr "Να επιλέγω τοποθεσία αποθήκευσης για κάθε αρχείο" + +#: glade/dgtkpref.glade:139 +msgid "Save Location" +msgstr "Τοποθεσία Αποθήκευσης" + +#: glade/dgtkpref.glade:180 +msgid "" +"Stop seeding torrents when\n" +"their share ratio reaches:" +msgstr "" + +#: glade/dgtkpref.glade:190 +msgid "Seeding" +msgstr "Διμοιράζεται" + +#: glade/dgtkpref.glade:209 +msgid "Use compact storage allocation" +msgstr "" + +#: glade/dgtkpref.glade:216 +#, fuzzy +msgid "Storage" +msgstr "Αποθήκευση" + +#: glade/dgtkpref.glade:246 +msgid "General" +msgstr "Γενικά" + +#: glade/dgtkpref.glade:280 +msgid "Try from:" +msgstr "Δοκιμή από:" + +#: glade/dgtkpref.glade:286 +msgid "to:" +msgstr "" + +#: glade/dgtkpref.glade:320 +msgid "Active port:" +msgstr "Ενεργή θύρα:" + +#: glade/dgtkpref.glade:332 +msgid "0000" +msgstr "0000" + +#: glade/dgtkpref.glade:343 +msgid "Test Port" +msgstr "Αναφορά τέστ" + +#: glade/dgtkpref.glade:357 +msgid "TCP Port" +msgstr "Θύρα TCP" + +#: glade/dgtkpref.glade:380 +msgid "(-1 is unlimited)" +msgstr "" + +#: glade/dgtkpref.glade:390 glade/dgtkpref.glade:511 +msgid "KB/s" +msgstr "KB/δευτ." + +#: glade/dgtkpref.glade:436 +msgid "Maximum Download Rate:" +msgstr "Μέγιστη ταχύτητα Λήψης:" + +#: glade/dgtkpref.glade:447 +msgid "Upload Slots" +msgstr "" + +#: glade/dgtkpref.glade:458 +msgid "Maximum Connections" +msgstr "" + +#: glade/dgtkpref.glade:485 +msgid "Maximum Upload Rate:" +msgstr "Μέγιστη ταχύτητα Αποστολής:" + +#: glade/dgtkpref.glade:548 +msgid "Bandwidth " +msgstr "Eύρος μπάντας " + +#: glade/dgtkpref.glade:576 +msgid "Network" +msgstr "Δίκτυο" + +#: glade/dgtkpref.glade:598 +msgid "gtk-cancel" +msgstr "gtk-άκυρο" + +#: glade/dgtkpref.glade:605 +msgid "gtk-ok" +msgstr "gtk-ΟΚ" + +#: glade/dgtkpref.glade:625 +msgid "Plugin Manager" +msgstr "Διαχειριστής Προσθέτων" + +#: glade/dgtkpref.glade:718 +msgid "gtk-close" +msgstr "gtk-close" + +#: src/dcommon.py:41 src/delugegtk.py:330 +msgid "Infinity" +msgstr "Άπειρο" + +#: src/dcommon.py:105 +msgid "Error: no webbrowser found" +msgstr "Σφάλμα : δεν βρέθηκε browser" + +#: src/deluge.py:332 +msgid "File was not found" +msgstr "" + +#: src/delugegtk.py:173 +msgid "Show / Hide Window" +msgstr "Εμφάνιση / Απόκρυψη Παραθύρου" + +#: src/delugegtk.py:250 +msgid "Plugin" +msgstr "Πρόσθετη λειτουργία" + +#: src/delugegtk.py:252 +msgid "Enabled" +msgstr "Ενεργοποιήθηκε" + +#: src/delugegtk.py:340 +msgid "Unknown" +msgstr "Άγνωστο" + +#: src/delugegtk.py:348 +msgid "Name" +msgstr "Όνομα" + +#: src/delugegtk.py:356 +msgid "Ratio" +msgstr "Αναλογία" + +#: src/delugegtk.py:465 +msgid "IP Address" +msgstr "Διεύθυνση IP" + +#: src/delugegtk.py:466 +msgid "Client" +msgstr "Πελάτης" + +#: src/delugegtk.py:467 +msgid "Percent Complete" +msgstr "Ποσοστό ολοκλήρωσης" + +#: src/delugegtk.py:468 +msgid "Download Rate" +msgstr "Ρυθμός κατεβάσματος" + +#: src/delugegtk.py:469 +msgid "Upload Rate" +msgstr "Ρυθμός ανεβάσματος" + +#: src/delugegtk.py:478 +msgid "Filename" +msgstr "'Ονομα αρχείου" + +#: src/delugegtk.py:480 +msgid "Offset" +msgstr "Μετατόπιση" + +#: src/delugegtk.py:668 +msgid "" +"For some reason, the previous state could not be loaded, so a blank state " +"has been loaded for you." +msgstr "" + +#: src/delugegtk.py:670 +msgid "Would you like to attempt to reload the previous session's downloads?" +msgstr "" + +#: src/delugegtk.py:679 +msgid "Choose the download directory for" +msgstr "" + +#: src/delugegtk.py:692 src/delugegtk.py:704 +msgid "Connections" +msgstr "Συνδέσεις" + +#: src/delugegtk.py:703 +msgid "Deluge Bittorrent Client" +msgstr "Deluge Πελάτης Bittorrent" + +#: src/delugegtk.py:891 +msgid "Add torrent from URL" +msgstr "Προσθήκη torrent από URL" + +#: src/delugegtk.py:895 +msgid "Enter the URL of the .torrent to download" +msgstr "Εισάγετε το URL του .torrent για κατέβασμα" + +#: src/delugegtk.py:959 +msgid "Warning - all downloaded files for this torrent will be deleted!" +msgstr "Προσοχή - όλα τα κατεβασμένα αρχεία αυτού του torrent θα διαγραφούν!" + +#: src/dgtk.py:71 +msgid "Choose a .torrent file" +msgstr "Επιλογή αρχείου .torrent" + +#: src/dgtk.py:76 +msgid "Torrent files" +msgstr "Αρχεία torrent" + +#: src/dgtk.py:80 +msgid "All files" +msgstr "Όλα τα αρχεία" + +#: src/dgtk.py:97 +msgid "Choose a download directory" +msgstr "Επιλογή καταλόγου κατεβάσματος" \ No newline at end of file diff --git a/encryption/po/en_CA.po b/encryption/po/en_CA.po new file mode 100644 index 000000000..6a4ea90a0 --- /dev/null +++ b/encryption/po/en_CA.po @@ -0,0 +1,466 @@ +# English (Canada) translation for deluge +# Copyright (c) 2006 Rosetta Contributors and Canonical Ltd 2006 +# This file is distributed under the same license as the deluge package. +# FIRST AUTHOR , 2006. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: deluge\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2007-03-06 16:19-0500\n" +"PO-Revision-Date: 2006-12-14 16:16+0000\n" +"Last-Translator: Jonathan Zeppettini \n" +"Language-Team: English (Canada) \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Rosetta-Export-Date: 2007-03-18 15:58+0000\n" + +#: glade/delugegtk.glade:93 +msgid "Estimated Time Remaining:" +msgstr "" + +#: glade/delugegtk.glade:131 +msgid "Peers:" +msgstr "" + +#: glade/delugegtk.glade:145 +msgid "Seeders:" +msgstr "" + +#: glade/delugegtk.glade:157 +msgid "Total Size:" +msgstr "Share Ratio:" + +#: glade/delugegtk.glade:169 +msgid "Total Downloaded:" +msgstr "Share Ratio:" + +#: glade/delugegtk.glade:181 +msgid "Downloaded this session:" +msgstr "Downloaded this session:" + +#: glade/delugegtk.glade:229 +msgid "Tracker:" +msgstr "Tracker:" + +#: glade/delugegtk.glade:241 +msgid "Tracker Response:" +msgstr "Tracker Response:" + +#: glade/delugegtk.glade:253 +msgid "Tracker Status:" +msgstr "Tracker Status:" + +#: glade/delugegtk.glade:277 +msgid "Next Announce:" +msgstr "Next Announce:" + +#: glade/delugegtk.glade:303 +msgid "Pieces:" +msgstr "Pieces:" + +#: glade/delugegtk.glade:317 +msgid "Total Uploaded:" +msgstr "Total Uploaded:" + +#: glade/delugegtk.glade:355 +msgid "Share Ratio:" +msgstr "Share Ratio:" + +#: glade/delugegtk.glade:369 +msgid "Uploaded This Session:" +msgstr "Uploaded This Session:" + +#: glade/delugegtk.glade:431 +msgid "Use compact storage allocation:" +msgstr "Use compact storage allocation:" + +#: glade/delugegtk.glade:455 +msgid "Download Rate:" +msgstr "" + +#: glade/delugegtk.glade:479 +msgid "Upload Rate:" +msgstr "" + +#: glade/delugegtk.glade:522 glade/delugegtk.glade:746 +msgid "Details" +msgstr "" + +#: glade/delugegtk.glade:549 glade/delugegtk.glade:790 +#: glade/dgtkpopups.glade:37 src/delugegtk.py:352 +msgid "Peers" +msgstr "Peers" + +#: glade/delugegtk.glade:578 +msgid "Files" +msgstr "" + +#: glade/delugegtk.glade:613 +msgid "_File" +msgstr "_File" + +#: glade/delugegtk.glade:620 glade/delugegtk.glade:901 src/delugegtk.py:174 +msgid "Add Torrent" +msgstr "Add Torrent" + +#: glade/delugegtk.glade:636 +msgid "Add Torrent from URL" +msgstr "" + +#: glade/delugegtk.glade:644 glade/delugegtk.glade:914 +#: glade/dgtkpopups.glade:81 +msgid "Remove Torrent" +msgstr "Remove Torrent" + +#: glade/delugegtk.glade:658 +msgid "Clear Completed" +msgstr "" + +#: glade/delugegtk.glade:692 +msgid "_Edit" +msgstr "_Edit" + +#: glade/delugegtk.glade:709 glade/dgtkpopups.glade:221 +#: glade/dgtkpref.glade:697 src/delugegtk.py:177 +msgid "Plugins" +msgstr "" + +#: glade/delugegtk.glade:729 +msgid "_View" +msgstr "" + +#: glade/delugegtk.glade:737 +msgid "Toolbar" +msgstr "" + +#: glade/delugegtk.glade:755 +msgid "Columns" +msgstr "" + +#: glade/delugegtk.glade:763 glade/dgtkpopups.glade:10 src/delugegtk.py:349 +#: src/delugegtk.py:479 +msgid "Size" +msgstr "Size" + +#: glade/delugegtk.glade:772 glade/dgtkpopups.glade:19 src/delugegtk.py:350 +msgid "Status" +msgstr "" + +#: glade/delugegtk.glade:781 glade/dgtkpopups.glade:28 src/delugegtk.py:351 +msgid "Seeders" +msgstr "Seeders" + +#: glade/delugegtk.glade:799 src/delugegtk.py:353 src/delugegtk.py:477 +#: src/delugegtk.py:692 src/delugegtk.py:704 +msgid "Download" +msgstr "Download" + +#: glade/delugegtk.glade:808 src/delugegtk.py:354 src/delugegtk.py:693 +#: src/delugegtk.py:705 +msgid "Upload" +msgstr "Upload" + +#: glade/delugegtk.glade:817 glade/dgtkpopups.glade:64 src/delugegtk.py:355 +msgid "Time Remaining" +msgstr "" + +#: glade/delugegtk.glade:826 glade/dgtkpopups.glade:73 +msgid "Share Ratio" +msgstr "" + +#: glade/delugegtk.glade:843 +msgid "_Help" +msgstr "_Help" + +#: glade/delugegtk.glade:927 +msgid "Clear Finished Torrents" +msgstr "Clear Finished Torrents" + +#: glade/delugegtk.glade:949 +msgid "Start / Pause" +msgstr "" + +#: glade/delugegtk.glade:962 +msgid "Queue Torrent Up" +msgstr "Queue Torrent Up" + +#: glade/delugegtk.glade:975 +msgid "Queue Torrent Down" +msgstr "Queue Torrent Down" + +#: glade/dgtkpopups.glade:46 +msgid "Download Speed" +msgstr "" + +#: glade/dgtkpopups.glade:55 +msgid "Upload Speed" +msgstr "" + +#: glade/dgtkpopups.glade:97 +msgid "" +"Are you sure you want to remove the selected torrent(s) from Deluge?" +msgstr "" +"Are you sure you want to remove the selected torrent(s) from Deluge?" + +#: glade/dgtkpopups.glade:107 +msgid "Delete downloaded files" +msgstr "Delete downloaded files" + +#: glade/dgtkpopups.glade:167 +msgid "Show/Hide" +msgstr "" + +#: glade/dgtkpopups.glade:175 +msgid "Add a Torrent..." +msgstr "" + +#: glade/dgtkpopups.glade:191 src/delugegtk.py:175 +msgid "Clear Finished" +msgstr "Clear Finished" + +#: glade/dgtkpopups.glade:212 glade/dgtkpref.glade:672 +msgid "gtk-preferences" +msgstr "gtk-preferences" + +#: glade/dgtkpopups.glade:242 +msgid "gtk-quit" +msgstr "" + +#: glade/dgtkpref.glade:8 +msgid "Preferences Dialog" +msgstr "" + +#: glade/dgtkpref.glade:53 +msgid "Enable system tray icon" +msgstr "" + +#: glade/dgtkpref.glade:65 +msgid "Minimize to tray on close" +msgstr "" + +#: glade/dgtkpref.glade:80 +msgid "Options" +msgstr "" + +#: glade/dgtkpref.glade:115 +msgid "Save all downloads to:" +msgstr "Save all downloads to:" + +#: glade/dgtkpref.glade:127 +msgid "Ask me where to save each download" +msgstr "" + +#: glade/dgtkpref.glade:139 +msgid "Save Location" +msgstr "" + +#: glade/dgtkpref.glade:180 +msgid "" +"Stop seeding torrents when\n" +"their share ratio reaches:" +msgstr "" + +#: glade/dgtkpref.glade:190 +msgid "Seeding" +msgstr "Seeding" + +#: glade/dgtkpref.glade:209 +msgid "Use compact storage allocation" +msgstr "" + +#: glade/dgtkpref.glade:216 +msgid "Storage" +msgstr "" + +#: glade/dgtkpref.glade:246 +msgid "General" +msgstr "" + +#: glade/dgtkpref.glade:280 +msgid "Try from:" +msgstr "Try from:" + +#: glade/dgtkpref.glade:286 +msgid "to:" +msgstr "" + +#: glade/dgtkpref.glade:320 +msgid "Active port:" +msgstr "Active port:" + +#: glade/dgtkpref.glade:332 +msgid "0000" +msgstr "" + +#: glade/dgtkpref.glade:343 +msgid "Test Port" +msgstr "Test Port" + +#: glade/dgtkpref.glade:357 +msgid "TCP Port" +msgstr "" + +#: glade/dgtkpref.glade:380 +msgid "(-1 is unlimited)" +msgstr "" + +#: glade/dgtkpref.glade:390 glade/dgtkpref.glade:511 +msgid "KB/s" +msgstr "" + +#: glade/dgtkpref.glade:436 +msgid "Maximum Download Rate:" +msgstr "Maximum Download Rate:" + +#: glade/dgtkpref.glade:447 +msgid "Upload Slots" +msgstr "" + +#: glade/dgtkpref.glade:458 +msgid "Maximum Connections" +msgstr "" + +#: glade/dgtkpref.glade:485 +msgid "Maximum Upload Rate:" +msgstr "Maximum Upload Rate:" + +#: glade/dgtkpref.glade:548 +msgid "Bandwidth " +msgstr "" + +#: glade/dgtkpref.glade:576 +msgid "Network" +msgstr "Network" + +#: glade/dgtkpref.glade:598 +msgid "gtk-cancel" +msgstr "gtk-cancel" + +#: glade/dgtkpref.glade:605 +msgid "gtk-ok" +msgstr "gtk-ok" + +#: glade/dgtkpref.glade:625 +msgid "Plugin Manager" +msgstr "Plugin Manager" + +#: glade/dgtkpref.glade:718 +msgid "gtk-close" +msgstr "" + +#: src/dcommon.py:41 src/delugegtk.py:330 +msgid "Infinity" +msgstr "" + +#: src/dcommon.py:105 +msgid "Error: no webbrowser found" +msgstr "" + +#: src/deluge.py:332 +msgid "File was not found" +msgstr "" + +#: src/delugegtk.py:173 +msgid "Show / Hide Window" +msgstr "" + +#: src/delugegtk.py:250 +msgid "Plugin" +msgstr "" + +#: src/delugegtk.py:252 +msgid "Enabled" +msgstr "" + +#: src/delugegtk.py:340 +msgid "Unknown" +msgstr "Unknown" + +#: src/delugegtk.py:348 +msgid "Name" +msgstr "Name" + +#: src/delugegtk.py:356 +msgid "Ratio" +msgstr "" + +#: src/delugegtk.py:465 +msgid "IP Address" +msgstr "" + +#: src/delugegtk.py:466 +msgid "Client" +msgstr "Client" + +#: src/delugegtk.py:467 +msgid "Percent Complete" +msgstr "" + +#: src/delugegtk.py:468 +msgid "Download Rate" +msgstr "" + +#: src/delugegtk.py:469 +msgid "Upload Rate" +msgstr "" + +#: src/delugegtk.py:478 +msgid "Filename" +msgstr "" + +#: src/delugegtk.py:480 +msgid "Offset" +msgstr "" + +#: src/delugegtk.py:668 +msgid "" +"For some reason, the previous state could not be loaded, so a blank state " +"has been loaded for you." +msgstr "" + +#: src/delugegtk.py:670 +msgid "Would you like to attempt to reload the previous session's downloads?" +msgstr "" + +#: src/delugegtk.py:679 +msgid "Choose the download directory for" +msgstr "" + +#: src/delugegtk.py:692 src/delugegtk.py:704 +msgid "Connections" +msgstr "Connections" + +#: src/delugegtk.py:703 +msgid "Deluge Bittorrent Client" +msgstr "Deluge Bittorrent Client" + +#: src/delugegtk.py:891 +msgid "Add torrent from URL" +msgstr "" + +#: src/delugegtk.py:895 +msgid "Enter the URL of the .torrent to download" +msgstr "" + +#: src/delugegtk.py:959 +msgid "Warning - all downloaded files for this torrent will be deleted!" +msgstr "Warning - all downloaded files for this torrent will be deleted!" + +#: src/dgtk.py:71 +msgid "Choose a .torrent file" +msgstr "" + +#: src/dgtk.py:76 +msgid "Torrent files" +msgstr "" + +#: src/dgtk.py:80 +msgid "All files" +msgstr "All files" + +#: src/dgtk.py:97 +msgid "Choose a download directory" +msgstr "" \ No newline at end of file diff --git a/encryption/po/en_GB.po b/encryption/po/en_GB.po new file mode 100644 index 000000000..847dfc5c3 --- /dev/null +++ b/encryption/po/en_GB.po @@ -0,0 +1,468 @@ +# English (United Kingdom) translation for deluge +# Copyright (c) 2006 Rosetta Contributors and Canonical Ltd 2006 +# This file is distributed under the same license as the deluge package. +# FIRST AUTHOR , 2006. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: deluge\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2007-03-06 16:19-0500\n" +"PO-Revision-Date: 2007-03-06 05:07+0000\n" +"Last-Translator: Jeff Bailes \n" +"Language-Team: English (United Kingdom) \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Rosetta-Export-Date: 2007-03-18 15:58+0000\n" + +#: glade/delugegtk.glade:93 +msgid "Estimated Time Remaining:" +msgstr "Estimated Time Remaining:" + +#: glade/delugegtk.glade:131 +msgid "Peers:" +msgstr "Peers:" + +#: glade/delugegtk.glade:145 +msgid "Seeders:" +msgstr "Seeders:" + +#: glade/delugegtk.glade:157 +msgid "Total Size:" +msgstr "Total Size:" + +#: glade/delugegtk.glade:169 +msgid "Total Downloaded:" +msgstr "Total Downloaded:" + +#: glade/delugegtk.glade:181 +msgid "Downloaded this session:" +msgstr "Downloaded this session:" + +#: glade/delugegtk.glade:229 +msgid "Tracker:" +msgstr "Tracker:" + +#: glade/delugegtk.glade:241 +msgid "Tracker Response:" +msgstr "Tracker Response:" + +#: glade/delugegtk.glade:253 +msgid "Tracker Status:" +msgstr "Tracker Status:" + +#: glade/delugegtk.glade:277 +msgid "Next Announce:" +msgstr "Next Announce:" + +#: glade/delugegtk.glade:303 +msgid "Pieces:" +msgstr "Pieces:" + +#: glade/delugegtk.glade:317 +msgid "Total Uploaded:" +msgstr "Total Uploaded:" + +#: glade/delugegtk.glade:355 +msgid "Share Ratio:" +msgstr "Share Ratio:" + +#: glade/delugegtk.glade:369 +msgid "Uploaded This Session:" +msgstr "Uploaded This Session:" + +#: glade/delugegtk.glade:431 +msgid "Use compact storage allocation:" +msgstr "Use compact storage allocation:" + +#: glade/delugegtk.glade:455 +msgid "Download Rate:" +msgstr "Download Rate:" + +#: glade/delugegtk.glade:479 +msgid "Upload Rate:" +msgstr "Upload Rate:" + +#: glade/delugegtk.glade:522 glade/delugegtk.glade:746 +msgid "Details" +msgstr "Details" + +#: glade/delugegtk.glade:549 glade/delugegtk.glade:790 +#: glade/dgtkpopups.glade:37 src/delugegtk.py:352 +msgid "Peers" +msgstr "Peers" + +#: glade/delugegtk.glade:578 +msgid "Files" +msgstr "Files" + +#: glade/delugegtk.glade:613 +msgid "_File" +msgstr "_File" + +#: glade/delugegtk.glade:620 glade/delugegtk.glade:901 src/delugegtk.py:174 +msgid "Add Torrent" +msgstr "Add Torrent" + +#: glade/delugegtk.glade:636 +msgid "Add Torrent from URL" +msgstr "Add Torrent from URL" + +#: glade/delugegtk.glade:644 glade/delugegtk.glade:914 +#: glade/dgtkpopups.glade:81 +msgid "Remove Torrent" +msgstr "Remove Torrent" + +#: glade/delugegtk.glade:658 +msgid "Clear Completed" +msgstr "Clear Completed" + +#: glade/delugegtk.glade:692 +msgid "_Edit" +msgstr "_Edit" + +#: glade/delugegtk.glade:709 glade/dgtkpopups.glade:221 +#: glade/dgtkpref.glade:697 src/delugegtk.py:177 +msgid "Plugins" +msgstr "Plugins" + +#: glade/delugegtk.glade:729 +msgid "_View" +msgstr "_View" + +#: glade/delugegtk.glade:737 +msgid "Toolbar" +msgstr "Toolbar" + +#: glade/delugegtk.glade:755 +msgid "Columns" +msgstr "Columns" + +#: glade/delugegtk.glade:763 glade/dgtkpopups.glade:10 src/delugegtk.py:349 +#: src/delugegtk.py:479 +msgid "Size" +msgstr "Size" + +#: glade/delugegtk.glade:772 glade/dgtkpopups.glade:19 src/delugegtk.py:350 +msgid "Status" +msgstr "Status" + +#: glade/delugegtk.glade:781 glade/dgtkpopups.glade:28 src/delugegtk.py:351 +msgid "Seeders" +msgstr "Seeders" + +#: glade/delugegtk.glade:799 src/delugegtk.py:353 src/delugegtk.py:477 +#: src/delugegtk.py:692 src/delugegtk.py:704 +msgid "Download" +msgstr "Download" + +#: glade/delugegtk.glade:808 src/delugegtk.py:354 src/delugegtk.py:693 +#: src/delugegtk.py:705 +msgid "Upload" +msgstr "Upload" + +#: glade/delugegtk.glade:817 glade/dgtkpopups.glade:64 src/delugegtk.py:355 +msgid "Time Remaining" +msgstr "Time Remaining" + +#: glade/delugegtk.glade:826 glade/dgtkpopups.glade:73 +msgid "Share Ratio" +msgstr "Share Ratio" + +#: glade/delugegtk.glade:843 +msgid "_Help" +msgstr "_Help" + +#: glade/delugegtk.glade:927 +msgid "Clear Finished Torrents" +msgstr "Clear Finished Torrents" + +#: glade/delugegtk.glade:949 +msgid "Start / Pause" +msgstr "Start / Pause" + +#: glade/delugegtk.glade:962 +msgid "Queue Torrent Up" +msgstr "Queue Torrent Up" + +#: glade/delugegtk.glade:975 +msgid "Queue Torrent Down" +msgstr "Queue Torrent Down" + +#: glade/dgtkpopups.glade:46 +msgid "Download Speed" +msgstr "Download Speed" + +#: glade/dgtkpopups.glade:55 +msgid "Upload Speed" +msgstr "Upload Speed" + +#: glade/dgtkpopups.glade:97 +msgid "" +"Are you sure you want to remove the selected torrent(s) from Deluge?" +msgstr "" +"Are you sure you want to remove the selected torrent(s) from Deluge?" + +#: glade/dgtkpopups.glade:107 +msgid "Delete downloaded files" +msgstr "Delete downloaded files" + +#: glade/dgtkpopups.glade:167 +msgid "Show/Hide" +msgstr "Show/Hide" + +#: glade/dgtkpopups.glade:175 +msgid "Add a Torrent..." +msgstr "Add a Torrent..." + +#: glade/dgtkpopups.glade:191 src/delugegtk.py:175 +msgid "Clear Finished" +msgstr "Clear Finished" + +#: glade/dgtkpopups.glade:212 glade/dgtkpref.glade:672 +msgid "gtk-preferences" +msgstr "gtk-preferences" + +#: glade/dgtkpopups.glade:242 +msgid "gtk-quit" +msgstr "gtk-quit" + +#: glade/dgtkpref.glade:8 +msgid "Preferences Dialog" +msgstr "Preferences Dialogue" + +#: glade/dgtkpref.glade:53 +msgid "Enable system tray icon" +msgstr "Enable system tray icon" + +#: glade/dgtkpref.glade:65 +msgid "Minimize to tray on close" +msgstr "Minimise to tray on close" + +#: glade/dgtkpref.glade:80 +msgid "Options" +msgstr "Options" + +#: glade/dgtkpref.glade:115 +msgid "Save all downloads to:" +msgstr "Save all downloads to:" + +#: glade/dgtkpref.glade:127 +msgid "Ask me where to save each download" +msgstr "Ask me where to save each download" + +#: glade/dgtkpref.glade:139 +msgid "Save Location" +msgstr "Save Location" + +#: glade/dgtkpref.glade:180 +msgid "" +"Stop seeding torrents when\n" +"their share ratio reaches:" +msgstr "" +"Stop seeding torrents when\n" +"their share ratio reaches:" + +#: glade/dgtkpref.glade:190 +msgid "Seeding" +msgstr "Seeding" + +#: glade/dgtkpref.glade:209 +msgid "Use compact storage allocation" +msgstr "Use compact storage allocation" + +#: glade/dgtkpref.glade:216 +msgid "Storage" +msgstr "Storage" + +#: glade/dgtkpref.glade:246 +msgid "General" +msgstr "General" + +#: glade/dgtkpref.glade:280 +msgid "Try from:" +msgstr "Try from:" + +#: glade/dgtkpref.glade:286 +msgid "to:" +msgstr "to:" + +#: glade/dgtkpref.glade:320 +msgid "Active port:" +msgstr "Active port:" + +#: glade/dgtkpref.glade:332 +msgid "0000" +msgstr "0000" + +#: glade/dgtkpref.glade:343 +msgid "Test Port" +msgstr "Test Port" + +#: glade/dgtkpref.glade:357 +msgid "TCP Port" +msgstr "TCP Port" + +#: glade/dgtkpref.glade:380 +msgid "(-1 is unlimited)" +msgstr "(-1 is unlimited)" + +#: glade/dgtkpref.glade:390 glade/dgtkpref.glade:511 +msgid "KB/s" +msgstr "KB/s" + +#: glade/dgtkpref.glade:436 +msgid "Maximum Download Rate:" +msgstr "Maximum Download Rate:" + +#: glade/dgtkpref.glade:447 +msgid "Upload Slots" +msgstr "Upload Slots" + +#: glade/dgtkpref.glade:458 +msgid "Maximum Connections" +msgstr "" + +#: glade/dgtkpref.glade:485 +msgid "Maximum Upload Rate:" +msgstr "Maximum Upload Rate:" + +#: glade/dgtkpref.glade:548 +msgid "Bandwidth " +msgstr "Bandwidth " + +#: glade/dgtkpref.glade:576 +msgid "Network" +msgstr "Network" + +#: glade/dgtkpref.glade:598 +msgid "gtk-cancel" +msgstr "gtk-cancel" + +#: glade/dgtkpref.glade:605 +msgid "gtk-ok" +msgstr "gtk-ok" + +#: glade/dgtkpref.glade:625 +msgid "Plugin Manager" +msgstr "Plugin Manager" + +#: glade/dgtkpref.glade:718 +msgid "gtk-close" +msgstr "gtk-close" + +#: src/dcommon.py:41 src/delugegtk.py:330 +msgid "Infinity" +msgstr "Infinity" + +#: src/dcommon.py:105 +msgid "Error: no webbrowser found" +msgstr "Error: no Web browser found" + +#: src/deluge.py:332 +msgid "File was not found" +msgstr "" + +#: src/delugegtk.py:173 +msgid "Show / Hide Window" +msgstr "Show / Hide Window" + +#: src/delugegtk.py:250 +msgid "Plugin" +msgstr "Plugin" + +#: src/delugegtk.py:252 +msgid "Enabled" +msgstr "Enabled" + +#: src/delugegtk.py:340 +msgid "Unknown" +msgstr "Unknown" + +#: src/delugegtk.py:348 +msgid "Name" +msgstr "Name" + +#: src/delugegtk.py:356 +msgid "Ratio" +msgstr "Ratio" + +#: src/delugegtk.py:465 +msgid "IP Address" +msgstr "IP Address" + +#: src/delugegtk.py:466 +msgid "Client" +msgstr "Client" + +#: src/delugegtk.py:467 +msgid "Percent Complete" +msgstr "Percent Complete" + +#: src/delugegtk.py:468 +msgid "Download Rate" +msgstr "Download Rate" + +#: src/delugegtk.py:469 +msgid "Upload Rate" +msgstr "Upload Rate" + +#: src/delugegtk.py:478 +msgid "Filename" +msgstr "Filename" + +#: src/delugegtk.py:480 +msgid "Offset" +msgstr "Offset" + +#: src/delugegtk.py:668 +msgid "" +"For some reason, the previous state could not be loaded, so a blank state " +"has been loaded for you." +msgstr "" + +#: src/delugegtk.py:670 +msgid "Would you like to attempt to reload the previous session's downloads?" +msgstr "" + +#: src/delugegtk.py:679 +msgid "Choose the download directory for" +msgstr "" + +#: src/delugegtk.py:692 src/delugegtk.py:704 +msgid "Connections" +msgstr "Connections" + +#: src/delugegtk.py:703 +msgid "Deluge Bittorrent Client" +msgstr "Deluge Bittorrent Client" + +#: src/delugegtk.py:891 +msgid "Add torrent from URL" +msgstr "Add torrent from URL" + +#: src/delugegtk.py:895 +msgid "Enter the URL of the .torrent to download" +msgstr "Enter the URL of the .torrent to download" + +#: src/delugegtk.py:959 +msgid "Warning - all downloaded files for this torrent will be deleted!" +msgstr "Warning - all downloaded files for this torrent will be deleted!" + +#: src/dgtk.py:71 +msgid "Choose a .torrent file" +msgstr "Choose a .torrent file" + +#: src/dgtk.py:76 +msgid "Torrent files" +msgstr "Torrent files" + +#: src/dgtk.py:80 +msgid "All files" +msgstr "All files" + +#: src/dgtk.py:97 +msgid "Choose a download directory" +msgstr "Choose a download directory" \ No newline at end of file diff --git a/encryption/po/es.po b/encryption/po/es.po new file mode 100644 index 000000000..830568a4e --- /dev/null +++ b/encryption/po/es.po @@ -0,0 +1,478 @@ +# Spanish translation for deluge +# Copyright (c) 2006 Rosetta Contributors and Canonical Ltd 2006 +# This file is distributed under the same license as the deluge package. +# FIRST AUTHOR , 2006. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: deluge\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2007-03-06 16:19-0500\n" +"PO-Revision-Date: 2007-03-13 03:49+0000\n" +"Last-Translator: alejf \n" +"Language-Team: Spanish \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Rosetta-Export-Date: 2007-03-18 15:58+0000\n" + +#: glade/delugegtk.glade:93 +msgid "Estimated Time Remaining:" +msgstr "Tiempo Estimado que Falta:" + +#: glade/delugegtk.glade:131 +msgid "Peers:" +msgstr "Peers:" + +#: glade/delugegtk.glade:145 +msgid "Seeders:" +msgstr "Seeders:" + +#: glade/delugegtk.glade:157 +msgid "Total Size:" +msgstr "Tamaño Total:" + +#: glade/delugegtk.glade:169 +msgid "Total Downloaded:" +msgstr "Total Descargado:" + +#: glade/delugegtk.glade:181 +msgid "Downloaded this session:" +msgstr "Sesión descargada:" + +#: glade/delugegtk.glade:229 +msgid "Tracker:" +msgstr "Tracker:" + +#: glade/delugegtk.glade:241 +msgid "Tracker Response:" +msgstr "Respuesta del Tracker:" + +#: glade/delugegtk.glade:253 +msgid "Tracker Status:" +msgstr "Estado del Tracker:" + +#: glade/delugegtk.glade:277 +msgid "Next Announce:" +msgstr "Próximo Anuncio:" + +#: glade/delugegtk.glade:303 +msgid "Pieces:" +msgstr "Partes:" + +#: glade/delugegtk.glade:317 +msgid "Total Uploaded:" +msgstr "Total Subido:" + +#: glade/delugegtk.glade:355 +msgid "Share Ratio:" +msgstr "Porcentaje compartido:" + +#: glade/delugegtk.glade:369 +msgid "Uploaded This Session:" +msgstr "Esta Sesión Fue Subida" + +#: glade/delugegtk.glade:431 +msgid "Use compact storage allocation:" +msgstr "Usar asignación de espacio compactado:" + +#: glade/delugegtk.glade:455 +msgid "Download Rate:" +msgstr "Indice de Descarga:" + +#: glade/delugegtk.glade:479 +msgid "Upload Rate:" +msgstr "Índice de Subida:" + +#: glade/delugegtk.glade:522 glade/delugegtk.glade:746 +msgid "Details" +msgstr "Detalles" + +#: glade/delugegtk.glade:549 glade/delugegtk.glade:790 +#: glade/dgtkpopups.glade:37 src/delugegtk.py:352 +#, fuzzy +msgid "Peers" +msgstr "Peers" + +#: glade/delugegtk.glade:578 +msgid "Files" +msgstr "Archivos" + +#: glade/delugegtk.glade:613 +msgid "_File" +msgstr "_Archivo" + +#: glade/delugegtk.glade:620 glade/delugegtk.glade:901 src/delugegtk.py:174 +msgid "Add Torrent" +msgstr "Añadir Torrent" + +#: glade/delugegtk.glade:636 +msgid "Add Torrent from URL" +msgstr "Agregar Torrent desde URL" + +#: glade/delugegtk.glade:644 glade/delugegtk.glade:914 +#: glade/dgtkpopups.glade:81 +msgid "Remove Torrent" +msgstr "Quitar torrent" + +#: glade/delugegtk.glade:658 +msgid "Clear Completed" +msgstr "Limpiar Completados" + +#: glade/delugegtk.glade:692 +msgid "_Edit" +msgstr "_Editar" + +#: glade/delugegtk.glade:709 glade/dgtkpopups.glade:221 +#: glade/dgtkpref.glade:697 src/delugegtk.py:177 +msgid "Plugins" +msgstr "Extensiones" + +#: glade/delugegtk.glade:729 +msgid "_View" +msgstr "_Ver" + +#: glade/delugegtk.glade:737 +msgid "Toolbar" +msgstr "Barra de Herramientas" + +#: glade/delugegtk.glade:755 +msgid "Columns" +msgstr "Columnas" + +#: glade/delugegtk.glade:763 glade/dgtkpopups.glade:10 src/delugegtk.py:349 +#: src/delugegtk.py:479 +msgid "Size" +msgstr "Tamaño" + +#: glade/delugegtk.glade:772 glade/dgtkpopups.glade:19 src/delugegtk.py:350 +msgid "Status" +msgstr "Estado" + +#: glade/delugegtk.glade:781 glade/dgtkpopups.glade:28 src/delugegtk.py:351 +msgid "Seeders" +msgstr "Seeders" + +#: glade/delugegtk.glade:799 src/delugegtk.py:353 src/delugegtk.py:477 +#: src/delugegtk.py:692 src/delugegtk.py:704 +msgid "Download" +msgstr "Descargar" + +#: glade/delugegtk.glade:808 src/delugegtk.py:354 src/delugegtk.py:693 +#: src/delugegtk.py:705 +msgid "Upload" +msgstr "Subir" + +#: glade/delugegtk.glade:817 glade/dgtkpopups.glade:64 src/delugegtk.py:355 +msgid "Time Remaining" +msgstr "Tiempo Restante" + +#: glade/delugegtk.glade:826 glade/dgtkpopups.glade:73 +msgid "Share Ratio" +msgstr "Índice de Compartición" + +#: glade/delugegtk.glade:843 +msgid "_Help" +msgstr "_Ayuda" + +#: glade/delugegtk.glade:927 +msgid "Clear Finished Torrents" +msgstr "Limpiar los torrents finalizados" + +#: glade/delugegtk.glade:949 +msgid "Start / Pause" +msgstr "Iniciar / Pausar" + +#: glade/delugegtk.glade:962 +msgid "Queue Torrent Up" +msgstr "Subir posicion del torrent en cola" + +#: glade/delugegtk.glade:975 +msgid "Queue Torrent Down" +msgstr "Bajar posicion del torrent en cola" + +#: glade/dgtkpopups.glade:46 +msgid "Download Speed" +msgstr "Velocidad de Descarga" + +#: glade/dgtkpopups.glade:55 +msgid "Upload Speed" +msgstr "Velocidad de Subida" + +#: glade/dgtkpopups.glade:97 +msgid "" +"Are you sure you want to remove the selected torrent(s) from Deluge?" +msgstr "" +"¿Está seguro de querer eliminar el(los) torrent(s) seleccionados de " +"Deluge?" + +#: glade/dgtkpopups.glade:107 +msgid "Delete downloaded files" +msgstr "Eliminar Los Archivos Descargados" + +#: glade/dgtkpopups.glade:167 +msgid "Show/Hide" +msgstr "Mostrar/Ocultar" + +#: glade/dgtkpopups.glade:175 +msgid "Add a Torrent..." +msgstr "Agregar un Torrent..." + +#: glade/dgtkpopups.glade:191 src/delugegtk.py:175 +msgid "Clear Finished" +msgstr "Limpiar Terminados" + +#: glade/dgtkpopups.glade:212 glade/dgtkpref.glade:672 +msgid "gtk-preferences" +msgstr "preferencias de gtk" + +#: glade/dgtkpopups.glade:242 +msgid "gtk-quit" +msgstr "Salir-gtk" + +#: glade/dgtkpref.glade:8 +msgid "Preferences Dialog" +msgstr "Diálogo de Preferencias" + +#: glade/dgtkpref.glade:53 +msgid "Enable system tray icon" +msgstr "Activar el icono de la bandeja del sistema" + +#: glade/dgtkpref.glade:65 +msgid "Minimize to tray on close" +msgstr "Minimizar a la bandeja del sistema al cerrar" + +#: glade/dgtkpref.glade:80 +msgid "Options" +msgstr "Opciones" + +#: glade/dgtkpref.glade:115 +msgid "Save all downloads to:" +msgstr "Guardar todas las descargas en:" + +#: glade/dgtkpref.glade:127 +msgid "Ask me where to save each download" +msgstr "Preguntame donde guardar cada vez que descargue" + +#: glade/dgtkpref.glade:139 +msgid "Save Location" +msgstr "Localización para guardar" + +#: glade/dgtkpref.glade:180 +#, fuzzy +msgid "" +"Stop seeding torrents when\n" +"their share ratio reaches:" +msgstr "" +"Parar de seedear torrents cuando\n" +"su índice de compartición alcance:" + +#: glade/dgtkpref.glade:190 +msgid "Seeding" +msgstr "Compartiendo" + +#: glade/dgtkpref.glade:209 +msgid "Use compact storage allocation" +msgstr "Usar Espacio Compacto" + +#: glade/dgtkpref.glade:216 +msgid "Storage" +msgstr "Almacenamiento" + +#: glade/dgtkpref.glade:246 +msgid "General" +msgstr "General" + +#: glade/dgtkpref.glade:280 +msgid "Try from:" +msgstr "Intentar desde:" + +#: glade/dgtkpref.glade:286 +msgid "to:" +msgstr "a:" + +#: glade/dgtkpref.glade:320 +msgid "Active port:" +msgstr "Puerto Activo:" + +#: glade/dgtkpref.glade:332 +msgid "0000" +msgstr "0000" + +#: glade/dgtkpref.glade:343 +msgid "Test Port" +msgstr "Probar el Puerto" + +#: glade/dgtkpref.glade:357 +msgid "TCP Port" +msgstr "Puerto TCP" + +#: glade/dgtkpref.glade:380 +msgid "(-1 is unlimited)" +msgstr "(-1 is inlimitada)" + +#: glade/dgtkpref.glade:390 glade/dgtkpref.glade:511 +msgid "KB/s" +msgstr "KB/s" + +#: glade/dgtkpref.glade:436 +msgid "Maximum Download Rate:" +msgstr "Velocidad máxima de descarga:" + +#: glade/dgtkpref.glade:447 +msgid "Upload Slots" +msgstr "Filas de Mandar" + +#: glade/dgtkpref.glade:458 +#, fuzzy +msgid "Maximum Connections" +msgstr "Maximo de conexiones" + +#: glade/dgtkpref.glade:485 +msgid "Maximum Upload Rate:" +msgstr "Velocidad máxima de subida:" + +#: glade/dgtkpref.glade:548 +msgid "Bandwidth " +msgstr "Ancho de banda " + +#: glade/dgtkpref.glade:576 +msgid "Network" +msgstr "Red" + +#: glade/dgtkpref.glade:598 +msgid "gtk-cancel" +msgstr "gtk-cancel" + +#: glade/dgtkpref.glade:605 +msgid "gtk-ok" +msgstr "gtk-ok" + +#: glade/dgtkpref.glade:625 +msgid "Plugin Manager" +msgstr "Administrador de plugins" + +#: glade/dgtkpref.glade:718 +msgid "gtk-close" +msgstr "gtk-close" + +#: src/dcommon.py:41 src/delugegtk.py:330 +msgid "Infinity" +msgstr "Infinito" + +#: src/dcommon.py:105 +msgid "Error: no webbrowser found" +msgstr "Error: navegador no encontrado" + +#: src/deluge.py:332 +msgid "File was not found" +msgstr "Archivo no fue encontrado" + +#: src/delugegtk.py:173 +msgid "Show / Hide Window" +msgstr "Mostrar / Ocultar Ventana" + +#: src/delugegtk.py:250 +msgid "Plugin" +msgstr "Extensión" + +#: src/delugegtk.py:252 +msgid "Enabled" +msgstr "Activado" + +#: src/delugegtk.py:340 +msgid "Unknown" +msgstr "Desconocido" + +#: src/delugegtk.py:348 +msgid "Name" +msgstr "Nombre" + +#: src/delugegtk.py:356 +msgid "Ratio" +msgstr "Proporción" + +#: src/delugegtk.py:465 +msgid "IP Address" +msgstr "Dirección IP" + +#: src/delugegtk.py:466 +msgid "Client" +msgstr "Cliente" + +#: src/delugegtk.py:467 +msgid "Percent Complete" +msgstr "Porcentaje completado" + +#: src/delugegtk.py:468 +msgid "Download Rate" +msgstr "Proporción Descargado" + +#: src/delugegtk.py:469 +msgid "Upload Rate" +msgstr "Proporción Subido" + +#: src/delugegtk.py:478 +msgid "Filename" +msgstr "Nombre del archivo" + +#: src/delugegtk.py:480 +msgid "Offset" +msgstr "Compensar" + +#: src/delugegtk.py:668 +msgid "" +"For some reason, the previous state could not be loaded, so a blank state " +"has been loaded for you." +msgstr "" +"Por alguna razon, el estado previo no puede ser cargado, así que un espacio " +"en blanco ha sido cargado para ti" + +#: src/delugegtk.py:670 +#, fuzzy +msgid "Would you like to attempt to reload the previous session's downloads?" +msgstr "" +"¿Te gustaria intentar una recarga de lo descargado de la sesion previa?" + +#: src/delugegtk.py:679 +msgid "Choose the download directory for" +msgstr "Cabia el directorio de descarga" + +#: src/delugegtk.py:692 src/delugegtk.py:704 +msgid "Connections" +msgstr "Conexiones" + +#: src/delugegtk.py:703 +msgid "Deluge Bittorrent Client" +msgstr "Deluge - Cliente Bittorrent" + +#: src/delugegtk.py:891 +msgid "Add torrent from URL" +msgstr "Añadir torrent desde Dirección URL" + +#: src/delugegtk.py:895 +msgid "Enter the URL of the .torrent to download" +msgstr "Introduzca la dirección del .torrent para descargar" + +#: src/delugegtk.py:959 +msgid "Warning - all downloaded files for this torrent will be deleted!" +msgstr "" +"Advertencia - ¡todos los archivos descargados para este torrent serán " +"borrados!" + +#: src/dgtk.py:71 +msgid "Choose a .torrent file" +msgstr "Elige un fichero .torrent" + +#: src/dgtk.py:76 +msgid "Torrent files" +msgstr "Archivos torrents" + +#: src/dgtk.py:80 +msgid "All files" +msgstr "Todos los archivos" + +#: src/dgtk.py:97 +msgid "Choose a download directory" +msgstr "Elige un directorio de descarga" \ No newline at end of file diff --git a/encryption/po/et.po b/encryption/po/et.po new file mode 100644 index 000000000..5d87f57d9 --- /dev/null +++ b/encryption/po/et.po @@ -0,0 +1,465 @@ +# Estonian translation for deluge +# Copyright (c) 2006 Rosetta Contributors and Canonical Ltd 2006 +# This file is distributed under the same license as the deluge package. +# FIRST AUTHOR , 2006. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: deluge\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2007-03-06 16:19-0500\n" +"PO-Revision-Date: 2006-11-28 20:13+0000\n" +"Last-Translator: Kaarel \n" +"Language-Team: Estonian \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Rosetta-Export-Date: 2007-03-18 15:58+0000\n" + +#: glade/delugegtk.glade:93 +msgid "Estimated Time Remaining:" +msgstr "" + +#: glade/delugegtk.glade:131 +msgid "Peers:" +msgstr "" + +#: glade/delugegtk.glade:145 +msgid "Seeders:" +msgstr "" + +#: glade/delugegtk.glade:157 +msgid "Total Size:" +msgstr "Kogusuurus:" + +#: glade/delugegtk.glade:169 +msgid "Total Downloaded:" +msgstr "Kokku Allalaetud:" + +#: glade/delugegtk.glade:181 +msgid "Downloaded this session:" +msgstr "Praegusel sessioonil allalaetud:" + +#: glade/delugegtk.glade:229 +msgid "Tracker:" +msgstr "" + +#: glade/delugegtk.glade:241 +msgid "Tracker Response:" +msgstr "" + +#: glade/delugegtk.glade:253 +msgid "Tracker Status:" +msgstr "" + +#: glade/delugegtk.glade:277 +msgid "Next Announce:" +msgstr "" + +#: glade/delugegtk.glade:303 +msgid "Pieces:" +msgstr "Tükid:" + +#: glade/delugegtk.glade:317 +msgid "Total Uploaded:" +msgstr "Kokku Üleslaetud:" + +#: glade/delugegtk.glade:355 +msgid "Share Ratio:" +msgstr "" + +#: glade/delugegtk.glade:369 +msgid "Uploaded This Session:" +msgstr "" + +#: glade/delugegtk.glade:431 +msgid "Use compact storage allocation:" +msgstr "" + +#: glade/delugegtk.glade:455 +msgid "Download Rate:" +msgstr "" + +#: glade/delugegtk.glade:479 +msgid "Upload Rate:" +msgstr "" + +#: glade/delugegtk.glade:522 glade/delugegtk.glade:746 +msgid "Details" +msgstr "" + +#: glade/delugegtk.glade:549 glade/delugegtk.glade:790 +#: glade/dgtkpopups.glade:37 src/delugegtk.py:352 +msgid "Peers" +msgstr "Peerid" + +#: glade/delugegtk.glade:578 +msgid "Files" +msgstr "" + +#: glade/delugegtk.glade:613 +msgid "_File" +msgstr "_Fail" + +#: glade/delugegtk.glade:620 glade/delugegtk.glade:901 src/delugegtk.py:174 +msgid "Add Torrent" +msgstr "Lisa Torrent" + +#: glade/delugegtk.glade:636 +msgid "Add Torrent from URL" +msgstr "" + +#: glade/delugegtk.glade:644 glade/delugegtk.glade:914 +#: glade/dgtkpopups.glade:81 +msgid "Remove Torrent" +msgstr "Eemalda Torrent" + +#: glade/delugegtk.glade:658 +msgid "Clear Completed" +msgstr "" + +#: glade/delugegtk.glade:692 +msgid "_Edit" +msgstr "_Redigeerimine" + +#: glade/delugegtk.glade:709 glade/dgtkpopups.glade:221 +#: glade/dgtkpref.glade:697 src/delugegtk.py:177 +msgid "Plugins" +msgstr "" + +#: glade/delugegtk.glade:729 +msgid "_View" +msgstr "" + +#: glade/delugegtk.glade:737 +msgid "Toolbar" +msgstr "" + +#: glade/delugegtk.glade:755 +msgid "Columns" +msgstr "" + +#: glade/delugegtk.glade:763 glade/dgtkpopups.glade:10 src/delugegtk.py:349 +#: src/delugegtk.py:479 +msgid "Size" +msgstr "Suurus" + +#: glade/delugegtk.glade:772 glade/dgtkpopups.glade:19 src/delugegtk.py:350 +msgid "Status" +msgstr "" + +#: glade/delugegtk.glade:781 glade/dgtkpopups.glade:28 src/delugegtk.py:351 +msgid "Seeders" +msgstr "Seemneid" + +#: glade/delugegtk.glade:799 src/delugegtk.py:353 src/delugegtk.py:477 +#: src/delugegtk.py:692 src/delugegtk.py:704 +msgid "Download" +msgstr "Kiirus Alla" + +#: glade/delugegtk.glade:808 src/delugegtk.py:354 src/delugegtk.py:693 +#: src/delugegtk.py:705 +msgid "Upload" +msgstr "Kiirus Üles" + +#: glade/delugegtk.glade:817 glade/dgtkpopups.glade:64 src/delugegtk.py:355 +msgid "Time Remaining" +msgstr "" + +#: glade/delugegtk.glade:826 glade/dgtkpopups.glade:73 +msgid "Share Ratio" +msgstr "Jagamis Suhe" + +#: glade/delugegtk.glade:843 +msgid "_Help" +msgstr "_Abi" + +#: glade/delugegtk.glade:927 +msgid "Clear Finished Torrents" +msgstr "Puhasta Lõpetanud Torrentid" + +#: glade/delugegtk.glade:949 +msgid "Start / Pause" +msgstr "" + +#: glade/delugegtk.glade:962 +msgid "Queue Torrent Up" +msgstr "Järjekorrasta Torrent Ülespoole" + +#: glade/delugegtk.glade:975 +msgid "Queue Torrent Down" +msgstr "Järjekorrasta Torrent Allapoole" + +#: glade/dgtkpopups.glade:46 +msgid "Download Speed" +msgstr "" + +#: glade/dgtkpopups.glade:55 +msgid "Upload Speed" +msgstr "" + +#: glade/dgtkpopups.glade:97 +msgid "" +"Are you sure you want to remove the selected torrent(s) from Deluge?" +msgstr "" + +#: glade/dgtkpopups.glade:107 +msgid "Delete downloaded files" +msgstr "Kustuta allalaetud failid" + +#: glade/dgtkpopups.glade:167 +msgid "Show/Hide" +msgstr "" + +#: glade/dgtkpopups.glade:175 +msgid "Add a Torrent..." +msgstr "" + +#: glade/dgtkpopups.glade:191 src/delugegtk.py:175 +msgid "Clear Finished" +msgstr "Puhastu Lõpetanutest" + +#: glade/dgtkpopups.glade:212 glade/dgtkpref.glade:672 +msgid "gtk-preferences" +msgstr "eelistused" + +#: glade/dgtkpopups.glade:242 +msgid "gtk-quit" +msgstr "" + +#: glade/dgtkpref.glade:8 +msgid "Preferences Dialog" +msgstr "" + +#: glade/dgtkpref.glade:53 +msgid "Enable system tray icon" +msgstr "" + +#: glade/dgtkpref.glade:65 +msgid "Minimize to tray on close" +msgstr "" + +#: glade/dgtkpref.glade:80 +msgid "Options" +msgstr "" + +#: glade/dgtkpref.glade:115 +msgid "Save all downloads to:" +msgstr "Salvesta kõik allalaadimised:" + +#: glade/dgtkpref.glade:127 +msgid "Ask me where to save each download" +msgstr "" + +#: glade/dgtkpref.glade:139 +msgid "Save Location" +msgstr "" + +#: glade/dgtkpref.glade:180 +msgid "" +"Stop seeding torrents when\n" +"their share ratio reaches:" +msgstr "" + +#: glade/dgtkpref.glade:190 +msgid "Seeding" +msgstr "Jagamas" + +#: glade/dgtkpref.glade:209 +msgid "Use compact storage allocation" +msgstr "" + +#: glade/dgtkpref.glade:216 +msgid "Storage" +msgstr "" + +#: glade/dgtkpref.glade:246 +msgid "General" +msgstr "" + +#: glade/dgtkpref.glade:280 +msgid "Try from:" +msgstr "" + +#: glade/dgtkpref.glade:286 +msgid "to:" +msgstr "" + +#: glade/dgtkpref.glade:320 +msgid "Active port:" +msgstr "" + +#: glade/dgtkpref.glade:332 +msgid "0000" +msgstr "" + +#: glade/dgtkpref.glade:343 +msgid "Test Port" +msgstr "" + +#: glade/dgtkpref.glade:357 +msgid "TCP Port" +msgstr "" + +#: glade/dgtkpref.glade:380 +msgid "(-1 is unlimited)" +msgstr "" + +#: glade/dgtkpref.glade:390 glade/dgtkpref.glade:511 +msgid "KB/s" +msgstr "" + +#: glade/dgtkpref.glade:436 +msgid "Maximum Download Rate:" +msgstr "Maks. Alla:" + +#: glade/dgtkpref.glade:447 +msgid "Upload Slots" +msgstr "" + +#: glade/dgtkpref.glade:458 +msgid "Maximum Connections" +msgstr "" + +#: glade/dgtkpref.glade:485 +msgid "Maximum Upload Rate:" +msgstr "Maks. Üles:" + +#: glade/dgtkpref.glade:548 +msgid "Bandwidth " +msgstr "" + +#: glade/dgtkpref.glade:576 +msgid "Network" +msgstr "" + +#: glade/dgtkpref.glade:598 +msgid "gtk-cancel" +msgstr "keela" + +#: glade/dgtkpref.glade:605 +msgid "gtk-ok" +msgstr "ok" + +#: glade/dgtkpref.glade:625 +msgid "Plugin Manager" +msgstr "" + +#: glade/dgtkpref.glade:718 +msgid "gtk-close" +msgstr "" + +#: src/dcommon.py:41 src/delugegtk.py:330 +msgid "Infinity" +msgstr "" + +#: src/dcommon.py:105 +msgid "Error: no webbrowser found" +msgstr "" + +#: src/deluge.py:332 +msgid "File was not found" +msgstr "" + +#: src/delugegtk.py:173 +msgid "Show / Hide Window" +msgstr "" + +#: src/delugegtk.py:250 +msgid "Plugin" +msgstr "" + +#: src/delugegtk.py:252 +msgid "Enabled" +msgstr "" + +#: src/delugegtk.py:340 +msgid "Unknown" +msgstr "Tundmatu" + +#: src/delugegtk.py:348 +msgid "Name" +msgstr "Nimi" + +#: src/delugegtk.py:356 +msgid "Ratio" +msgstr "" + +#: src/delugegtk.py:465 +msgid "IP Address" +msgstr "" + +#: src/delugegtk.py:466 +msgid "Client" +msgstr "Klient" + +#: src/delugegtk.py:467 +msgid "Percent Complete" +msgstr "" + +#: src/delugegtk.py:468 +msgid "Download Rate" +msgstr "" + +#: src/delugegtk.py:469 +msgid "Upload Rate" +msgstr "" + +#: src/delugegtk.py:478 +msgid "Filename" +msgstr "" + +#: src/delugegtk.py:480 +msgid "Offset" +msgstr "" + +#: src/delugegtk.py:668 +msgid "" +"For some reason, the previous state could not be loaded, so a blank state " +"has been loaded for you." +msgstr "" + +#: src/delugegtk.py:670 +msgid "Would you like to attempt to reload the previous session's downloads?" +msgstr "" + +#: src/delugegtk.py:679 +msgid "Choose the download directory for" +msgstr "" + +#: src/delugegtk.py:692 src/delugegtk.py:704 +msgid "Connections" +msgstr "Ühendusi" + +#: src/delugegtk.py:703 +msgid "Deluge Bittorrent Client" +msgstr "Deluge Bittorrent Klient" + +#: src/delugegtk.py:891 +msgid "Add torrent from URL" +msgstr "" + +#: src/delugegtk.py:895 +msgid "Enter the URL of the .torrent to download" +msgstr "" + +#: src/delugegtk.py:959 +msgid "Warning - all downloaded files for this torrent will be deleted!" +msgstr "" + +#: src/dgtk.py:71 +msgid "Choose a .torrent file" +msgstr "" + +#: src/dgtk.py:76 +msgid "Torrent files" +msgstr "" + +#: src/dgtk.py:80 +msgid "All files" +msgstr "Kõik failid" + +#: src/dgtk.py:97 +msgid "Choose a download directory" +msgstr "" \ No newline at end of file diff --git a/encryption/po/eu.po b/encryption/po/eu.po new file mode 100644 index 000000000..36af8cdad --- /dev/null +++ b/encryption/po/eu.po @@ -0,0 +1,467 @@ +# Basque translation for deluge +# Copyright (c) 2006 Rosetta Contributors and Canonical Ltd 2006 +# This file is distributed under the same license as the deluge package. +# FIRST AUTHOR , 2006. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: deluge\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2007-03-06 16:19-0500\n" +"PO-Revision-Date: 2006-11-01 15:00+0000\n" +"Last-Translator: Xabi Ezpeleta \n" +"Language-Team: Basque \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Rosetta-Export-Date: 2007-03-18 15:58+0000\n" + +#: glade/delugegtk.glade:93 +msgid "Estimated Time Remaining:" +msgstr "" + +#: glade/delugegtk.glade:131 +msgid "Peers:" +msgstr "" + +#: glade/delugegtk.glade:145 +msgid "Seeders:" +msgstr "" + +#: glade/delugegtk.glade:157 +msgid "Total Size:" +msgstr "" + +#: glade/delugegtk.glade:169 +msgid "Total Downloaded:" +msgstr "" + +#: glade/delugegtk.glade:181 +msgid "Downloaded this session:" +msgstr "" + +#: glade/delugegtk.glade:229 +msgid "Tracker:" +msgstr "" + +#: glade/delugegtk.glade:241 +msgid "Tracker Response:" +msgstr "" + +#: glade/delugegtk.glade:253 +msgid "Tracker Status:" +msgstr "" + +#: glade/delugegtk.glade:277 +msgid "Next Announce:" +msgstr "" + +#: glade/delugegtk.glade:303 +msgid "Pieces:" +msgstr "" + +#: glade/delugegtk.glade:317 +msgid "Total Uploaded:" +msgstr "" + +#: glade/delugegtk.glade:355 +msgid "Share Ratio:" +msgstr "" + +#: glade/delugegtk.glade:369 +msgid "Uploaded This Session:" +msgstr "" + +#: glade/delugegtk.glade:431 +msgid "Use compact storage allocation:" +msgstr "" + +#: glade/delugegtk.glade:455 +msgid "Download Rate:" +msgstr "" + +#: glade/delugegtk.glade:479 +msgid "Upload Rate:" +msgstr "" + +#: glade/delugegtk.glade:522 glade/delugegtk.glade:746 +msgid "Details" +msgstr "" + +#: glade/delugegtk.glade:549 glade/delugegtk.glade:790 +#: glade/dgtkpopups.glade:37 src/delugegtk.py:352 +msgid "Peers" +msgstr "Bezeroak" + +#: glade/delugegtk.glade:578 +msgid "Files" +msgstr "" + +#: glade/delugegtk.glade:613 +msgid "_File" +msgstr "_Fitxategia" + +#: glade/delugegtk.glade:620 glade/delugegtk.glade:901 src/delugegtk.py:174 +msgid "Add Torrent" +msgstr "Gehitu Torrent-a" + +#: glade/delugegtk.glade:636 +msgid "Add Torrent from URL" +msgstr "" + +#: glade/delugegtk.glade:644 glade/delugegtk.glade:914 +#: glade/dgtkpopups.glade:81 +msgid "Remove Torrent" +msgstr "Ezabatu Torrent-a" + +#: glade/delugegtk.glade:658 +msgid "Clear Completed" +msgstr "" + +#: glade/delugegtk.glade:692 +msgid "_Edit" +msgstr "_Editatu" + +#: glade/delugegtk.glade:709 glade/dgtkpopups.glade:221 +#: glade/dgtkpref.glade:697 src/delugegtk.py:177 +msgid "Plugins" +msgstr "" + +#: glade/delugegtk.glade:729 +msgid "_View" +msgstr "" + +#: glade/delugegtk.glade:737 +msgid "Toolbar" +msgstr "" + +#: glade/delugegtk.glade:755 +msgid "Columns" +msgstr "" + +#: glade/delugegtk.glade:763 glade/dgtkpopups.glade:10 src/delugegtk.py:349 +#: src/delugegtk.py:479 +msgid "Size" +msgstr "Tamaina" + +#: glade/delugegtk.glade:772 glade/dgtkpopups.glade:19 src/delugegtk.py:350 +msgid "Status" +msgstr "" + +#: glade/delugegtk.glade:781 glade/dgtkpopups.glade:28 src/delugegtk.py:351 +msgid "Seeders" +msgstr "Haziak" + +#: glade/delugegtk.glade:799 src/delugegtk.py:353 src/delugegtk.py:477 +#: src/delugegtk.py:692 src/delugegtk.py:704 +msgid "Download" +msgstr "Jetsiera" + +#: glade/delugegtk.glade:808 src/delugegtk.py:354 src/delugegtk.py:693 +#: src/delugegtk.py:705 +msgid "Upload" +msgstr "Igoera" + +#: glade/delugegtk.glade:817 glade/dgtkpopups.glade:64 src/delugegtk.py:355 +msgid "Time Remaining" +msgstr "" + +#: glade/delugegtk.glade:826 glade/dgtkpopups.glade:73 +msgid "Share Ratio" +msgstr "Elkarbanatze Indizea" + +#: glade/delugegtk.glade:843 +msgid "_Help" +msgstr "_Laguntza" + +#: glade/delugegtk.glade:927 +msgid "Clear Finished Torrents" +msgstr "Garbitu bukatutako Torrent-ak" + +#: glade/delugegtk.glade:949 +msgid "Start / Pause" +msgstr "" + +#: glade/delugegtk.glade:962 +#, fuzzy +msgid "Queue Torrent Up" +msgstr "Igo Torrenta Ilaran" + +#: glade/delugegtk.glade:975 +#, fuzzy +msgid "Queue Torrent Down" +msgstr "Jetsi Torrenta Ilaran" + +#: glade/dgtkpopups.glade:46 +msgid "Download Speed" +msgstr "" + +#: glade/dgtkpopups.glade:55 +msgid "Upload Speed" +msgstr "" + +#: glade/dgtkpopups.glade:97 +msgid "" +"Are you sure you want to remove the selected torrent(s) from Deluge?" +msgstr "" + +#: glade/dgtkpopups.glade:107 +msgid "Delete downloaded files" +msgstr "" + +#: glade/dgtkpopups.glade:167 +msgid "Show/Hide" +msgstr "" + +#: glade/dgtkpopups.glade:175 +msgid "Add a Torrent..." +msgstr "" + +#: glade/dgtkpopups.glade:191 src/delugegtk.py:175 +msgid "Clear Finished" +msgstr "Garbitu bukatutakoak" + +#: glade/dgtkpopups.glade:212 glade/dgtkpref.glade:672 +msgid "gtk-preferences" +msgstr "gtk-preferences" + +#: glade/dgtkpopups.glade:242 +msgid "gtk-quit" +msgstr "" + +#: glade/dgtkpref.glade:8 +msgid "Preferences Dialog" +msgstr "" + +#: glade/dgtkpref.glade:53 +msgid "Enable system tray icon" +msgstr "" + +#: glade/dgtkpref.glade:65 +msgid "Minimize to tray on close" +msgstr "" + +#: glade/dgtkpref.glade:80 +msgid "Options" +msgstr "" + +#: glade/dgtkpref.glade:115 +msgid "Save all downloads to:" +msgstr "Non gorde jetsitako fitxategiak:" + +#: glade/dgtkpref.glade:127 +msgid "Ask me where to save each download" +msgstr "" + +#: glade/dgtkpref.glade:139 +msgid "Save Location" +msgstr "" + +#: glade/dgtkpref.glade:180 +msgid "" +"Stop seeding torrents when\n" +"their share ratio reaches:" +msgstr "" + +#: glade/dgtkpref.glade:190 +msgid "Seeding" +msgstr "Elkarbanatzen" + +#: glade/dgtkpref.glade:209 +msgid "Use compact storage allocation" +msgstr "" + +#: glade/dgtkpref.glade:216 +msgid "Storage" +msgstr "" + +#: glade/dgtkpref.glade:246 +msgid "General" +msgstr "" + +#: glade/dgtkpref.glade:280 +msgid "Try from:" +msgstr "" + +#: glade/dgtkpref.glade:286 +msgid "to:" +msgstr "" + +#: glade/dgtkpref.glade:320 +msgid "Active port:" +msgstr "" + +#: glade/dgtkpref.glade:332 +msgid "0000" +msgstr "" + +#: glade/dgtkpref.glade:343 +msgid "Test Port" +msgstr "" + +#: glade/dgtkpref.glade:357 +msgid "TCP Port" +msgstr "" + +#: glade/dgtkpref.glade:380 +msgid "(-1 is unlimited)" +msgstr "" + +#: glade/dgtkpref.glade:390 glade/dgtkpref.glade:511 +msgid "KB/s" +msgstr "" + +#: glade/dgtkpref.glade:436 +msgid "Maximum Download Rate:" +msgstr "Gehienezko Jetsiera Indizea:" + +#: glade/dgtkpref.glade:447 +msgid "Upload Slots" +msgstr "" + +#: glade/dgtkpref.glade:458 +msgid "Maximum Connections" +msgstr "" + +#: glade/dgtkpref.glade:485 +msgid "Maximum Upload Rate:" +msgstr "Gehienezko Igoera Indizea:" + +#: glade/dgtkpref.glade:548 +msgid "Bandwidth " +msgstr "" + +#: glade/dgtkpref.glade:576 +msgid "Network" +msgstr "" + +#: glade/dgtkpref.glade:598 +msgid "gtk-cancel" +msgstr "gtk-cancel" + +#: glade/dgtkpref.glade:605 +msgid "gtk-ok" +msgstr "gtk-ok" + +#: glade/dgtkpref.glade:625 +msgid "Plugin Manager" +msgstr "" + +#: glade/dgtkpref.glade:718 +msgid "gtk-close" +msgstr "" + +#: src/dcommon.py:41 src/delugegtk.py:330 +msgid "Infinity" +msgstr "" + +#: src/dcommon.py:105 +msgid "Error: no webbrowser found" +msgstr "" + +#: src/deluge.py:332 +msgid "File was not found" +msgstr "" + +#: src/delugegtk.py:173 +msgid "Show / Hide Window" +msgstr "" + +#: src/delugegtk.py:250 +msgid "Plugin" +msgstr "" + +#: src/delugegtk.py:252 +msgid "Enabled" +msgstr "" + +#: src/delugegtk.py:340 +msgid "Unknown" +msgstr "Ezezaguna" + +#: src/delugegtk.py:348 +msgid "Name" +msgstr "Izena" + +#: src/delugegtk.py:356 +msgid "Ratio" +msgstr "" + +#: src/delugegtk.py:465 +msgid "IP Address" +msgstr "" + +#: src/delugegtk.py:466 +msgid "Client" +msgstr "Bezeroa" + +#: src/delugegtk.py:467 +msgid "Percent Complete" +msgstr "" + +#: src/delugegtk.py:468 +msgid "Download Rate" +msgstr "" + +#: src/delugegtk.py:469 +msgid "Upload Rate" +msgstr "" + +#: src/delugegtk.py:478 +msgid "Filename" +msgstr "" + +#: src/delugegtk.py:480 +msgid "Offset" +msgstr "" + +#: src/delugegtk.py:668 +msgid "" +"For some reason, the previous state could not be loaded, so a blank state " +"has been loaded for you." +msgstr "" + +#: src/delugegtk.py:670 +msgid "Would you like to attempt to reload the previous session's downloads?" +msgstr "" + +#: src/delugegtk.py:679 +msgid "Choose the download directory for" +msgstr "" + +#: src/delugegtk.py:692 src/delugegtk.py:704 +msgid "Connections" +msgstr "Konexioak" + +#: src/delugegtk.py:703 +msgid "Deluge Bittorrent Client" +msgstr "Deluge Bittorrent Bezeroa" + +#: src/delugegtk.py:891 +msgid "Add torrent from URL" +msgstr "" + +#: src/delugegtk.py:895 +msgid "Enter the URL of the .torrent to download" +msgstr "" + +#: src/delugegtk.py:959 +msgid "Warning - all downloaded files for this torrent will be deleted!" +msgstr "" + +#: src/dgtk.py:71 +msgid "Choose a .torrent file" +msgstr "" + +#: src/dgtk.py:76 +msgid "Torrent files" +msgstr "" + +#: src/dgtk.py:80 +msgid "All files" +msgstr "Fitxategi guztiak" + +#: src/dgtk.py:97 +msgid "Choose a download directory" +msgstr "" \ No newline at end of file diff --git a/encryption/po/fi.po b/encryption/po/fi.po new file mode 100644 index 000000000..f83f45271 --- /dev/null +++ b/encryption/po/fi.po @@ -0,0 +1,467 @@ +# Finnish translation for deluge +# Copyright (c) 2006 Rosetta Contributors and Canonical Ltd 2006 +# This file is distributed under the same license as the deluge package. +# FIRST AUTHOR , 2006. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: deluge\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2007-03-06 16:19-0500\n" +"PO-Revision-Date: 2007-03-05 17:00+0000\n" +"Last-Translator: Tuomo Sipola \n" +"Language-Team: Finnish \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Rosetta-Export-Date: 2007-03-18 15:58+0000\n" + +#: glade/delugegtk.glade:93 +msgid "Estimated Time Remaining:" +msgstr "Aikaa jäljellä:" + +#: glade/delugegtk.glade:131 +msgid "Peers:" +msgstr "Lataajat:" + +#: glade/delugegtk.glade:145 +msgid "Seeders:" +msgstr "Jakajat:" + +#: glade/delugegtk.glade:157 +msgid "Total Size:" +msgstr "Koko:" + +#: glade/delugegtk.glade:169 +msgid "Total Downloaded:" +msgstr "Ladattu yhteensä:" + +#: glade/delugegtk.glade:181 +msgid "Downloaded this session:" +msgstr "Ladattu tässä istunnossa:" + +#: glade/delugegtk.glade:229 +msgid "Tracker:" +msgstr "Seurantapalvelin:" + +#: glade/delugegtk.glade:241 +msgid "Tracker Response:" +msgstr "Palvelimen vastaus:" + +#: glade/delugegtk.glade:253 +msgid "Tracker Status:" +msgstr "Palvelimen tila:" + +#: glade/delugegtk.glade:277 +msgid "Next Announce:" +msgstr "Seuraava päivitys:" + +#: glade/delugegtk.glade:303 +msgid "Pieces:" +msgstr "Osia:" + +#: glade/delugegtk.glade:317 +msgid "Total Uploaded:" +msgstr "Lähetetty yhteensä:" + +#: glade/delugegtk.glade:355 +msgid "Share Ratio:" +msgstr "Jakosuhde:" + +#: glade/delugegtk.glade:369 +msgid "Uploaded This Session:" +msgstr "Lähetetty tässä istunnossa:" + +#: glade/delugegtk.glade:431 +msgid "Use compact storage allocation:" +msgstr "Käytä kompaktia tilanvarausta:" + +#: glade/delugegtk.glade:455 +msgid "Download Rate:" +msgstr "Latausnopeus:" + +#: glade/delugegtk.glade:479 +msgid "Upload Rate:" +msgstr "Lähetysnopeus:" + +#: glade/delugegtk.glade:522 glade/delugegtk.glade:746 +msgid "Details" +msgstr "Tiedot" + +#: glade/delugegtk.glade:549 glade/delugegtk.glade:790 +#: glade/dgtkpopups.glade:37 src/delugegtk.py:352 +msgid "Peers" +msgstr "Lataajat" + +#: glade/delugegtk.glade:578 +msgid "Files" +msgstr "Tiedostot" + +#: glade/delugegtk.glade:613 +msgid "_File" +msgstr "_Tiedosto" + +#: glade/delugegtk.glade:620 glade/delugegtk.glade:901 src/delugegtk.py:174 +msgid "Add Torrent" +msgstr "Lisää torrent" + +#: glade/delugegtk.glade:636 +msgid "Add Torrent from URL" +msgstr "Lisää torrent osoitteesta" + +#: glade/delugegtk.glade:644 glade/delugegtk.glade:914 +#: glade/dgtkpopups.glade:81 +msgid "Remove Torrent" +msgstr "Poista torrent" + +#: glade/delugegtk.glade:658 +msgid "Clear Completed" +msgstr "Poista valmiit" + +#: glade/delugegtk.glade:692 +msgid "_Edit" +msgstr "_Muokkaa" + +#: glade/delugegtk.glade:709 glade/dgtkpopups.glade:221 +#: glade/dgtkpref.glade:697 src/delugegtk.py:177 +msgid "Plugins" +msgstr "Lisäosat" + +#: glade/delugegtk.glade:729 +msgid "_View" +msgstr "_Näytä" + +#: glade/delugegtk.glade:737 +msgid "Toolbar" +msgstr "Työkalupalkki" + +#: glade/delugegtk.glade:755 +msgid "Columns" +msgstr "Sarakkeet" + +#: glade/delugegtk.glade:763 glade/dgtkpopups.glade:10 src/delugegtk.py:349 +#: src/delugegtk.py:479 +msgid "Size" +msgstr "Koko" + +#: glade/delugegtk.glade:772 glade/dgtkpopups.glade:19 src/delugegtk.py:350 +msgid "Status" +msgstr "Tila" + +#: glade/delugegtk.glade:781 glade/dgtkpopups.glade:28 src/delugegtk.py:351 +msgid "Seeders" +msgstr "Lähettäjät" + +#: glade/delugegtk.glade:799 src/delugegtk.py:353 src/delugegtk.py:477 +#: src/delugegtk.py:692 src/delugegtk.py:704 +msgid "Download" +msgstr "Lataus" + +#: glade/delugegtk.glade:808 src/delugegtk.py:354 src/delugegtk.py:693 +#: src/delugegtk.py:705 +msgid "Upload" +msgstr "Lähetys" + +#: glade/delugegtk.glade:817 glade/dgtkpopups.glade:64 src/delugegtk.py:355 +msgid "Time Remaining" +msgstr "Aikaa jäljellä" + +#: glade/delugegtk.glade:826 glade/dgtkpopups.glade:73 +msgid "Share Ratio" +msgstr "Jakosuhde" + +#: glade/delugegtk.glade:843 +msgid "_Help" +msgstr "_Ohje" + +#: glade/delugegtk.glade:927 +msgid "Clear Finished Torrents" +msgstr "Poista valmiit torrentit" + +#: glade/delugegtk.glade:949 +msgid "Start / Pause" +msgstr "Aloita / Lopeta" + +#: glade/delugegtk.glade:962 +msgid "Queue Torrent Up" +msgstr "Siirrä ylös jonossa" + +#: glade/delugegtk.glade:975 +msgid "Queue Torrent Down" +msgstr "Siirrä alas jonossa" + +#: glade/dgtkpopups.glade:46 +msgid "Download Speed" +msgstr "Latausnopeus" + +#: glade/dgtkpopups.glade:55 +msgid "Upload Speed" +msgstr "Lähetysnopeus" + +#: glade/dgtkpopups.glade:97 +msgid "" +"Are you sure you want to remove the selected torrent(s) from Deluge?" +msgstr "Haluatko varmasti poistaa valitut torrentit?" + +#: glade/dgtkpopups.glade:107 +msgid "Delete downloaded files" +msgstr "Poista ladatut tiedostot" + +#: glade/dgtkpopups.glade:167 +msgid "Show/Hide" +msgstr "Näytä/Piilota" + +#: glade/dgtkpopups.glade:175 +msgid "Add a Torrent..." +msgstr "Lisää torrent..." + +#: glade/dgtkpopups.glade:191 src/delugegtk.py:175 +msgid "Clear Finished" +msgstr "Poista valmiit" + +#: glade/dgtkpopups.glade:212 glade/dgtkpref.glade:672 +msgid "gtk-preferences" +msgstr "gtk-asetukset" + +#: glade/dgtkpopups.glade:242 +msgid "gtk-quit" +msgstr "gtk-lopeta" + +#: glade/dgtkpref.glade:8 +msgid "Preferences Dialog" +msgstr "Asetusikkuna" + +#: glade/dgtkpref.glade:53 +msgid "Enable system tray icon" +msgstr "Näytä kuvake ilmoitusalueella" + +#: glade/dgtkpref.glade:65 +msgid "Minimize to tray on close" +msgstr "Pienennä kuvakkeeksi suljettaessa" + +#: glade/dgtkpref.glade:80 +msgid "Options" +msgstr "Valinnat" + +#: glade/dgtkpref.glade:115 +msgid "Save all downloads to:" +msgstr "Tallenna kaikki lataukset:" + +#: glade/dgtkpref.glade:127 +msgid "Ask me where to save each download" +msgstr "Kysy erikseen kunkin latauksen tallennuspaikka" + +#: glade/dgtkpref.glade:139 +msgid "Save Location" +msgstr "Tallennuspaikka" + +#: glade/dgtkpref.glade:180 +msgid "" +"Stop seeding torrents when\n" +"their share ratio reaches:" +msgstr "" +"Lopeta torrentin jakaminen \n" +"kun jakosuhde saavuttaa:" + +#: glade/dgtkpref.glade:190 +msgid "Seeding" +msgstr "Jakaa" + +#: glade/dgtkpref.glade:209 +msgid "Use compact storage allocation" +msgstr "Käytä kompaktia tilanvarausta" + +#: glade/dgtkpref.glade:216 +msgid "Storage" +msgstr "Tallennus" + +#: glade/dgtkpref.glade:246 +msgid "General" +msgstr "Yleiset" + +#: glade/dgtkpref.glade:280 +msgid "Try from:" +msgstr "Yritä:" + +#: glade/dgtkpref.glade:286 +msgid "to:" +msgstr "-" + +#: glade/dgtkpref.glade:320 +msgid "Active port:" +msgstr "Portti:" + +#: glade/dgtkpref.glade:332 +msgid "0000" +msgstr "0000" + +#: glade/dgtkpref.glade:343 +msgid "Test Port" +msgstr "Kokeile porttia" + +#: glade/dgtkpref.glade:357 +msgid "TCP Port" +msgstr "TCP-portti" + +#: glade/dgtkpref.glade:380 +msgid "(-1 is unlimited)" +msgstr "(-1 = rajoittamaton)" + +#: glade/dgtkpref.glade:390 glade/dgtkpref.glade:511 +msgid "KB/s" +msgstr "kt/s" + +#: glade/dgtkpref.glade:436 +msgid "Maximum Download Rate:" +msgstr "Suurin latausnopeus:" + +#: glade/dgtkpref.glade:447 +msgid "Upload Slots" +msgstr "Lähetyspaikat" + +#: glade/dgtkpref.glade:458 +msgid "Maximum Connections" +msgstr "" + +#: glade/dgtkpref.glade:485 +msgid "Maximum Upload Rate:" +msgstr "Suurin lähetysnopeus:" + +#: glade/dgtkpref.glade:548 +msgid "Bandwidth " +msgstr "Kaistankäyttö " + +#: glade/dgtkpref.glade:576 +msgid "Network" +msgstr "Verkko" + +#: glade/dgtkpref.glade:598 +msgid "gtk-cancel" +msgstr "gtk-peruuta" + +#: glade/dgtkpref.glade:605 +msgid "gtk-ok" +msgstr "gtk-ok" + +#: glade/dgtkpref.glade:625 +msgid "Plugin Manager" +msgstr "Lisäosien hallinta" + +#: glade/dgtkpref.glade:718 +msgid "gtk-close" +msgstr "gtk-sulje" + +#: src/dcommon.py:41 src/delugegtk.py:330 +msgid "Infinity" +msgstr "Ääretön" + +#: src/dcommon.py:105 +msgid "Error: no webbrowser found" +msgstr "Virhe: Internet-selainta ei löydy" + +#: src/deluge.py:332 +msgid "File was not found" +msgstr "" + +#: src/delugegtk.py:173 +msgid "Show / Hide Window" +msgstr "Näytä / Piilota" + +#: src/delugegtk.py:250 +msgid "Plugin" +msgstr "Lisäosa" + +#: src/delugegtk.py:252 +msgid "Enabled" +msgstr "Käytössä" + +#: src/delugegtk.py:340 +msgid "Unknown" +msgstr "Tuntematon" + +#: src/delugegtk.py:348 +msgid "Name" +msgstr "Nimi" + +#: src/delugegtk.py:356 +msgid "Ratio" +msgstr "Suhde" + +#: src/delugegtk.py:465 +msgid "IP Address" +msgstr "IP-osoite" + +#: src/delugegtk.py:466 +msgid "Client" +msgstr "Ohjelma" + +#: src/delugegtk.py:467 +msgid "Percent Complete" +msgstr "% valmiina" + +#: src/delugegtk.py:468 +msgid "Download Rate" +msgstr "Latausnopeus" + +#: src/delugegtk.py:469 +msgid "Upload Rate" +msgstr "Lähetysnopeus" + +#: src/delugegtk.py:478 +msgid "Filename" +msgstr "Tiedostonimi" + +#: src/delugegtk.py:480 +msgid "Offset" +msgstr "Siirtymä" + +#: src/delugegtk.py:668 +msgid "" +"For some reason, the previous state could not be loaded, so a blank state " +"has been loaded for you." +msgstr "" + +#: src/delugegtk.py:670 +msgid "Would you like to attempt to reload the previous session's downloads?" +msgstr "" + +#: src/delugegtk.py:679 +msgid "Choose the download directory for" +msgstr "" + +#: src/delugegtk.py:692 src/delugegtk.py:704 +msgid "Connections" +msgstr "Yhteyksiä" + +#: src/delugegtk.py:703 +msgid "Deluge Bittorrent Client" +msgstr "Deluge BitTorrent-asiakasohjelma" + +#: src/delugegtk.py:891 +msgid "Add torrent from URL" +msgstr "Lisää torrent osoitteesta" + +#: src/delugegtk.py:895 +msgid "Enter the URL of the .torrent to download" +msgstr "Anna lisättävän torrentin URL-osoite" + +#: src/delugegtk.py:959 +msgid "Warning - all downloaded files for this torrent will be deleted!" +msgstr "Varoitus - kaikki tämän torrentin ladatut tiedostot poistetaan!" + +#: src/dgtk.py:71 +msgid "Choose a .torrent file" +msgstr "Valitse .torrent-tiedosto" + +#: src/dgtk.py:76 +msgid "Torrent files" +msgstr "Torrent-tiedostot" + +#: src/dgtk.py:80 +msgid "All files" +msgstr "Kaikki tiedostot" + +#: src/dgtk.py:97 +msgid "Choose a download directory" +msgstr "Valitse latauskansio" \ No newline at end of file diff --git a/encryption/po/fr.po b/encryption/po/fr.po new file mode 100644 index 000000000..836197d1e --- /dev/null +++ b/encryption/po/fr.po @@ -0,0 +1,474 @@ +# French translation for deluge +# Copyright (c) 2006 Rosetta Contributors and Canonical Ltd 2006 +# This file is distributed under the same license as the deluge package. +# FIRST AUTHOR , 2006. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: deluge\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2007-03-06 16:19-0500\n" +"PO-Revision-Date: 2007-03-14 18:22+0000\n" +"Last-Translator: Matrik \n" +"Language-Team: French \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Rosetta-Export-Date: 2007-03-18 15:58+0000\n" + +#: glade/delugegtk.glade:93 +msgid "Estimated Time Remaining:" +msgstr "Temps restant estimé :" + +#: glade/delugegtk.glade:131 +msgid "Peers:" +msgstr "Clients :" + +#: glade/delugegtk.glade:145 +msgid "Seeders:" +msgstr "Sources :" + +#: glade/delugegtk.glade:157 +msgid "Total Size:" +msgstr "Taille totale :" + +#: glade/delugegtk.glade:169 +msgid "Total Downloaded:" +msgstr "Total téléchargé :" + +#: glade/delugegtk.glade:181 +msgid "Downloaded this session:" +msgstr "Telechargé lors de cette session :" + +#: glade/delugegtk.glade:229 +msgid "Tracker:" +msgstr "Tracker :" + +#: glade/delugegtk.glade:241 +msgid "Tracker Response:" +msgstr "Réponse du tracker :" + +#: glade/delugegtk.glade:253 +msgid "Tracker Status:" +msgstr "Statut du tracker :" + +#: glade/delugegtk.glade:277 +msgid "Next Announce:" +msgstr "Prochaine annonce :" + +#: glade/delugegtk.glade:303 +msgid "Pieces:" +msgstr "Pièces :" + +#: glade/delugegtk.glade:317 +msgid "Total Uploaded:" +msgstr "Partage total :" + +#: glade/delugegtk.glade:355 +msgid "Share Ratio:" +msgstr "Ratio de partage :" + +#: glade/delugegtk.glade:369 +msgid "Uploaded This Session:" +msgstr "Partagé durant cette session :" + +#: glade/delugegtk.glade:431 +msgid "Use compact storage allocation:" +msgstr "Utiliser l'allocation de stockage compacte :" + +#: glade/delugegtk.glade:455 +msgid "Download Rate:" +msgstr "Vitesse de réception :" + +#: glade/delugegtk.glade:479 +msgid "Upload Rate:" +msgstr "Vitesse d'envoi :" + +#: glade/delugegtk.glade:522 glade/delugegtk.glade:746 +msgid "Details" +msgstr "Détails" + +#: glade/delugegtk.glade:549 glade/delugegtk.glade:790 +#: glade/dgtkpopups.glade:37 src/delugegtk.py:352 +msgid "Peers" +msgstr "Clients" + +#: glade/delugegtk.glade:578 +msgid "Files" +msgstr "Fichiers" + +#: glade/delugegtk.glade:613 +msgid "_File" +msgstr "_Torrent" + +#: glade/delugegtk.glade:620 glade/delugegtk.glade:901 src/delugegtk.py:174 +msgid "Add Torrent" +msgstr "Ajouter un torrent" + +#: glade/delugegtk.glade:636 +msgid "Add Torrent from URL" +msgstr "Ajouter un torrent depuis l'URL" + +#: glade/delugegtk.glade:644 glade/delugegtk.glade:914 +#: glade/dgtkpopups.glade:81 +msgid "Remove Torrent" +msgstr "Supprimer le torrent" + +#: glade/delugegtk.glade:658 +msgid "Clear Completed" +msgstr "Effacer les torrents terminés" + +#: glade/delugegtk.glade:692 +msgid "_Edit" +msgstr "_Édition" + +#: glade/delugegtk.glade:709 glade/dgtkpopups.glade:221 +#: glade/dgtkpref.glade:697 src/delugegtk.py:177 +msgid "Plugins" +msgstr "Greffons" + +#: glade/delugegtk.glade:729 +msgid "_View" +msgstr "Afficha_ge" + +#: glade/delugegtk.glade:737 +msgid "Toolbar" +msgstr "Barre d'outils" + +#: glade/delugegtk.glade:755 +msgid "Columns" +msgstr "Colonnes" + +#: glade/delugegtk.glade:763 glade/dgtkpopups.glade:10 src/delugegtk.py:349 +#: src/delugegtk.py:479 +msgid "Size" +msgstr "Taille" + +#: glade/delugegtk.glade:772 glade/dgtkpopups.glade:19 src/delugegtk.py:350 +msgid "Status" +msgstr "Statut" + +#: glade/delugegtk.glade:781 glade/dgtkpopups.glade:28 src/delugegtk.py:351 +msgid "Seeders" +msgstr "Sources" + +#: glade/delugegtk.glade:799 src/delugegtk.py:353 src/delugegtk.py:477 +#: src/delugegtk.py:692 src/delugegtk.py:704 +msgid "Download" +msgstr "Réception" + +#: glade/delugegtk.glade:808 src/delugegtk.py:354 src/delugegtk.py:693 +#: src/delugegtk.py:705 +msgid "Upload" +msgstr "Émission" + +#: glade/delugegtk.glade:817 glade/dgtkpopups.glade:64 src/delugegtk.py:355 +msgid "Time Remaining" +msgstr "Temps restant" + +#: glade/delugegtk.glade:826 glade/dgtkpopups.glade:73 +msgid "Share Ratio" +msgstr "Taux de partage" + +#: glade/delugegtk.glade:843 +msgid "_Help" +msgstr "_Aide" + +#: glade/delugegtk.glade:927 +msgid "Clear Finished Torrents" +msgstr "Effacer les torrents terminés" + +#: glade/delugegtk.glade:949 +msgid "Start / Pause" +msgstr "Démarrer / Suspendre" + +#: glade/delugegtk.glade:962 +msgid "Queue Torrent Up" +msgstr "Déplacer le torrent vers le haut" + +#: glade/delugegtk.glade:975 +msgid "Queue Torrent Down" +msgstr "Déplacer le torrent vers le bas" + +#: glade/dgtkpopups.glade:46 +msgid "Download Speed" +msgstr "Vitesse de téléchargement" + +#: glade/dgtkpopups.glade:55 +msgid "Upload Speed" +msgstr "Vitesse d'émission" + +#: glade/dgtkpopups.glade:97 +msgid "" +"Are you sure you want to remove the selected torrent(s) from Deluge?" +msgstr "" +"Etes-vous sûr de vouloir supprimer le(s) torrent(s) sélectionné(s) de " +"Deluge ?" + +#: glade/dgtkpopups.glade:107 +msgid "Delete downloaded files" +msgstr "Supprimer les fichiers telechargés" + +#: glade/dgtkpopups.glade:167 +msgid "Show/Hide" +msgstr "Montrer/Cacher" + +#: glade/dgtkpopups.glade:175 +msgid "Add a Torrent..." +msgstr "Ajouter un Torrent..." + +#: glade/dgtkpopups.glade:191 src/delugegtk.py:175 +msgid "Clear Finished" +msgstr "Effacer terminés" + +#: glade/dgtkpopups.glade:212 glade/dgtkpref.glade:672 +msgid "gtk-preferences" +msgstr "gtk-preferences" + +#: glade/dgtkpopups.glade:242 +msgid "gtk-quit" +msgstr "gtk-quit" + +#: glade/dgtkpref.glade:8 +msgid "Preferences Dialog" +msgstr "Préférences..." + +#: glade/dgtkpref.glade:53 +msgid "Enable system tray icon" +msgstr "Activer l'icône dans la barre des tâches" + +#: glade/dgtkpref.glade:65 +msgid "Minimize to tray on close" +msgstr "Minimiser dans la barre des tâches à la fermeture" + +#: glade/dgtkpref.glade:80 +msgid "Options" +msgstr "Options" + +#: glade/dgtkpref.glade:115 +msgid "Save all downloads to:" +msgstr "Sauvegarder tous les téléchargements dans :" + +#: glade/dgtkpref.glade:127 +msgid "Ask me where to save each download" +msgstr "Demander oú enregistrer chaque téléchargement" + +#: glade/dgtkpref.glade:139 +msgid "Save Location" +msgstr "Emplacement de sauvegarde" + +#: glade/dgtkpref.glade:180 +msgid "" +"Stop seeding torrents when\n" +"their share ratio reaches:" +msgstr "" +"Arrêter de partager les torrents\n" +"quand leur taux de partage atteint :" + +#: glade/dgtkpref.glade:190 +msgid "Seeding" +msgstr "Partage" + +#: glade/dgtkpref.glade:209 +msgid "Use compact storage allocation" +msgstr "Utiliser l'allocation de stockage compacte" + +#: glade/dgtkpref.glade:216 +msgid "Storage" +msgstr "Stockage" + +#: glade/dgtkpref.glade:246 +msgid "General" +msgstr "Général" + +#: glade/dgtkpref.glade:280 +msgid "Try from:" +msgstr "Essayer depuis :" + +#: glade/dgtkpref.glade:286 +msgid "to:" +msgstr "à :" + +#: glade/dgtkpref.glade:320 +msgid "Active port:" +msgstr "Port Actif :" + +#: glade/dgtkpref.glade:332 +msgid "0000" +msgstr "0000" + +#: glade/dgtkpref.glade:343 +msgid "Test Port" +msgstr "Test du Port" + +#: glade/dgtkpref.glade:357 +msgid "TCP Port" +msgstr "Port TCP" + +#: glade/dgtkpref.glade:380 +msgid "(-1 is unlimited)" +msgstr "(aucune limite : -1)" + +#: glade/dgtkpref.glade:390 glade/dgtkpref.glade:511 +msgid "KB/s" +msgstr "ko/s" + +#: glade/dgtkpref.glade:436 +msgid "Maximum Download Rate:" +msgstr "Vitesse maximale de réception :" + +#: glade/dgtkpref.glade:447 +msgid "Upload Slots" +msgstr "Slots d'émission" + +#: glade/dgtkpref.glade:458 +msgid "Maximum Connections" +msgstr "Connexions maximum" + +#: glade/dgtkpref.glade:485 +msgid "Maximum Upload Rate:" +msgstr "Vitesse maximale d'émission :" + +#: glade/dgtkpref.glade:548 +msgid "Bandwidth " +msgstr "Bande passante " + +#: glade/dgtkpref.glade:576 +msgid "Network" +msgstr "Réseau" + +#: glade/dgtkpref.glade:598 +msgid "gtk-cancel" +msgstr "gtk-cancel" + +#: glade/dgtkpref.glade:605 +msgid "gtk-ok" +msgstr "gtk-ok" + +#: glade/dgtkpref.glade:625 +msgid "Plugin Manager" +msgstr "Gestionnaire de greffons" + +#: glade/dgtkpref.glade:718 +msgid "gtk-close" +msgstr "gtk-close" + +#: src/dcommon.py:41 src/delugegtk.py:330 +msgid "Infinity" +msgstr "Infini" + +#: src/dcommon.py:105 +msgid "Error: no webbrowser found" +msgstr "Erreur : aucun navigateur détecté" + +#: src/deluge.py:332 +msgid "File was not found" +msgstr "Le fichier n'a pas été trouvé" + +#: src/delugegtk.py:173 +msgid "Show / Hide Window" +msgstr "Afficher / masquer la fenêtre" + +#: src/delugegtk.py:250 +msgid "Plugin" +msgstr "Greffon" + +#: src/delugegtk.py:252 +msgid "Enabled" +msgstr "Activé" + +#: src/delugegtk.py:340 +msgid "Unknown" +msgstr "Inconnu" + +#: src/delugegtk.py:348 +msgid "Name" +msgstr "Nom" + +#: src/delugegtk.py:356 +msgid "Ratio" +msgstr "Ratio" + +#: src/delugegtk.py:465 +msgid "IP Address" +msgstr "Adresse IP" + +#: src/delugegtk.py:466 +msgid "Client" +msgstr "Client" + +#: src/delugegtk.py:467 +msgid "Percent Complete" +msgstr "Pourcentage complété" + +#: src/delugegtk.py:468 +msgid "Download Rate" +msgstr "Vitesse de réception" + +#: src/delugegtk.py:469 +msgid "Upload Rate" +msgstr "Vitesse d'envoi" + +#: src/delugegtk.py:478 +msgid "Filename" +msgstr "Nom de Fichier" + +#: src/delugegtk.py:480 +msgid "Offset" +msgstr "Décalage" + +#: src/delugegtk.py:668 +msgid "" +"For some reason, the previous state could not be loaded, so a blank state " +"has been loaded for you." +msgstr "" +"La session précédente n'a pas pu être chargée, une session vide a donc été " +"chargée pour vous." + +#: src/delugegtk.py:670 +msgid "Would you like to attempt to reload the previous session's downloads?" +msgstr "" +"Voulez-vous tenter de recharger les téléchargements de la session précédente " +"?" + +#: src/delugegtk.py:679 +msgid "Choose the download directory for" +msgstr "Choisissez le répertoire de téléchargement pour" + +#: src/delugegtk.py:692 src/delugegtk.py:704 +msgid "Connections" +msgstr "Connexions" + +#: src/delugegtk.py:703 +msgid "Deluge Bittorrent Client" +msgstr "Deluge - Client Bittorrent" + +#: src/delugegtk.py:891 +msgid "Add torrent from URL" +msgstr "Ajouter un torrent à partir d'une URL" + +#: src/delugegtk.py:895 +msgid "Enter the URL of the .torrent to download" +msgstr "Entrez l'URL du .torrent à télécharger" + +#: src/delugegtk.py:959 +msgid "Warning - all downloaded files for this torrent will be deleted!" +msgstr "" +"Attention - Tous les fichiers téléchargés pour ce torrent seront supprimés !" + +#: src/dgtk.py:71 +msgid "Choose a .torrent file" +msgstr "Sélection d'un torrent à ouvrir" + +#: src/dgtk.py:76 +msgid "Torrent files" +msgstr "Fichiers Torrent" + +#: src/dgtk.py:80 +msgid "All files" +msgstr "Tous les fichiers" + +#: src/dgtk.py:97 +msgid "Choose a download directory" +msgstr "Choisissez un répertoire de téléchargement" \ No newline at end of file diff --git a/encryption/po/gl.po b/encryption/po/gl.po new file mode 100644 index 000000000..ba77fae39 --- /dev/null +++ b/encryption/po/gl.po @@ -0,0 +1,465 @@ +# Galician translation for deluge +# Copyright (c) 2006 Rosetta Contributors and Canonical Ltd 2006 +# This file is distributed under the same license as the deluge package. +# FIRST AUTHOR , 2006. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: deluge\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2007-03-06 16:19-0500\n" +"PO-Revision-Date: 2006-11-06 22:21+0000\n" +"Last-Translator: Henrique Ferreiro \n" +"Language-Team: Galician \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Rosetta-Export-Date: 2007-03-18 15:58+0000\n" + +#: glade/delugegtk.glade:93 +msgid "Estimated Time Remaining:" +msgstr "" + +#: glade/delugegtk.glade:131 +msgid "Peers:" +msgstr "" + +#: glade/delugegtk.glade:145 +msgid "Seeders:" +msgstr "" + +#: glade/delugegtk.glade:157 +msgid "Total Size:" +msgstr "" + +#: glade/delugegtk.glade:169 +msgid "Total Downloaded:" +msgstr "" + +#: glade/delugegtk.glade:181 +msgid "Downloaded this session:" +msgstr "" + +#: glade/delugegtk.glade:229 +msgid "Tracker:" +msgstr "" + +#: glade/delugegtk.glade:241 +msgid "Tracker Response:" +msgstr "" + +#: glade/delugegtk.glade:253 +msgid "Tracker Status:" +msgstr "" + +#: glade/delugegtk.glade:277 +msgid "Next Announce:" +msgstr "" + +#: glade/delugegtk.glade:303 +msgid "Pieces:" +msgstr "" + +#: glade/delugegtk.glade:317 +msgid "Total Uploaded:" +msgstr "" + +#: glade/delugegtk.glade:355 +msgid "Share Ratio:" +msgstr "" + +#: glade/delugegtk.glade:369 +msgid "Uploaded This Session:" +msgstr "" + +#: glade/delugegtk.glade:431 +msgid "Use compact storage allocation:" +msgstr "" + +#: glade/delugegtk.glade:455 +msgid "Download Rate:" +msgstr "" + +#: glade/delugegtk.glade:479 +msgid "Upload Rate:" +msgstr "" + +#: glade/delugegtk.glade:522 glade/delugegtk.glade:746 +msgid "Details" +msgstr "" + +#: glade/delugegtk.glade:549 glade/delugegtk.glade:790 +#: glade/dgtkpopups.glade:37 src/delugegtk.py:352 +msgid "Peers" +msgstr "Pares" + +#: glade/delugegtk.glade:578 +msgid "Files" +msgstr "" + +#: glade/delugegtk.glade:613 +msgid "_File" +msgstr "_Ficheiro" + +#: glade/delugegtk.glade:620 glade/delugegtk.glade:901 src/delugegtk.py:174 +msgid "Add Torrent" +msgstr "Engadir Torrent" + +#: glade/delugegtk.glade:636 +msgid "Add Torrent from URL" +msgstr "" + +#: glade/delugegtk.glade:644 glade/delugegtk.glade:914 +#: glade/dgtkpopups.glade:81 +msgid "Remove Torrent" +msgstr "Borrar Torrent" + +#: glade/delugegtk.glade:658 +msgid "Clear Completed" +msgstr "" + +#: glade/delugegtk.glade:692 +msgid "_Edit" +msgstr "_Editar" + +#: glade/delugegtk.glade:709 glade/dgtkpopups.glade:221 +#: glade/dgtkpref.glade:697 src/delugegtk.py:177 +msgid "Plugins" +msgstr "" + +#: glade/delugegtk.glade:729 +msgid "_View" +msgstr "" + +#: glade/delugegtk.glade:737 +msgid "Toolbar" +msgstr "" + +#: glade/delugegtk.glade:755 +msgid "Columns" +msgstr "" + +#: glade/delugegtk.glade:763 glade/dgtkpopups.glade:10 src/delugegtk.py:349 +#: src/delugegtk.py:479 +msgid "Size" +msgstr "Tamaño" + +#: glade/delugegtk.glade:772 glade/dgtkpopups.glade:19 src/delugegtk.py:350 +msgid "Status" +msgstr "" + +#: glade/delugegtk.glade:781 glade/dgtkpopups.glade:28 src/delugegtk.py:351 +msgid "Seeders" +msgstr "Sementes" + +#: glade/delugegtk.glade:799 src/delugegtk.py:353 src/delugegtk.py:477 +#: src/delugegtk.py:692 src/delugegtk.py:704 +msgid "Download" +msgstr "Descarga" + +#: glade/delugegtk.glade:808 src/delugegtk.py:354 src/delugegtk.py:693 +#: src/delugegtk.py:705 +msgid "Upload" +msgstr "Subida" + +#: glade/delugegtk.glade:817 glade/dgtkpopups.glade:64 src/delugegtk.py:355 +msgid "Time Remaining" +msgstr "" + +#: glade/delugegtk.glade:826 glade/dgtkpopups.glade:73 +msgid "Share Ratio" +msgstr "Índice de Compartición" + +#: glade/delugegtk.glade:843 +msgid "_Help" +msgstr "_Axuda" + +#: glade/delugegtk.glade:927 +msgid "Clear Finished Torrents" +msgstr "Limpar torrents rematados" + +#: glade/delugegtk.glade:949 +msgid "Start / Pause" +msgstr "" + +#: glade/delugegtk.glade:962 +msgid "Queue Torrent Up" +msgstr "Subir Torrent na Cola" + +#: glade/delugegtk.glade:975 +msgid "Queue Torrent Down" +msgstr "Baixar Torrent na Cola" + +#: glade/dgtkpopups.glade:46 +msgid "Download Speed" +msgstr "" + +#: glade/dgtkpopups.glade:55 +msgid "Upload Speed" +msgstr "" + +#: glade/dgtkpopups.glade:97 +msgid "" +"Are you sure you want to remove the selected torrent(s) from Deluge?" +msgstr "" + +#: glade/dgtkpopups.glade:107 +msgid "Delete downloaded files" +msgstr "" + +#: glade/dgtkpopups.glade:167 +msgid "Show/Hide" +msgstr "" + +#: glade/dgtkpopups.glade:175 +msgid "Add a Torrent..." +msgstr "" + +#: glade/dgtkpopups.glade:191 src/delugegtk.py:175 +msgid "Clear Finished" +msgstr "Limpar rematados" + +#: glade/dgtkpopups.glade:212 glade/dgtkpref.glade:672 +msgid "gtk-preferences" +msgstr "gtk-preferences" + +#: glade/dgtkpopups.glade:242 +msgid "gtk-quit" +msgstr "" + +#: glade/dgtkpref.glade:8 +msgid "Preferences Dialog" +msgstr "" + +#: glade/dgtkpref.glade:53 +msgid "Enable system tray icon" +msgstr "" + +#: glade/dgtkpref.glade:65 +msgid "Minimize to tray on close" +msgstr "" + +#: glade/dgtkpref.glade:80 +msgid "Options" +msgstr "" + +#: glade/dgtkpref.glade:115 +msgid "Save all downloads to:" +msgstr "Gardar todas as descargas en:" + +#: glade/dgtkpref.glade:127 +msgid "Ask me where to save each download" +msgstr "" + +#: glade/dgtkpref.glade:139 +msgid "Save Location" +msgstr "" + +#: glade/dgtkpref.glade:180 +msgid "" +"Stop seeding torrents when\n" +"their share ratio reaches:" +msgstr "" + +#: glade/dgtkpref.glade:190 +msgid "Seeding" +msgstr "Compartindo" + +#: glade/dgtkpref.glade:209 +msgid "Use compact storage allocation" +msgstr "" + +#: glade/dgtkpref.glade:216 +msgid "Storage" +msgstr "" + +#: glade/dgtkpref.glade:246 +msgid "General" +msgstr "" + +#: glade/dgtkpref.glade:280 +msgid "Try from:" +msgstr "" + +#: glade/dgtkpref.glade:286 +msgid "to:" +msgstr "" + +#: glade/dgtkpref.glade:320 +msgid "Active port:" +msgstr "" + +#: glade/dgtkpref.glade:332 +msgid "0000" +msgstr "" + +#: glade/dgtkpref.glade:343 +msgid "Test Port" +msgstr "" + +#: glade/dgtkpref.glade:357 +msgid "TCP Port" +msgstr "" + +#: glade/dgtkpref.glade:380 +msgid "(-1 is unlimited)" +msgstr "" + +#: glade/dgtkpref.glade:390 glade/dgtkpref.glade:511 +msgid "KB/s" +msgstr "" + +#: glade/dgtkpref.glade:436 +msgid "Maximum Download Rate:" +msgstr "Velocidade Máxima de Descarga:" + +#: glade/dgtkpref.glade:447 +msgid "Upload Slots" +msgstr "" + +#: glade/dgtkpref.glade:458 +msgid "Maximum Connections" +msgstr "" + +#: glade/dgtkpref.glade:485 +msgid "Maximum Upload Rate:" +msgstr "Índice Máximo de Subida:" + +#: glade/dgtkpref.glade:548 +msgid "Bandwidth " +msgstr "" + +#: glade/dgtkpref.glade:576 +msgid "Network" +msgstr "" + +#: glade/dgtkpref.glade:598 +msgid "gtk-cancel" +msgstr "gtk-cancel" + +#: glade/dgtkpref.glade:605 +msgid "gtk-ok" +msgstr "gtk-ok" + +#: glade/dgtkpref.glade:625 +msgid "Plugin Manager" +msgstr "" + +#: glade/dgtkpref.glade:718 +msgid "gtk-close" +msgstr "" + +#: src/dcommon.py:41 src/delugegtk.py:330 +msgid "Infinity" +msgstr "" + +#: src/dcommon.py:105 +msgid "Error: no webbrowser found" +msgstr "" + +#: src/deluge.py:332 +msgid "File was not found" +msgstr "" + +#: src/delugegtk.py:173 +msgid "Show / Hide Window" +msgstr "" + +#: src/delugegtk.py:250 +msgid "Plugin" +msgstr "" + +#: src/delugegtk.py:252 +msgid "Enabled" +msgstr "" + +#: src/delugegtk.py:340 +msgid "Unknown" +msgstr "Descoñecido" + +#: src/delugegtk.py:348 +msgid "Name" +msgstr "Nome" + +#: src/delugegtk.py:356 +msgid "Ratio" +msgstr "" + +#: src/delugegtk.py:465 +msgid "IP Address" +msgstr "" + +#: src/delugegtk.py:466 +msgid "Client" +msgstr "Cliente" + +#: src/delugegtk.py:467 +msgid "Percent Complete" +msgstr "" + +#: src/delugegtk.py:468 +msgid "Download Rate" +msgstr "" + +#: src/delugegtk.py:469 +msgid "Upload Rate" +msgstr "" + +#: src/delugegtk.py:478 +msgid "Filename" +msgstr "" + +#: src/delugegtk.py:480 +msgid "Offset" +msgstr "" + +#: src/delugegtk.py:668 +msgid "" +"For some reason, the previous state could not be loaded, so a blank state " +"has been loaded for you." +msgstr "" + +#: src/delugegtk.py:670 +msgid "Would you like to attempt to reload the previous session's downloads?" +msgstr "" + +#: src/delugegtk.py:679 +msgid "Choose the download directory for" +msgstr "" + +#: src/delugegtk.py:692 src/delugegtk.py:704 +msgid "Connections" +msgstr "Conexións" + +#: src/delugegtk.py:703 +msgid "Deluge Bittorrent Client" +msgstr "Cliente de Bittorrent Deluge" + +#: src/delugegtk.py:891 +msgid "Add torrent from URL" +msgstr "" + +#: src/delugegtk.py:895 +msgid "Enter the URL of the .torrent to download" +msgstr "" + +#: src/delugegtk.py:959 +msgid "Warning - all downloaded files for this torrent will be deleted!" +msgstr "" + +#: src/dgtk.py:71 +msgid "Choose a .torrent file" +msgstr "" + +#: src/dgtk.py:76 +msgid "Torrent files" +msgstr "" + +#: src/dgtk.py:80 +msgid "All files" +msgstr "Todos os arquivos" + +#: src/dgtk.py:97 +msgid "Choose a download directory" +msgstr "" \ No newline at end of file diff --git a/encryption/po/he.po b/encryption/po/he.po new file mode 100644 index 000000000..e9b21a2b7 --- /dev/null +++ b/encryption/po/he.po @@ -0,0 +1,465 @@ +# Hebrew translation for deluge +# Copyright (c) 2006 Rosetta Contributors and Canonical Ltd 2006 +# This file is distributed under the same license as the deluge package. +# FIRST AUTHOR , 2006. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: deluge\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2007-03-06 16:19-0500\n" +"PO-Revision-Date: 2006-11-17 19:49+0000\n" +"Last-Translator: kripkenstein \n" +"Language-Team: Hebrew \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Rosetta-Export-Date: 2007-03-18 15:58+0000\n" + +#: glade/delugegtk.glade:93 +msgid "Estimated Time Remaining:" +msgstr "" + +#: glade/delugegtk.glade:131 +msgid "Peers:" +msgstr "" + +#: glade/delugegtk.glade:145 +msgid "Seeders:" +msgstr "" + +#: glade/delugegtk.glade:157 +msgid "Total Size:" +msgstr "גודל כולל:" + +#: glade/delugegtk.glade:169 +msgid "Total Downloaded:" +msgstr "סה\"כ הורד:" + +#: glade/delugegtk.glade:181 +msgid "Downloaded this session:" +msgstr "הורד בהרצה הנוכחית:" + +#: glade/delugegtk.glade:229 +msgid "Tracker:" +msgstr "טראקר:" + +#: glade/delugegtk.glade:241 +msgid "Tracker Response:" +msgstr "תגובת טראקר:" + +#: glade/delugegtk.glade:253 +msgid "Tracker Status:" +msgstr "מצב טראקר:" + +#: glade/delugegtk.glade:277 +msgid "Next Announce:" +msgstr "קריאה באה:" + +#: glade/delugegtk.glade:303 +msgid "Pieces:" +msgstr "חתיכות:" + +#: glade/delugegtk.glade:317 +msgid "Total Uploaded:" +msgstr "סה\"כ הועלה:" + +#: glade/delugegtk.glade:355 +msgid "Share Ratio:" +msgstr "אחוז שיתוף:" + +#: glade/delugegtk.glade:369 +msgid "Uploaded This Session:" +msgstr "הועלה בהרצה הנוכחית:" + +#: glade/delugegtk.glade:431 +msgid "Use compact storage allocation:" +msgstr "משתמש באחסון קומפקטי:" + +#: glade/delugegtk.glade:455 +msgid "Download Rate:" +msgstr "" + +#: glade/delugegtk.glade:479 +msgid "Upload Rate:" +msgstr "" + +#: glade/delugegtk.glade:522 glade/delugegtk.glade:746 +msgid "Details" +msgstr "" + +#: glade/delugegtk.glade:549 glade/delugegtk.glade:790 +#: glade/dgtkpopups.glade:37 src/delugegtk.py:352 +msgid "Peers" +msgstr "פירים (peers)" + +#: glade/delugegtk.glade:578 +msgid "Files" +msgstr "" + +#: glade/delugegtk.glade:613 +msgid "_File" +msgstr "קובץ" + +#: glade/delugegtk.glade:620 glade/delugegtk.glade:901 src/delugegtk.py:174 +msgid "Add Torrent" +msgstr "הוסף טורנט" + +#: glade/delugegtk.glade:636 +msgid "Add Torrent from URL" +msgstr "" + +#: glade/delugegtk.glade:644 glade/delugegtk.glade:914 +#: glade/dgtkpopups.glade:81 +msgid "Remove Torrent" +msgstr "הסר טוררנט" + +#: glade/delugegtk.glade:658 +msgid "Clear Completed" +msgstr "" + +#: glade/delugegtk.glade:692 +msgid "_Edit" +msgstr "עריכה" + +#: glade/delugegtk.glade:709 glade/dgtkpopups.glade:221 +#: glade/dgtkpref.glade:697 src/delugegtk.py:177 +msgid "Plugins" +msgstr "" + +#: glade/delugegtk.glade:729 +msgid "_View" +msgstr "" + +#: glade/delugegtk.glade:737 +msgid "Toolbar" +msgstr "" + +#: glade/delugegtk.glade:755 +msgid "Columns" +msgstr "" + +#: glade/delugegtk.glade:763 glade/dgtkpopups.glade:10 src/delugegtk.py:349 +#: src/delugegtk.py:479 +msgid "Size" +msgstr "גודל" + +#: glade/delugegtk.glade:772 glade/dgtkpopups.glade:19 src/delugegtk.py:350 +msgid "Status" +msgstr "" + +#: glade/delugegtk.glade:781 glade/dgtkpopups.glade:28 src/delugegtk.py:351 +msgid "Seeders" +msgstr "סידרים (seeders)" + +#: glade/delugegtk.glade:799 src/delugegtk.py:353 src/delugegtk.py:477 +#: src/delugegtk.py:692 src/delugegtk.py:704 +msgid "Download" +msgstr "הורדה" + +#: glade/delugegtk.glade:808 src/delugegtk.py:354 src/delugegtk.py:693 +#: src/delugegtk.py:705 +msgid "Upload" +msgstr "העלאה" + +#: glade/delugegtk.glade:817 glade/dgtkpopups.glade:64 src/delugegtk.py:355 +msgid "Time Remaining" +msgstr "" + +#: glade/delugegtk.glade:826 glade/dgtkpopups.glade:73 +msgid "Share Ratio" +msgstr "אחוז שיתוף" + +#: glade/delugegtk.glade:843 +msgid "_Help" +msgstr "עזרה" + +#: glade/delugegtk.glade:927 +msgid "Clear Finished Torrents" +msgstr "הסר טורנטים שסיימו" + +#: glade/delugegtk.glade:949 +msgid "Start / Pause" +msgstr "" + +#: glade/delugegtk.glade:962 +msgid "Queue Torrent Up" +msgstr "העלה עדיפות" + +#: glade/delugegtk.glade:975 +msgid "Queue Torrent Down" +msgstr "הורד עדיפות" + +#: glade/dgtkpopups.glade:46 +msgid "Download Speed" +msgstr "" + +#: glade/dgtkpopups.glade:55 +msgid "Upload Speed" +msgstr "" + +#: glade/dgtkpopups.glade:97 +msgid "" +"Are you sure you want to remove the selected torrent(s) from Deluge?" +msgstr "באמת להסר את הטורנט(ים) הללו?" + +#: glade/dgtkpopups.glade:107 +msgid "Delete downloaded files" +msgstr "מחק קבצים שהורדו" + +#: glade/dgtkpopups.glade:167 +msgid "Show/Hide" +msgstr "" + +#: glade/dgtkpopups.glade:175 +msgid "Add a Torrent..." +msgstr "" + +#: glade/dgtkpopups.glade:191 src/delugegtk.py:175 +msgid "Clear Finished" +msgstr "הסר טורנטים שסיימו" + +#: glade/dgtkpopups.glade:212 glade/dgtkpref.glade:672 +msgid "gtk-preferences" +msgstr "gtk-preferences" + +#: glade/dgtkpopups.glade:242 +msgid "gtk-quit" +msgstr "" + +#: glade/dgtkpref.glade:8 +msgid "Preferences Dialog" +msgstr "" + +#: glade/dgtkpref.glade:53 +msgid "Enable system tray icon" +msgstr "" + +#: glade/dgtkpref.glade:65 +msgid "Minimize to tray on close" +msgstr "" + +#: glade/dgtkpref.glade:80 +msgid "Options" +msgstr "" + +#: glade/dgtkpref.glade:115 +msgid "Save all downloads to:" +msgstr "שמור את כל ההורדות ל-:" + +#: glade/dgtkpref.glade:127 +msgid "Ask me where to save each download" +msgstr "" + +#: glade/dgtkpref.glade:139 +msgid "Save Location" +msgstr "" + +#: glade/dgtkpref.glade:180 +msgid "" +"Stop seeding torrents when\n" +"their share ratio reaches:" +msgstr "" + +#: glade/dgtkpref.glade:190 +msgid "Seeding" +msgstr "סידינג" + +#: glade/dgtkpref.glade:209 +msgid "Use compact storage allocation" +msgstr "" + +#: glade/dgtkpref.glade:216 +msgid "Storage" +msgstr "" + +#: glade/dgtkpref.glade:246 +msgid "General" +msgstr "" + +#: glade/dgtkpref.glade:280 +msgid "Try from:" +msgstr "נסה מ:" + +#: glade/dgtkpref.glade:286 +msgid "to:" +msgstr "" + +#: glade/dgtkpref.glade:320 +msgid "Active port:" +msgstr "פורט פעיל:" + +#: glade/dgtkpref.glade:332 +msgid "0000" +msgstr "" + +#: glade/dgtkpref.glade:343 +msgid "Test Port" +msgstr "בדוק פורט" + +#: glade/dgtkpref.glade:357 +msgid "TCP Port" +msgstr "" + +#: glade/dgtkpref.glade:380 +msgid "(-1 is unlimited)" +msgstr "" + +#: glade/dgtkpref.glade:390 glade/dgtkpref.glade:511 +msgid "KB/s" +msgstr "" + +#: glade/dgtkpref.glade:436 +msgid "Maximum Download Rate:" +msgstr "קצב הורדה מקסימלי:" + +#: glade/dgtkpref.glade:447 +msgid "Upload Slots" +msgstr "" + +#: glade/dgtkpref.glade:458 +msgid "Maximum Connections" +msgstr "" + +#: glade/dgtkpref.glade:485 +msgid "Maximum Upload Rate:" +msgstr "קצב העלאה מקסימלי:" + +#: glade/dgtkpref.glade:548 +msgid "Bandwidth " +msgstr "" + +#: glade/dgtkpref.glade:576 +msgid "Network" +msgstr "רשת" + +#: glade/dgtkpref.glade:598 +msgid "gtk-cancel" +msgstr "gtk-cancel" + +#: glade/dgtkpref.glade:605 +msgid "gtk-ok" +msgstr "gtk-ok" + +#: glade/dgtkpref.glade:625 +msgid "Plugin Manager" +msgstr "מנהל פלאגאינים" + +#: glade/dgtkpref.glade:718 +msgid "gtk-close" +msgstr "" + +#: src/dcommon.py:41 src/delugegtk.py:330 +msgid "Infinity" +msgstr "" + +#: src/dcommon.py:105 +msgid "Error: no webbrowser found" +msgstr "" + +#: src/deluge.py:332 +msgid "File was not found" +msgstr "" + +#: src/delugegtk.py:173 +msgid "Show / Hide Window" +msgstr "" + +#: src/delugegtk.py:250 +msgid "Plugin" +msgstr "" + +#: src/delugegtk.py:252 +msgid "Enabled" +msgstr "" + +#: src/delugegtk.py:340 +msgid "Unknown" +msgstr "לא ידוע" + +#: src/delugegtk.py:348 +msgid "Name" +msgstr "שם" + +#: src/delugegtk.py:356 +msgid "Ratio" +msgstr "" + +#: src/delugegtk.py:465 +msgid "IP Address" +msgstr "" + +#: src/delugegtk.py:466 +msgid "Client" +msgstr "תוכנה" + +#: src/delugegtk.py:467 +msgid "Percent Complete" +msgstr "" + +#: src/delugegtk.py:468 +msgid "Download Rate" +msgstr "" + +#: src/delugegtk.py:469 +msgid "Upload Rate" +msgstr "" + +#: src/delugegtk.py:478 +msgid "Filename" +msgstr "" + +#: src/delugegtk.py:480 +msgid "Offset" +msgstr "" + +#: src/delugegtk.py:668 +msgid "" +"For some reason, the previous state could not be loaded, so a blank state " +"has been loaded for you." +msgstr "" + +#: src/delugegtk.py:670 +msgid "Would you like to attempt to reload the previous session's downloads?" +msgstr "" + +#: src/delugegtk.py:679 +msgid "Choose the download directory for" +msgstr "" + +#: src/delugegtk.py:692 src/delugegtk.py:704 +msgid "Connections" +msgstr "חיבורים" + +#: src/delugegtk.py:703 +msgid "Deluge Bittorrent Client" +msgstr "Deluge - תוכנת ביטטוררנט" + +#: src/delugegtk.py:891 +msgid "Add torrent from URL" +msgstr "" + +#: src/delugegtk.py:895 +msgid "Enter the URL of the .torrent to download" +msgstr "" + +#: src/delugegtk.py:959 +msgid "Warning - all downloaded files for this torrent will be deleted!" +msgstr "אזהרה - כל הקבצים שהורדו בשביל הטורנט הזה ימחקו!" + +#: src/dgtk.py:71 +msgid "Choose a .torrent file" +msgstr "" + +#: src/dgtk.py:76 +msgid "Torrent files" +msgstr "" + +#: src/dgtk.py:80 +msgid "All files" +msgstr "כל הקבצים" + +#: src/dgtk.py:97 +msgid "Choose a download directory" +msgstr "" \ No newline at end of file diff --git a/encryption/po/hr.po b/encryption/po/hr.po new file mode 100644 index 000000000..61dbe49cd --- /dev/null +++ b/encryption/po/hr.po @@ -0,0 +1,465 @@ +# Croatian translation for deluge +# Copyright (c) 2006 Rosetta Contributors and Canonical Ltd 2006 +# This file is distributed under the same license as the deluge package. +# FIRST AUTHOR , 2006. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: deluge\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2007-03-06 16:19-0500\n" +"PO-Revision-Date: 2006-12-30 12:26+0000\n" +"Last-Translator: Miroslav Matejaš \n" +"Language-Team: Croatian \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Rosetta-Export-Date: 2007-03-18 15:58+0000\n" + +#: glade/delugegtk.glade:93 +msgid "Estimated Time Remaining:" +msgstr "" + +#: glade/delugegtk.glade:131 +msgid "Peers:" +msgstr "" + +#: glade/delugegtk.glade:145 +msgid "Seeders:" +msgstr "" + +#: glade/delugegtk.glade:157 +msgid "Total Size:" +msgstr "" + +#: glade/delugegtk.glade:169 +msgid "Total Downloaded:" +msgstr "" + +#: glade/delugegtk.glade:181 +msgid "Downloaded this session:" +msgstr "" + +#: glade/delugegtk.glade:229 +msgid "Tracker:" +msgstr "" + +#: glade/delugegtk.glade:241 +msgid "Tracker Response:" +msgstr "" + +#: glade/delugegtk.glade:253 +msgid "Tracker Status:" +msgstr "" + +#: glade/delugegtk.glade:277 +msgid "Next Announce:" +msgstr "" + +#: glade/delugegtk.glade:303 +msgid "Pieces:" +msgstr "" + +#: glade/delugegtk.glade:317 +msgid "Total Uploaded:" +msgstr "" + +#: glade/delugegtk.glade:355 +msgid "Share Ratio:" +msgstr "" + +#: glade/delugegtk.glade:369 +msgid "Uploaded This Session:" +msgstr "" + +#: glade/delugegtk.glade:431 +msgid "Use compact storage allocation:" +msgstr "" + +#: glade/delugegtk.glade:455 +msgid "Download Rate:" +msgstr "" + +#: glade/delugegtk.glade:479 +msgid "Upload Rate:" +msgstr "" + +#: glade/delugegtk.glade:522 glade/delugegtk.glade:746 +msgid "Details" +msgstr "" + +#: glade/delugegtk.glade:549 glade/delugegtk.glade:790 +#: glade/dgtkpopups.glade:37 src/delugegtk.py:352 +msgid "Peers" +msgstr "" + +#: glade/delugegtk.glade:578 +msgid "Files" +msgstr "" + +#: glade/delugegtk.glade:613 +msgid "_File" +msgstr "_Datoteka" + +#: glade/delugegtk.glade:620 glade/delugegtk.glade:901 src/delugegtk.py:174 +msgid "Add Torrent" +msgstr "Dodaj torrent" + +#: glade/delugegtk.glade:636 +msgid "Add Torrent from URL" +msgstr "" + +#: glade/delugegtk.glade:644 glade/delugegtk.glade:914 +#: glade/dgtkpopups.glade:81 +msgid "Remove Torrent" +msgstr "" + +#: glade/delugegtk.glade:658 +msgid "Clear Completed" +msgstr "" + +#: glade/delugegtk.glade:692 +msgid "_Edit" +msgstr "_Uredi" + +#: glade/delugegtk.glade:709 glade/dgtkpopups.glade:221 +#: glade/dgtkpref.glade:697 src/delugegtk.py:177 +msgid "Plugins" +msgstr "" + +#: glade/delugegtk.glade:729 +msgid "_View" +msgstr "" + +#: glade/delugegtk.glade:737 +msgid "Toolbar" +msgstr "" + +#: glade/delugegtk.glade:755 +msgid "Columns" +msgstr "" + +#: glade/delugegtk.glade:763 glade/dgtkpopups.glade:10 src/delugegtk.py:349 +#: src/delugegtk.py:479 +msgid "Size" +msgstr "Veličina" + +#: glade/delugegtk.glade:772 glade/dgtkpopups.glade:19 src/delugegtk.py:350 +msgid "Status" +msgstr "" + +#: glade/delugegtk.glade:781 glade/dgtkpopups.glade:28 src/delugegtk.py:351 +msgid "Seeders" +msgstr "" + +#: glade/delugegtk.glade:799 src/delugegtk.py:353 src/delugegtk.py:477 +#: src/delugegtk.py:692 src/delugegtk.py:704 +msgid "Download" +msgstr "Preuzimanje" + +#: glade/delugegtk.glade:808 src/delugegtk.py:354 src/delugegtk.py:693 +#: src/delugegtk.py:705 +msgid "Upload" +msgstr "Slanje" + +#: glade/delugegtk.glade:817 glade/dgtkpopups.glade:64 src/delugegtk.py:355 +msgid "Time Remaining" +msgstr "" + +#: glade/delugegtk.glade:826 glade/dgtkpopups.glade:73 +msgid "Share Ratio" +msgstr "" + +#: glade/delugegtk.glade:843 +msgid "_Help" +msgstr "_Pomoć" + +#: glade/delugegtk.glade:927 +msgid "Clear Finished Torrents" +msgstr "Makni završene torrente" + +#: glade/delugegtk.glade:949 +msgid "Start / Pause" +msgstr "" + +#: glade/delugegtk.glade:962 +msgid "Queue Torrent Up" +msgstr "Pomakni torrent gore" + +#: glade/delugegtk.glade:975 +msgid "Queue Torrent Down" +msgstr "Pomakni torrent dolje" + +#: glade/dgtkpopups.glade:46 +msgid "Download Speed" +msgstr "" + +#: glade/dgtkpopups.glade:55 +msgid "Upload Speed" +msgstr "" + +#: glade/dgtkpopups.glade:97 +msgid "" +"Are you sure you want to remove the selected torrent(s) from Deluge?" +msgstr "" + +#: glade/dgtkpopups.glade:107 +msgid "Delete downloaded files" +msgstr "Obriši preuzetu datoteku" + +#: glade/dgtkpopups.glade:167 +msgid "Show/Hide" +msgstr "" + +#: glade/dgtkpopups.glade:175 +msgid "Add a Torrent..." +msgstr "" + +#: glade/dgtkpopups.glade:191 src/delugegtk.py:175 +msgid "Clear Finished" +msgstr "Makni završene" + +#: glade/dgtkpopups.glade:212 glade/dgtkpref.glade:672 +msgid "gtk-preferences" +msgstr "" + +#: glade/dgtkpopups.glade:242 +msgid "gtk-quit" +msgstr "" + +#: glade/dgtkpref.glade:8 +msgid "Preferences Dialog" +msgstr "" + +#: glade/dgtkpref.glade:53 +msgid "Enable system tray icon" +msgstr "" + +#: glade/dgtkpref.glade:65 +msgid "Minimize to tray on close" +msgstr "" + +#: glade/dgtkpref.glade:80 +msgid "Options" +msgstr "" + +#: glade/dgtkpref.glade:115 +msgid "Save all downloads to:" +msgstr "Spremi sva preuzimanja u:" + +#: glade/dgtkpref.glade:127 +msgid "Ask me where to save each download" +msgstr "" + +#: glade/dgtkpref.glade:139 +msgid "Save Location" +msgstr "" + +#: glade/dgtkpref.glade:180 +msgid "" +"Stop seeding torrents when\n" +"their share ratio reaches:" +msgstr "" + +#: glade/dgtkpref.glade:190 +msgid "Seeding" +msgstr "" + +#: glade/dgtkpref.glade:209 +msgid "Use compact storage allocation" +msgstr "" + +#: glade/dgtkpref.glade:216 +msgid "Storage" +msgstr "" + +#: glade/dgtkpref.glade:246 +msgid "General" +msgstr "" + +#: glade/dgtkpref.glade:280 +msgid "Try from:" +msgstr "" + +#: glade/dgtkpref.glade:286 +msgid "to:" +msgstr "" + +#: glade/dgtkpref.glade:320 +msgid "Active port:" +msgstr "" + +#: glade/dgtkpref.glade:332 +msgid "0000" +msgstr "" + +#: glade/dgtkpref.glade:343 +msgid "Test Port" +msgstr "" + +#: glade/dgtkpref.glade:357 +msgid "TCP Port" +msgstr "" + +#: glade/dgtkpref.glade:380 +msgid "(-1 is unlimited)" +msgstr "" + +#: glade/dgtkpref.glade:390 glade/dgtkpref.glade:511 +msgid "KB/s" +msgstr "" + +#: glade/dgtkpref.glade:436 +msgid "Maximum Download Rate:" +msgstr "" + +#: glade/dgtkpref.glade:447 +msgid "Upload Slots" +msgstr "" + +#: glade/dgtkpref.glade:458 +msgid "Maximum Connections" +msgstr "" + +#: glade/dgtkpref.glade:485 +msgid "Maximum Upload Rate:" +msgstr "" + +#: glade/dgtkpref.glade:548 +msgid "Bandwidth " +msgstr "" + +#: glade/dgtkpref.glade:576 +msgid "Network" +msgstr "Mreža" + +#: glade/dgtkpref.glade:598 +msgid "gtk-cancel" +msgstr "" + +#: glade/dgtkpref.glade:605 +msgid "gtk-ok" +msgstr "" + +#: glade/dgtkpref.glade:625 +msgid "Plugin Manager" +msgstr "" + +#: glade/dgtkpref.glade:718 +msgid "gtk-close" +msgstr "" + +#: src/dcommon.py:41 src/delugegtk.py:330 +msgid "Infinity" +msgstr "" + +#: src/dcommon.py:105 +msgid "Error: no webbrowser found" +msgstr "" + +#: src/deluge.py:332 +msgid "File was not found" +msgstr "" + +#: src/delugegtk.py:173 +msgid "Show / Hide Window" +msgstr "" + +#: src/delugegtk.py:250 +msgid "Plugin" +msgstr "" + +#: src/delugegtk.py:252 +msgid "Enabled" +msgstr "" + +#: src/delugegtk.py:340 +msgid "Unknown" +msgstr "Nepoznato" + +#: src/delugegtk.py:348 +msgid "Name" +msgstr "Ime" + +#: src/delugegtk.py:356 +msgid "Ratio" +msgstr "" + +#: src/delugegtk.py:465 +msgid "IP Address" +msgstr "" + +#: src/delugegtk.py:466 +msgid "Client" +msgstr "" + +#: src/delugegtk.py:467 +msgid "Percent Complete" +msgstr "" + +#: src/delugegtk.py:468 +msgid "Download Rate" +msgstr "" + +#: src/delugegtk.py:469 +msgid "Upload Rate" +msgstr "" + +#: src/delugegtk.py:478 +msgid "Filename" +msgstr "" + +#: src/delugegtk.py:480 +msgid "Offset" +msgstr "" + +#: src/delugegtk.py:668 +msgid "" +"For some reason, the previous state could not be loaded, so a blank state " +"has been loaded for you." +msgstr "" + +#: src/delugegtk.py:670 +msgid "Would you like to attempt to reload the previous session's downloads?" +msgstr "" + +#: src/delugegtk.py:679 +msgid "Choose the download directory for" +msgstr "" + +#: src/delugegtk.py:692 src/delugegtk.py:704 +msgid "Connections" +msgstr "Veze" + +#: src/delugegtk.py:703 +msgid "Deluge Bittorrent Client" +msgstr "Deluge bittorrent client" + +#: src/delugegtk.py:891 +msgid "Add torrent from URL" +msgstr "" + +#: src/delugegtk.py:895 +msgid "Enter the URL of the .torrent to download" +msgstr "" + +#: src/delugegtk.py:959 +msgid "Warning - all downloaded files for this torrent will be deleted!" +msgstr "" + +#: src/dgtk.py:71 +msgid "Choose a .torrent file" +msgstr "" + +#: src/dgtk.py:76 +msgid "Torrent files" +msgstr "" + +#: src/dgtk.py:80 +msgid "All files" +msgstr "Sve datoteke" + +#: src/dgtk.py:97 +msgid "Choose a download directory" +msgstr "" \ No newline at end of file diff --git a/encryption/po/hu.po b/encryption/po/hu.po new file mode 100644 index 000000000..b69b31a41 --- /dev/null +++ b/encryption/po/hu.po @@ -0,0 +1,468 @@ +# Hungarian translation for deluge +# Copyright (c) 2006 Rosetta Contributors and Canonical Ltd 2006 +# This file is distributed under the same license as the deluge package. +# FIRST AUTHOR , 2006. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: deluge\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2007-03-06 16:19-0500\n" +"PO-Revision-Date: 2007-02-24 01:27+0000\n" +"Last-Translator: Kateikyoushii \n" +"Language-Team: Hungarian \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Rosetta-Export-Date: 2007-03-18 15:58+0000\n" + +#: glade/delugegtk.glade:93 +msgid "Estimated Time Remaining:" +msgstr "" + +#: glade/delugegtk.glade:131 +msgid "Peers:" +msgstr "" + +#: glade/delugegtk.glade:145 +msgid "Seeders:" +msgstr "" + +#: glade/delugegtk.glade:157 +msgid "Total Size:" +msgstr "Teljes méret:" + +#: glade/delugegtk.glade:169 +msgid "Total Downloaded:" +msgstr "Összesen letöltve:" + +#: glade/delugegtk.glade:181 +msgid "Downloaded this session:" +msgstr "Most letöltve:" + +#: glade/delugegtk.glade:229 +msgid "Tracker:" +msgstr "Tracker:" + +#: glade/delugegtk.glade:241 +msgid "Tracker Response:" +msgstr "Tracker válasza:" + +#: glade/delugegtk.glade:253 +msgid "Tracker Status:" +msgstr "Tracker állapota:" + +#: glade/delugegtk.glade:277 +msgid "Next Announce:" +msgstr "Következő bejelentés:" + +#: glade/delugegtk.glade:303 +msgid "Pieces:" +msgstr "Darabok:" + +#: glade/delugegtk.glade:317 +msgid "Total Uploaded:" +msgstr "Összesen feltöltve:" + +#: glade/delugegtk.glade:355 +msgid "Share Ratio:" +msgstr "Megosztási arány:" + +#: glade/delugegtk.glade:369 +msgid "Uploaded This Session:" +msgstr "Most feltöltve:" + +#: glade/delugegtk.glade:431 +msgid "Use compact storage allocation:" +msgstr "Kompakt helyfoglalás használata" + +#: glade/delugegtk.glade:455 +msgid "Download Rate:" +msgstr "" + +#: glade/delugegtk.glade:479 +msgid "Upload Rate:" +msgstr "" + +#: glade/delugegtk.glade:522 glade/delugegtk.glade:746 +msgid "Details" +msgstr "" + +#: glade/delugegtk.glade:549 glade/delugegtk.glade:790 +#: glade/dgtkpopups.glade:37 src/delugegtk.py:352 +msgid "Peers" +msgstr "Ügyfelek" + +#: glade/delugegtk.glade:578 +msgid "Files" +msgstr "" + +#: glade/delugegtk.glade:613 +msgid "_File" +msgstr "Fájl" + +#: glade/delugegtk.glade:620 glade/delugegtk.glade:901 src/delugegtk.py:174 +msgid "Add Torrent" +msgstr "Torrent hozzáadása" + +#: glade/delugegtk.glade:636 +msgid "Add Torrent from URL" +msgstr "" + +#: glade/delugegtk.glade:644 glade/delugegtk.glade:914 +#: glade/dgtkpopups.glade:81 +msgid "Remove Torrent" +msgstr "Eltávolítás" + +#: glade/delugegtk.glade:658 +msgid "Clear Completed" +msgstr "" + +#: glade/delugegtk.glade:692 +msgid "_Edit" +msgstr "Szerkesztés" + +#: glade/delugegtk.glade:709 glade/dgtkpopups.glade:221 +#: glade/dgtkpref.glade:697 src/delugegtk.py:177 +msgid "Plugins" +msgstr "" + +#: glade/delugegtk.glade:729 +msgid "_View" +msgstr "" + +#: glade/delugegtk.glade:737 +msgid "Toolbar" +msgstr "" + +#: glade/delugegtk.glade:755 +msgid "Columns" +msgstr "" + +#: glade/delugegtk.glade:763 glade/dgtkpopups.glade:10 src/delugegtk.py:349 +#: src/delugegtk.py:479 +msgid "Size" +msgstr "Méret" + +#: glade/delugegtk.glade:772 glade/dgtkpopups.glade:19 src/delugegtk.py:350 +msgid "Status" +msgstr "" + +#: glade/delugegtk.glade:781 glade/dgtkpopups.glade:28 src/delugegtk.py:351 +msgid "Seeders" +msgstr "Készforrás" + +#: glade/delugegtk.glade:799 src/delugegtk.py:353 src/delugegtk.py:477 +#: src/delugegtk.py:692 src/delugegtk.py:704 +msgid "Download" +msgstr "Letöltés" + +#: glade/delugegtk.glade:808 src/delugegtk.py:354 src/delugegtk.py:693 +#: src/delugegtk.py:705 +msgid "Upload" +msgstr "Feltöltés" + +#: glade/delugegtk.glade:817 glade/dgtkpopups.glade:64 src/delugegtk.py:355 +msgid "Time Remaining" +msgstr "" + +#: glade/delugegtk.glade:826 glade/dgtkpopups.glade:73 +msgid "Share Ratio" +msgstr "" + +#: glade/delugegtk.glade:843 +msgid "_Help" +msgstr "_Segítség" + +#: glade/delugegtk.glade:927 +msgid "Clear Finished Torrents" +msgstr "Befejezett torrentek törlése" + +#: glade/delugegtk.glade:949 +msgid "Start / Pause" +msgstr "" + +#: glade/delugegtk.glade:962 +msgid "Queue Torrent Up" +msgstr "Előbbi letöltés" + +#: glade/delugegtk.glade:975 +msgid "Queue Torrent Down" +msgstr "Későbbi letöltés" + +#: glade/dgtkpopups.glade:46 +msgid "Download Speed" +msgstr "" + +#: glade/dgtkpopups.glade:55 +msgid "Upload Speed" +msgstr "" + +#: glade/dgtkpopups.glade:97 +msgid "" +"Are you sure you want to remove the selected torrent(s) from Deluge?" +msgstr "" +"Valóban el szeretnéd távolítani a kiválasztott torrente(ke)t a " +"programból?" + +#: glade/dgtkpopups.glade:107 +msgid "Delete downloaded files" +msgstr "Letöltött fájlok törlése" + +#: glade/dgtkpopups.glade:167 +msgid "Show/Hide" +msgstr "" + +#: glade/dgtkpopups.glade:175 +msgid "Add a Torrent..." +msgstr "" + +#: glade/dgtkpopups.glade:191 src/delugegtk.py:175 +msgid "Clear Finished" +msgstr "Befejezettek törlése" + +#: glade/dgtkpopups.glade:212 glade/dgtkpref.glade:672 +msgid "gtk-preferences" +msgstr "Beállítások" + +#: glade/dgtkpopups.glade:242 +msgid "gtk-quit" +msgstr "" + +#: glade/dgtkpref.glade:8 +msgid "Preferences Dialog" +msgstr "" + +#: glade/dgtkpref.glade:53 +msgid "Enable system tray icon" +msgstr "" + +#: glade/dgtkpref.glade:65 +msgid "Minimize to tray on close" +msgstr "" + +#: glade/dgtkpref.glade:80 +msgid "Options" +msgstr "" + +#: glade/dgtkpref.glade:115 +msgid "Save all downloads to:" +msgstr "Összes mentése ide:" + +#: glade/dgtkpref.glade:127 +msgid "Ask me where to save each download" +msgstr "" + +#: glade/dgtkpref.glade:139 +msgid "Save Location" +msgstr "" + +#: glade/dgtkpref.glade:180 +msgid "" +"Stop seeding torrents when\n" +"their share ratio reaches:" +msgstr "" + +#: glade/dgtkpref.glade:190 +msgid "Seeding" +msgstr "Megosztva" + +#: glade/dgtkpref.glade:209 +msgid "Use compact storage allocation" +msgstr "" + +#: glade/dgtkpref.glade:216 +msgid "Storage" +msgstr "" + +#: glade/dgtkpref.glade:246 +msgid "General" +msgstr "" + +#: glade/dgtkpref.glade:280 +msgid "Try from:" +msgstr "Próbálja ettől:" + +#: glade/dgtkpref.glade:286 +msgid "to:" +msgstr "" + +#: glade/dgtkpref.glade:320 +msgid "Active port:" +msgstr "Aktív port:" + +#: glade/dgtkpref.glade:332 +msgid "0000" +msgstr "" + +#: glade/dgtkpref.glade:343 +msgid "Test Port" +msgstr "Port tesztelése" + +#: glade/dgtkpref.glade:357 +msgid "TCP Port" +msgstr "" + +#: glade/dgtkpref.glade:380 +msgid "(-1 is unlimited)" +msgstr "" + +#: glade/dgtkpref.glade:390 glade/dgtkpref.glade:511 +msgid "KB/s" +msgstr "" + +#: glade/dgtkpref.glade:436 +msgid "Maximum Download Rate:" +msgstr "Maximális letöltési sebesség:" + +#: glade/dgtkpref.glade:447 +msgid "Upload Slots" +msgstr "" + +#: glade/dgtkpref.glade:458 +msgid "Maximum Connections" +msgstr "" + +#: glade/dgtkpref.glade:485 +msgid "Maximum Upload Rate:" +msgstr "Maximális feltöltési sebesség:" + +#: glade/dgtkpref.glade:548 +msgid "Bandwidth " +msgstr "" + +#: glade/dgtkpref.glade:576 +msgid "Network" +msgstr "Hálózat" + +#: glade/dgtkpref.glade:598 +#, fuzzy +msgid "gtk-cancel" +msgstr "Mégse" + +#: glade/dgtkpref.glade:605 +msgid "gtk-ok" +msgstr "OK" + +#: glade/dgtkpref.glade:625 +msgid "Plugin Manager" +msgstr "Plugin kezelés" + +#: glade/dgtkpref.glade:718 +msgid "gtk-close" +msgstr "" + +#: src/dcommon.py:41 src/delugegtk.py:330 +msgid "Infinity" +msgstr "" + +#: src/dcommon.py:105 +msgid "Error: no webbrowser found" +msgstr "" + +#: src/deluge.py:332 +msgid "File was not found" +msgstr "" + +#: src/delugegtk.py:173 +msgid "Show / Hide Window" +msgstr "" + +#: src/delugegtk.py:250 +msgid "Plugin" +msgstr "" + +#: src/delugegtk.py:252 +msgid "Enabled" +msgstr "" + +#: src/delugegtk.py:340 +msgid "Unknown" +msgstr "Ismeretlen" + +#: src/delugegtk.py:348 +msgid "Name" +msgstr "Név" + +#: src/delugegtk.py:356 +msgid "Ratio" +msgstr "" + +#: src/delugegtk.py:465 +msgid "IP Address" +msgstr "" + +#: src/delugegtk.py:466 +msgid "Client" +msgstr "Ügyfél" + +#: src/delugegtk.py:467 +msgid "Percent Complete" +msgstr "" + +#: src/delugegtk.py:468 +msgid "Download Rate" +msgstr "" + +#: src/delugegtk.py:469 +msgid "Upload Rate" +msgstr "" + +#: src/delugegtk.py:478 +msgid "Filename" +msgstr "" + +#: src/delugegtk.py:480 +msgid "Offset" +msgstr "" + +#: src/delugegtk.py:668 +msgid "" +"For some reason, the previous state could not be loaded, so a blank state " +"has been loaded for you." +msgstr "" + +#: src/delugegtk.py:670 +msgid "Would you like to attempt to reload the previous session's downloads?" +msgstr "" + +#: src/delugegtk.py:679 +msgid "Choose the download directory for" +msgstr "" + +#: src/delugegtk.py:692 src/delugegtk.py:704 +msgid "Connections" +msgstr "Kapcsolatok" + +#: src/delugegtk.py:703 +msgid "Deluge Bittorrent Client" +msgstr "Deluge Bittorrent Kliens" + +#: src/delugegtk.py:891 +msgid "Add torrent from URL" +msgstr "" + +#: src/delugegtk.py:895 +msgid "Enter the URL of the .torrent to download" +msgstr "" + +#: src/delugegtk.py:959 +msgid "Warning - all downloaded files for this torrent will be deleted!" +msgstr "Figyelem - a torrenthez tartozó összes fájl törölve lesz!" + +#: src/dgtk.py:71 +msgid "Choose a .torrent file" +msgstr "" + +#: src/dgtk.py:76 +msgid "Torrent files" +msgstr "" + +#: src/dgtk.py:80 +msgid "All files" +msgstr "Minden fájl" + +#: src/dgtk.py:97 +msgid "Choose a download directory" +msgstr "" \ No newline at end of file diff --git a/encryption/po/it.po b/encryption/po/it.po new file mode 100644 index 000000000..08139acf9 --- /dev/null +++ b/encryption/po/it.po @@ -0,0 +1,469 @@ +# Italian translation for deluge +# Copyright (c) 2006 Rosetta Contributors and Canonical Ltd 2006 +# This file is distributed under the same license as the deluge package. +# FIRST AUTHOR , 2006. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: deluge\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2007-03-06 16:19-0500\n" +"PO-Revision-Date: 2007-03-01 18:56+0000\n" +"Last-Translator: Andrea Ratto \n" +"Language-Team: Italian \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Rosetta-Export-Date: 2007-03-18 15:58+0000\n" + +#: glade/delugegtk.glade:93 +msgid "Estimated Time Remaining:" +msgstr "Tempo Rimasto Stimato:" + +#: glade/delugegtk.glade:131 +msgid "Peers:" +msgstr "Peer" + +#: glade/delugegtk.glade:145 +msgid "Seeders:" +msgstr "Seeder" + +#: glade/delugegtk.glade:157 +msgid "Total Size:" +msgstr "Dimensione Totale:" + +#: glade/delugegtk.glade:169 +msgid "Total Downloaded:" +msgstr "Totale Scaricato:" + +#: glade/delugegtk.glade:181 +msgid "Downloaded this session:" +msgstr "Scaricati in questa sessione:" + +#: glade/delugegtk.glade:229 +msgid "Tracker:" +msgstr "Tracker:" + +#: glade/delugegtk.glade:241 +msgid "Tracker Response:" +msgstr "Risposta del Tracker:" + +#: glade/delugegtk.glade:253 +msgid "Tracker Status:" +msgstr "Stato del Tracker:" + +#: glade/delugegtk.glade:277 +msgid "Next Announce:" +msgstr "Prossimo annuncio:" + +#: glade/delugegtk.glade:303 +msgid "Pieces:" +msgstr "Parti:" + +#: glade/delugegtk.glade:317 +msgid "Total Uploaded:" +msgstr "Totale Inviato:" + +#: glade/delugegtk.glade:355 +msgid "Share Ratio:" +msgstr "Rapporto di Condivisione:" + +#: glade/delugegtk.glade:369 +msgid "Uploaded This Session:" +msgstr "Inviato in questa sessione:" + +#: glade/delugegtk.glade:431 +msgid "Use compact storage allocation:" +msgstr "Usa allocazione compatta per i file:" + +#: glade/delugegtk.glade:455 +msgid "Download Rate:" +msgstr "Velocità di Download:" + +#: glade/delugegtk.glade:479 +msgid "Upload Rate:" +msgstr "Velocità di Upload:" + +#: glade/delugegtk.glade:522 glade/delugegtk.glade:746 +msgid "Details" +msgstr "Dettagli" + +#: glade/delugegtk.glade:549 glade/delugegtk.glade:790 +#: glade/dgtkpopups.glade:37 src/delugegtk.py:352 +msgid "Peers" +msgstr "Incompleti" + +#: glade/delugegtk.glade:578 +msgid "Files" +msgstr "File" + +#: glade/delugegtk.glade:613 +msgid "_File" +msgstr "_File" + +#: glade/delugegtk.glade:620 glade/delugegtk.glade:901 src/delugegtk.py:174 +msgid "Add Torrent" +msgstr "Aggiungi Torrent" + +#: glade/delugegtk.glade:636 +msgid "Add Torrent from URL" +msgstr "Aggiungi Torrent da URL" + +#: glade/delugegtk.glade:644 glade/delugegtk.glade:914 +#: glade/dgtkpopups.glade:81 +msgid "Remove Torrent" +msgstr "Rimuovi Torrent" + +#: glade/delugegtk.glade:658 +msgid "Clear Completed" +msgstr "Rimuovi i download completati" + +#: glade/delugegtk.glade:692 +msgid "_Edit" +msgstr "_Modifica" + +#: glade/delugegtk.glade:709 glade/dgtkpopups.glade:221 +#: glade/dgtkpref.glade:697 src/delugegtk.py:177 +msgid "Plugins" +msgstr "Plugin" + +#: glade/delugegtk.glade:729 +msgid "_View" +msgstr "_Visualizza" + +#: glade/delugegtk.glade:737 +msgid "Toolbar" +msgstr "Barra degli strumenti" + +#: glade/delugegtk.glade:755 +msgid "Columns" +msgstr "Colonne" + +#: glade/delugegtk.glade:763 glade/dgtkpopups.glade:10 src/delugegtk.py:349 +#: src/delugegtk.py:479 +msgid "Size" +msgstr "Dimensione" + +#: glade/delugegtk.glade:772 glade/dgtkpopups.glade:19 src/delugegtk.py:350 +msgid "Status" +msgstr "Stato" + +#: glade/delugegtk.glade:781 glade/dgtkpopups.glade:28 src/delugegtk.py:351 +msgid "Seeders" +msgstr "Fonti" + +#: glade/delugegtk.glade:799 src/delugegtk.py:353 src/delugegtk.py:477 +#: src/delugegtk.py:692 src/delugegtk.py:704 +msgid "Download" +msgstr "Ricezione" + +#: glade/delugegtk.glade:808 src/delugegtk.py:354 src/delugegtk.py:693 +#: src/delugegtk.py:705 +msgid "Upload" +msgstr "Invio" + +#: glade/delugegtk.glade:817 glade/dgtkpopups.glade:64 src/delugegtk.py:355 +msgid "Time Remaining" +msgstr "Tempo Rimanente" + +#: glade/delugegtk.glade:826 glade/dgtkpopups.glade:73 +msgid "Share Ratio" +msgstr "Rapporto di Condivisione" + +#: glade/delugegtk.glade:843 +msgid "_Help" +msgstr "_Aiuto" + +#: glade/delugegtk.glade:927 +msgid "Clear Finished Torrents" +msgstr "Elimina i Torrent Completati" + +#: glade/delugegtk.glade:949 +msgid "Start / Pause" +msgstr "Avvia / Pausa" + +#: glade/delugegtk.glade:962 +msgid "Queue Torrent Up" +msgstr "Sposta il Torrent più in alto nella coda" + +#: glade/delugegtk.glade:975 +msgid "Queue Torrent Down" +msgstr "Sposta il Torrent più in basso nella coda" + +#: glade/dgtkpopups.glade:46 +msgid "Download Speed" +msgstr "Velocità di Ricezione" + +#: glade/dgtkpopups.glade:55 +msgid "Upload Speed" +msgstr "Velocità di Invio" + +#: glade/dgtkpopups.glade:97 +msgid "" +"Are you sure you want to remove the selected torrent(s) from Deluge?" +msgstr "" +"Sei sicuro di voler rimuovere i torrent selezionati da Deluge?" + +#: glade/dgtkpopups.glade:107 +msgid "Delete downloaded files" +msgstr "Cancella i file scaricati" + +#: glade/dgtkpopups.glade:167 +msgid "Show/Hide" +msgstr "Mostra/Nascondi" + +#: glade/dgtkpopups.glade:175 +msgid "Add a Torrent..." +msgstr "Aggiungi un Torrent..." + +#: glade/dgtkpopups.glade:191 src/delugegtk.py:175 +msgid "Clear Finished" +msgstr "Elimina Completati" + +#: glade/dgtkpopups.glade:212 glade/dgtkpref.glade:672 +msgid "gtk-preferences" +msgstr "Preferenze GTK" + +#: glade/dgtkpopups.glade:242 +msgid "gtk-quit" +msgstr "" + +#: glade/dgtkpref.glade:8 +msgid "Preferences Dialog" +msgstr "Finestra delle Impostazioni" + +#: glade/dgtkpref.glade:53 +msgid "Enable system tray icon" +msgstr "Abilita icona del vassoio di sistema" + +#: glade/dgtkpref.glade:65 +msgid "Minimize to tray on close" +msgstr "Minimizza nel vassoio all'uscita" + +#: glade/dgtkpref.glade:80 +msgid "Options" +msgstr "Opzioni" + +#: glade/dgtkpref.glade:115 +msgid "Save all downloads to:" +msgstr "Salva tutti i file scaricati in:" + +#: glade/dgtkpref.glade:127 +msgid "Ask me where to save each download" +msgstr "Chiedimi dove salvare ogni download" + +#: glade/dgtkpref.glade:139 +msgid "Save Location" +msgstr "Cartella di Salvataggio" + +#: glade/dgtkpref.glade:180 +msgid "" +"Stop seeding torrents when\n" +"their share ratio reaches:" +msgstr "" +"Smetti di condividere i torrent quando\n" +"il loro share ratio raggiunge:" + +#: glade/dgtkpref.glade:190 +msgid "Seeding" +msgstr "In Condivisione" + +#: glade/dgtkpref.glade:209 +msgid "Use compact storage allocation" +msgstr "Usa l'allocazione compatta dello spazio" + +#: glade/dgtkpref.glade:216 +msgid "Storage" +msgstr "Spazio" + +#: glade/dgtkpref.glade:246 +msgid "General" +msgstr "" + +#: glade/dgtkpref.glade:280 +msgid "Try from:" +msgstr "Prova da:" + +#: glade/dgtkpref.glade:286 +msgid "to:" +msgstr "a:" + +#: glade/dgtkpref.glade:320 +msgid "Active port:" +msgstr "Porta in uso:" + +#: glade/dgtkpref.glade:332 +msgid "0000" +msgstr "" + +#: glade/dgtkpref.glade:343 +msgid "Test Port" +msgstr "Esegui un test sulla porta" + +#: glade/dgtkpref.glade:357 +msgid "TCP Port" +msgstr "Porta TCP" + +#: glade/dgtkpref.glade:380 +msgid "(-1 is unlimited)" +msgstr "(-1 per illimitato)" + +#: glade/dgtkpref.glade:390 glade/dgtkpref.glade:511 +msgid "KB/s" +msgstr "KB/s" + +#: glade/dgtkpref.glade:436 +msgid "Maximum Download Rate:" +msgstr "Velocità massima di download:" + +#: glade/dgtkpref.glade:447 +msgid "Upload Slots" +msgstr "Posti in invio" + +#: glade/dgtkpref.glade:458 +msgid "Maximum Connections" +msgstr "" + +#: glade/dgtkpref.glade:485 +msgid "Maximum Upload Rate:" +msgstr "Velocità massima di upload:" + +#: glade/dgtkpref.glade:548 +msgid "Bandwidth " +msgstr "Banda " + +#: glade/dgtkpref.glade:576 +msgid "Network" +msgstr "Rete" + +#: glade/dgtkpref.glade:598 +msgid "gtk-cancel" +msgstr "gtk-cancel" + +#: glade/dgtkpref.glade:605 +msgid "gtk-ok" +msgstr "gtk-ok" + +#: glade/dgtkpref.glade:625 +msgid "Plugin Manager" +msgstr "Gestione Plugin" + +#: glade/dgtkpref.glade:718 +msgid "gtk-close" +msgstr "" + +#: src/dcommon.py:41 src/delugegtk.py:330 +msgid "Infinity" +msgstr "Infinito" + +#: src/dcommon.py:105 +msgid "Error: no webbrowser found" +msgstr "Errore: nessun browser trovato" + +#: src/deluge.py:332 +msgid "File was not found" +msgstr "" + +#: src/delugegtk.py:173 +msgid "Show / Hide Window" +msgstr "Mostra / Nascondi Finestra" + +#: src/delugegtk.py:250 +msgid "Plugin" +msgstr "Plugin" + +#: src/delugegtk.py:252 +msgid "Enabled" +msgstr "Abilitato" + +#: src/delugegtk.py:340 +msgid "Unknown" +msgstr "Sconosciuto" + +#: src/delugegtk.py:348 +msgid "Name" +msgstr "Nome" + +#: src/delugegtk.py:356 +msgid "Ratio" +msgstr "Rapporto" + +#: src/delugegtk.py:465 +msgid "IP Address" +msgstr "Indirizzo IP" + +#: src/delugegtk.py:466 +msgid "Client" +msgstr "Client" + +#: src/delugegtk.py:467 +msgid "Percent Complete" +msgstr "Percentuale Completata" + +#: src/delugegtk.py:468 +msgid "Download Rate" +msgstr "Velocità di Download" + +#: src/delugegtk.py:469 +msgid "Upload Rate" +msgstr "Velocità di Upload" + +#: src/delugegtk.py:478 +msgid "Filename" +msgstr "Nome del file" + +#: src/delugegtk.py:480 +msgid "Offset" +msgstr "Scostamento" + +#: src/delugegtk.py:668 +msgid "" +"For some reason, the previous state could not be loaded, so a blank state " +"has been loaded for you." +msgstr "" + +#: src/delugegtk.py:670 +msgid "Would you like to attempt to reload the previous session's downloads?" +msgstr "" + +#: src/delugegtk.py:679 +msgid "Choose the download directory for" +msgstr "" + +#: src/delugegtk.py:692 src/delugegtk.py:704 +msgid "Connections" +msgstr "Connessioni" + +#: src/delugegtk.py:703 +msgid "Deluge Bittorrent Client" +msgstr "Deluge Bittorrent Client" + +#: src/delugegtk.py:891 +msgid "Add torrent from URL" +msgstr "Aggiungi torrent da URL" + +#: src/delugegtk.py:895 +msgid "Enter the URL of the .torrent to download" +msgstr "Immettere l'URL del file .torrent da scaricare" + +#: src/delugegtk.py:959 +msgid "Warning - all downloaded files for this torrent will be deleted!" +msgstr "" +"Attenzione - tutti i file scaricati per questo torrent saranno cancellati!" + +#: src/dgtk.py:71 +msgid "Choose a .torrent file" +msgstr "Seleziona un file .torrent" + +#: src/dgtk.py:76 +msgid "Torrent files" +msgstr "File Torrent" + +#: src/dgtk.py:80 +msgid "All files" +msgstr "Tutti i Files" + +#: src/dgtk.py:97 +msgid "Choose a download directory" +msgstr "Seleziona dove salvare i file" \ No newline at end of file diff --git a/encryption/po/ja.po b/encryption/po/ja.po new file mode 100644 index 000000000..96cf26492 --- /dev/null +++ b/encryption/po/ja.po @@ -0,0 +1,465 @@ +# Japanese translation for deluge +# Copyright (c) 2007 Rosetta Contributors and Canonical Ltd 2007 +# This file is distributed under the same license as the deluge package. +# FIRST AUTHOR , 2007. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: deluge\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2007-03-06 16:19-0500\n" +"PO-Revision-Date: 2007-03-04 06:02+0000\n" +"Last-Translator: sanafrato \n" +"Language-Team: Japanese \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Rosetta-Export-Date: 2007-03-18 15:58+0000\n" + +#: glade/delugegtk.glade:93 +msgid "Estimated Time Remaining:" +msgstr "" + +#: glade/delugegtk.glade:131 +msgid "Peers:" +msgstr "ピア:" + +#: glade/delugegtk.glade:145 +msgid "Seeders:" +msgstr "シーダー:" + +#: glade/delugegtk.glade:157 +msgid "Total Size:" +msgstr "" + +#: glade/delugegtk.glade:169 +msgid "Total Downloaded:" +msgstr "" + +#: glade/delugegtk.glade:181 +msgid "Downloaded this session:" +msgstr "" + +#: glade/delugegtk.glade:229 +msgid "Tracker:" +msgstr "トラッカー:" + +#: glade/delugegtk.glade:241 +msgid "Tracker Response:" +msgstr "" + +#: glade/delugegtk.glade:253 +msgid "Tracker Status:" +msgstr "" + +#: glade/delugegtk.glade:277 +msgid "Next Announce:" +msgstr "" + +#: glade/delugegtk.glade:303 +msgid "Pieces:" +msgstr "ピース数:" + +#: glade/delugegtk.glade:317 +msgid "Total Uploaded:" +msgstr "" + +#: glade/delugegtk.glade:355 +msgid "Share Ratio:" +msgstr "" + +#: glade/delugegtk.glade:369 +msgid "Uploaded This Session:" +msgstr "" + +#: glade/delugegtk.glade:431 +msgid "Use compact storage allocation:" +msgstr "" + +#: glade/delugegtk.glade:455 +msgid "Download Rate:" +msgstr "" + +#: glade/delugegtk.glade:479 +msgid "Upload Rate:" +msgstr "" + +#: glade/delugegtk.glade:522 glade/delugegtk.glade:746 +msgid "Details" +msgstr "詳細" + +#: glade/delugegtk.glade:549 glade/delugegtk.glade:790 +#: glade/dgtkpopups.glade:37 src/delugegtk.py:352 +msgid "Peers" +msgstr "ピア" + +#: glade/delugegtk.glade:578 +msgid "Files" +msgstr "ファイル" + +#: glade/delugegtk.glade:613 +msgid "_File" +msgstr "_ファイル" + +#: glade/delugegtk.glade:620 glade/delugegtk.glade:901 src/delugegtk.py:174 +msgid "Add Torrent" +msgstr "Torrentを追加" + +#: glade/delugegtk.glade:636 +msgid "Add Torrent from URL" +msgstr "URLからTorrentを追加" + +#: glade/delugegtk.glade:644 glade/delugegtk.glade:914 +#: glade/dgtkpopups.glade:81 +msgid "Remove Torrent" +msgstr "Torrentを削除" + +#: glade/delugegtk.glade:658 +msgid "Clear Completed" +msgstr "完全にクリア" + +#: glade/delugegtk.glade:692 +msgid "_Edit" +msgstr "_編集" + +#: glade/delugegtk.glade:709 glade/dgtkpopups.glade:221 +#: glade/dgtkpref.glade:697 src/delugegtk.py:177 +msgid "Plugins" +msgstr "プラグイン" + +#: glade/delugegtk.glade:729 +msgid "_View" +msgstr "_ビュー" + +#: glade/delugegtk.glade:737 +msgid "Toolbar" +msgstr "ツールバー" + +#: glade/delugegtk.glade:755 +msgid "Columns" +msgstr "カラム" + +#: glade/delugegtk.glade:763 glade/dgtkpopups.glade:10 src/delugegtk.py:349 +#: src/delugegtk.py:479 +msgid "Size" +msgstr "サイズ" + +#: glade/delugegtk.glade:772 glade/dgtkpopups.glade:19 src/delugegtk.py:350 +msgid "Status" +msgstr "状態" + +#: glade/delugegtk.glade:781 glade/dgtkpopups.glade:28 src/delugegtk.py:351 +msgid "Seeders" +msgstr "シーダー" + +#: glade/delugegtk.glade:799 src/delugegtk.py:353 src/delugegtk.py:477 +#: src/delugegtk.py:692 src/delugegtk.py:704 +msgid "Download" +msgstr "ダウンロード" + +#: glade/delugegtk.glade:808 src/delugegtk.py:354 src/delugegtk.py:693 +#: src/delugegtk.py:705 +msgid "Upload" +msgstr "アップロード" + +#: glade/delugegtk.glade:817 glade/dgtkpopups.glade:64 src/delugegtk.py:355 +msgid "Time Remaining" +msgstr "残り時間" + +#: glade/delugegtk.glade:826 glade/dgtkpopups.glade:73 +msgid "Share Ratio" +msgstr "" + +#: glade/delugegtk.glade:843 +msgid "_Help" +msgstr "_ヘルプ" + +#: glade/delugegtk.glade:927 +msgid "Clear Finished Torrents" +msgstr "終了したTorrentをクリア" + +#: glade/delugegtk.glade:949 +msgid "Start / Pause" +msgstr "スタート / 休止" + +#: glade/delugegtk.glade:962 +msgid "Queue Torrent Up" +msgstr "" + +#: glade/delugegtk.glade:975 +msgid "Queue Torrent Down" +msgstr "" + +#: glade/dgtkpopups.glade:46 +msgid "Download Speed" +msgstr "ダウン速度" + +#: glade/dgtkpopups.glade:55 +msgid "Upload Speed" +msgstr "アップ速度" + +#: glade/dgtkpopups.glade:97 +msgid "" +"Are you sure you want to remove the selected torrent(s) from Deluge?" +msgstr "" + +#: glade/dgtkpopups.glade:107 +msgid "Delete downloaded files" +msgstr "ダウンロードしたファイルを削除" + +#: glade/dgtkpopups.glade:167 +msgid "Show/Hide" +msgstr "表示/非表示" + +#: glade/dgtkpopups.glade:175 +msgid "Add a Torrent..." +msgstr "Torrentを追加..." + +#: glade/dgtkpopups.glade:191 src/delugegtk.py:175 +msgid "Clear Finished" +msgstr "" + +#: glade/dgtkpopups.glade:212 glade/dgtkpref.glade:672 +msgid "gtk-preferences" +msgstr "" + +#: glade/dgtkpopups.glade:242 +msgid "gtk-quit" +msgstr "" + +#: glade/dgtkpref.glade:8 +msgid "Preferences Dialog" +msgstr "" + +#: glade/dgtkpref.glade:53 +msgid "Enable system tray icon" +msgstr "システムトレイアイコンを有効" + +#: glade/dgtkpref.glade:65 +msgid "Minimize to tray on close" +msgstr "" + +#: glade/dgtkpref.glade:80 +msgid "Options" +msgstr "オプション" + +#: glade/dgtkpref.glade:115 +msgid "Save all downloads to:" +msgstr "" + +#: glade/dgtkpref.glade:127 +msgid "Ask me where to save each download" +msgstr "" + +#: glade/dgtkpref.glade:139 +msgid "Save Location" +msgstr "保存先" + +#: glade/dgtkpref.glade:180 +msgid "" +"Stop seeding torrents when\n" +"their share ratio reaches:" +msgstr "" + +#: glade/dgtkpref.glade:190 +msgid "Seeding" +msgstr "シード中" + +#: glade/dgtkpref.glade:209 +msgid "Use compact storage allocation" +msgstr "" + +#: glade/dgtkpref.glade:216 +msgid "Storage" +msgstr "ストレージ" + +#: glade/dgtkpref.glade:246 +msgid "General" +msgstr "一般" + +#: glade/dgtkpref.glade:280 +msgid "Try from:" +msgstr "" + +#: glade/dgtkpref.glade:286 +msgid "to:" +msgstr "to:" + +#: glade/dgtkpref.glade:320 +msgid "Active port:" +msgstr "" + +#: glade/dgtkpref.glade:332 +msgid "0000" +msgstr "0000" + +#: glade/dgtkpref.glade:343 +msgid "Test Port" +msgstr "テストポート" + +#: glade/dgtkpref.glade:357 +msgid "TCP Port" +msgstr "TCP ポート" + +#: glade/dgtkpref.glade:380 +msgid "(-1 is unlimited)" +msgstr "" + +#: glade/dgtkpref.glade:390 glade/dgtkpref.glade:511 +msgid "KB/s" +msgstr "KB/s" + +#: glade/dgtkpref.glade:436 +msgid "Maximum Download Rate:" +msgstr "" + +#: glade/dgtkpref.glade:447 +msgid "Upload Slots" +msgstr "" + +#: glade/dgtkpref.glade:458 +msgid "Maximum Connections" +msgstr "" + +#: glade/dgtkpref.glade:485 +msgid "Maximum Upload Rate:" +msgstr "" + +#: glade/dgtkpref.glade:548 +msgid "Bandwidth " +msgstr "帯域 " + +#: glade/dgtkpref.glade:576 +msgid "Network" +msgstr "ネットワーク" + +#: glade/dgtkpref.glade:598 +msgid "gtk-cancel" +msgstr "" + +#: glade/dgtkpref.glade:605 +msgid "gtk-ok" +msgstr "" + +#: glade/dgtkpref.glade:625 +msgid "Plugin Manager" +msgstr "プラグインマネージャー" + +#: glade/dgtkpref.glade:718 +msgid "gtk-close" +msgstr "" + +#: src/dcommon.py:41 src/delugegtk.py:330 +msgid "Infinity" +msgstr "∞" + +#: src/dcommon.py:105 +msgid "Error: no webbrowser found" +msgstr "エラー: ウェブブラウザーが見つかりません" + +#: src/deluge.py:332 +msgid "File was not found" +msgstr "" + +#: src/delugegtk.py:173 +msgid "Show / Hide Window" +msgstr "" + +#: src/delugegtk.py:250 +msgid "Plugin" +msgstr "プラグイン" + +#: src/delugegtk.py:252 +msgid "Enabled" +msgstr "" + +#: src/delugegtk.py:340 +msgid "Unknown" +msgstr "" + +#: src/delugegtk.py:348 +msgid "Name" +msgstr "名前" + +#: src/delugegtk.py:356 +msgid "Ratio" +msgstr "" + +#: src/delugegtk.py:465 +msgid "IP Address" +msgstr "IPアドレス" + +#: src/delugegtk.py:466 +msgid "Client" +msgstr "クライアント" + +#: src/delugegtk.py:467 +msgid "Percent Complete" +msgstr "パーセント完了" + +#: src/delugegtk.py:468 +msgid "Download Rate" +msgstr "" + +#: src/delugegtk.py:469 +msgid "Upload Rate" +msgstr "" + +#: src/delugegtk.py:478 +msgid "Filename" +msgstr "ファイル名" + +#: src/delugegtk.py:480 +msgid "Offset" +msgstr "" + +#: src/delugegtk.py:668 +msgid "" +"For some reason, the previous state could not be loaded, so a blank state " +"has been loaded for you." +msgstr "" + +#: src/delugegtk.py:670 +msgid "Would you like to attempt to reload the previous session's downloads?" +msgstr "" + +#: src/delugegtk.py:679 +msgid "Choose the download directory for" +msgstr "" + +#: src/delugegtk.py:692 src/delugegtk.py:704 +msgid "Connections" +msgstr "接続" + +#: src/delugegtk.py:703 +msgid "Deluge Bittorrent Client" +msgstr "Deluge Bittorrent Client" + +#: src/delugegtk.py:891 +msgid "Add torrent from URL" +msgstr "URLからTorrentを追加" + +#: src/delugegtk.py:895 +msgid "Enter the URL of the .torrent to download" +msgstr "" + +#: src/delugegtk.py:959 +msgid "Warning - all downloaded files for this torrent will be deleted!" +msgstr "" + +#: src/dgtk.py:71 +msgid "Choose a .torrent file" +msgstr ".torrentファイルを選択" + +#: src/dgtk.py:76 +msgid "Torrent files" +msgstr "Torrentファイル" + +#: src/dgtk.py:80 +msgid "All files" +msgstr "すべてのファイル" + +#: src/dgtk.py:97 +msgid "Choose a download directory" +msgstr "保存先を選択" \ No newline at end of file diff --git a/encryption/po/ko.po b/encryption/po/ko.po new file mode 100644 index 000000000..b871253d9 --- /dev/null +++ b/encryption/po/ko.po @@ -0,0 +1,465 @@ +# Korean translation for deluge +# Copyright (c) 2007 Rosetta Contributors and Canonical Ltd 2007 +# This file is distributed under the same license as the deluge package. +# FIRST AUTHOR , 2007. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: deluge\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2007-03-06 16:19-0500\n" +"PO-Revision-Date: 2007-02-13 21:38+0000\n" +"Last-Translator: Sunjae Park \n" +"Language-Team: Korean \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Rosetta-Export-Date: 2007-03-18 15:58+0000\n" + +#: glade/delugegtk.glade:93 +msgid "Estimated Time Remaining:" +msgstr "" + +#: glade/delugegtk.glade:131 +msgid "Peers:" +msgstr "" + +#: glade/delugegtk.glade:145 +msgid "Seeders:" +msgstr "" + +#: glade/delugegtk.glade:157 +msgid "Total Size:" +msgstr "전체 크기:" + +#: glade/delugegtk.glade:169 +msgid "Total Downloaded:" +msgstr "내려받은 총량:" + +#: glade/delugegtk.glade:181 +msgid "Downloaded this session:" +msgstr "이 세션에서 내려받음:" + +#: glade/delugegtk.glade:229 +msgid "Tracker:" +msgstr "추적자:" + +#: glade/delugegtk.glade:241 +msgid "Tracker Response:" +msgstr "추적자 응답:" + +#: glade/delugegtk.glade:253 +msgid "Tracker Status:" +msgstr "추적자 상태:" + +#: glade/delugegtk.glade:277 +msgid "Next Announce:" +msgstr "다음 고지:" + +#: glade/delugegtk.glade:303 +msgid "Pieces:" +msgstr "조각들:" + +#: glade/delugegtk.glade:317 +msgid "Total Uploaded:" +msgstr "올린 총량:" + +#: glade/delugegtk.glade:355 +msgid "Share Ratio:" +msgstr "공유비:" + +#: glade/delugegtk.glade:369 +msgid "Uploaded This Session:" +msgstr "이 세션에서 올림:" + +#: glade/delugegtk.glade:431 +msgid "Use compact storage allocation:" +msgstr "작은 저장공간 할당 방식 사용:" + +#: glade/delugegtk.glade:455 +msgid "Download Rate:" +msgstr "" + +#: glade/delugegtk.glade:479 +msgid "Upload Rate:" +msgstr "" + +#: glade/delugegtk.glade:522 glade/delugegtk.glade:746 +msgid "Details" +msgstr "" + +#: glade/delugegtk.glade:549 glade/delugegtk.glade:790 +#: glade/dgtkpopups.glade:37 src/delugegtk.py:352 +msgid "Peers" +msgstr "또래목록" + +#: glade/delugegtk.glade:578 +msgid "Files" +msgstr "" + +#: glade/delugegtk.glade:613 +msgid "_File" +msgstr "파일(_F)" + +#: glade/delugegtk.glade:620 glade/delugegtk.glade:901 src/delugegtk.py:174 +msgid "Add Torrent" +msgstr "토런트 더하기" + +#: glade/delugegtk.glade:636 +msgid "Add Torrent from URL" +msgstr "" + +#: glade/delugegtk.glade:644 glade/delugegtk.glade:914 +#: glade/dgtkpopups.glade:81 +msgid "Remove Torrent" +msgstr "토런트 삭제" + +#: glade/delugegtk.glade:658 +msgid "Clear Completed" +msgstr "" + +#: glade/delugegtk.glade:692 +msgid "_Edit" +msgstr "편집(_E)" + +#: glade/delugegtk.glade:709 glade/dgtkpopups.glade:221 +#: glade/dgtkpref.glade:697 src/delugegtk.py:177 +msgid "Plugins" +msgstr "" + +#: glade/delugegtk.glade:729 +msgid "_View" +msgstr "" + +#: glade/delugegtk.glade:737 +msgid "Toolbar" +msgstr "" + +#: glade/delugegtk.glade:755 +msgid "Columns" +msgstr "" + +#: glade/delugegtk.glade:763 glade/dgtkpopups.glade:10 src/delugegtk.py:349 +#: src/delugegtk.py:479 +msgid "Size" +msgstr "크기" + +#: glade/delugegtk.glade:772 glade/dgtkpopups.glade:19 src/delugegtk.py:350 +msgid "Status" +msgstr "" + +#: glade/delugegtk.glade:781 glade/dgtkpopups.glade:28 src/delugegtk.py:351 +msgid "Seeders" +msgstr "씨뿌림이목록" + +#: glade/delugegtk.glade:799 src/delugegtk.py:353 src/delugegtk.py:477 +#: src/delugegtk.py:692 src/delugegtk.py:704 +msgid "Download" +msgstr "내려받기" + +#: glade/delugegtk.glade:808 src/delugegtk.py:354 src/delugegtk.py:693 +#: src/delugegtk.py:705 +msgid "Upload" +msgstr "올리기" + +#: glade/delugegtk.glade:817 glade/dgtkpopups.glade:64 src/delugegtk.py:355 +msgid "Time Remaining" +msgstr "" + +#: glade/delugegtk.glade:826 glade/dgtkpopups.glade:73 +msgid "Share Ratio" +msgstr "" + +#: glade/delugegtk.glade:843 +msgid "_Help" +msgstr "도움말(_H)" + +#: glade/delugegtk.glade:927 +msgid "Clear Finished Torrents" +msgstr "완료된 토런트들 비우기" + +#: glade/delugegtk.glade:949 +msgid "Start / Pause" +msgstr "" + +#: glade/delugegtk.glade:962 +msgid "Queue Torrent Up" +msgstr "토런트 대기 순서 올리기" + +#: glade/delugegtk.glade:975 +msgid "Queue Torrent Down" +msgstr "토런트 대기 순서 내리기" + +#: glade/dgtkpopups.glade:46 +msgid "Download Speed" +msgstr "" + +#: glade/dgtkpopups.glade:55 +msgid "Upload Speed" +msgstr "" + +#: glade/dgtkpopups.glade:97 +msgid "" +"Are you sure you want to remove the selected torrent(s) from Deluge?" +msgstr "Deluge로부터 선택된 토런트(들)을 정말 삭제하시겠습니까?" + +#: glade/dgtkpopups.glade:107 +msgid "Delete downloaded files" +msgstr "내려받은 파일들 지우기" + +#: glade/dgtkpopups.glade:167 +msgid "Show/Hide" +msgstr "" + +#: glade/dgtkpopups.glade:175 +msgid "Add a Torrent..." +msgstr "" + +#: glade/dgtkpopups.glade:191 src/delugegtk.py:175 +msgid "Clear Finished" +msgstr "완료된 것 비우기" + +#: glade/dgtkpopups.glade:212 glade/dgtkpref.glade:672 +msgid "gtk-preferences" +msgstr "gtk-preferences" + +#: glade/dgtkpopups.glade:242 +msgid "gtk-quit" +msgstr "" + +#: glade/dgtkpref.glade:8 +msgid "Preferences Dialog" +msgstr "" + +#: glade/dgtkpref.glade:53 +msgid "Enable system tray icon" +msgstr "" + +#: glade/dgtkpref.glade:65 +msgid "Minimize to tray on close" +msgstr "" + +#: glade/dgtkpref.glade:80 +msgid "Options" +msgstr "" + +#: glade/dgtkpref.glade:115 +msgid "Save all downloads to:" +msgstr "모든 내려받기를 여기에 저장:" + +#: glade/dgtkpref.glade:127 +msgid "Ask me where to save each download" +msgstr "" + +#: glade/dgtkpref.glade:139 +msgid "Save Location" +msgstr "" + +#: glade/dgtkpref.glade:180 +msgid "" +"Stop seeding torrents when\n" +"their share ratio reaches:" +msgstr "" + +#: glade/dgtkpref.glade:190 +msgid "Seeding" +msgstr "뿌리는 중" + +#: glade/dgtkpref.glade:209 +msgid "Use compact storage allocation" +msgstr "" + +#: glade/dgtkpref.glade:216 +msgid "Storage" +msgstr "" + +#: glade/dgtkpref.glade:246 +msgid "General" +msgstr "" + +#: glade/dgtkpref.glade:280 +msgid "Try from:" +msgstr "시도 시작번호:" + +#: glade/dgtkpref.glade:286 +msgid "to:" +msgstr "" + +#: glade/dgtkpref.glade:320 +msgid "Active port:" +msgstr "활성 포트:" + +#: glade/dgtkpref.glade:332 +msgid "0000" +msgstr "" + +#: glade/dgtkpref.glade:343 +msgid "Test Port" +msgstr "포트 검사" + +#: glade/dgtkpref.glade:357 +msgid "TCP Port" +msgstr "" + +#: glade/dgtkpref.glade:380 +msgid "(-1 is unlimited)" +msgstr "" + +#: glade/dgtkpref.glade:390 glade/dgtkpref.glade:511 +msgid "KB/s" +msgstr "" + +#: glade/dgtkpref.glade:436 +msgid "Maximum Download Rate:" +msgstr "최대 내려받기 비율" + +#: glade/dgtkpref.glade:447 +msgid "Upload Slots" +msgstr "" + +#: glade/dgtkpref.glade:458 +msgid "Maximum Connections" +msgstr "" + +#: glade/dgtkpref.glade:485 +msgid "Maximum Upload Rate:" +msgstr "최대 올리기 비율" + +#: glade/dgtkpref.glade:548 +msgid "Bandwidth " +msgstr "" + +#: glade/dgtkpref.glade:576 +msgid "Network" +msgstr "네트워크" + +#: glade/dgtkpref.glade:598 +msgid "gtk-cancel" +msgstr "gtk-cancel" + +#: glade/dgtkpref.glade:605 +msgid "gtk-ok" +msgstr "gtk-ok" + +#: glade/dgtkpref.glade:625 +msgid "Plugin Manager" +msgstr "플러그인 관리자" + +#: glade/dgtkpref.glade:718 +msgid "gtk-close" +msgstr "" + +#: src/dcommon.py:41 src/delugegtk.py:330 +msgid "Infinity" +msgstr "" + +#: src/dcommon.py:105 +msgid "Error: no webbrowser found" +msgstr "" + +#: src/deluge.py:332 +msgid "File was not found" +msgstr "" + +#: src/delugegtk.py:173 +msgid "Show / Hide Window" +msgstr "" + +#: src/delugegtk.py:250 +msgid "Plugin" +msgstr "" + +#: src/delugegtk.py:252 +msgid "Enabled" +msgstr "" + +#: src/delugegtk.py:340 +msgid "Unknown" +msgstr "모름" + +#: src/delugegtk.py:348 +msgid "Name" +msgstr "이름" + +#: src/delugegtk.py:356 +msgid "Ratio" +msgstr "" + +#: src/delugegtk.py:465 +msgid "IP Address" +msgstr "" + +#: src/delugegtk.py:466 +msgid "Client" +msgstr "사용프로그램" + +#: src/delugegtk.py:467 +msgid "Percent Complete" +msgstr "" + +#: src/delugegtk.py:468 +msgid "Download Rate" +msgstr "" + +#: src/delugegtk.py:469 +msgid "Upload Rate" +msgstr "" + +#: src/delugegtk.py:478 +msgid "Filename" +msgstr "" + +#: src/delugegtk.py:480 +msgid "Offset" +msgstr "" + +#: src/delugegtk.py:668 +msgid "" +"For some reason, the previous state could not be loaded, so a blank state " +"has been loaded for you." +msgstr "" + +#: src/delugegtk.py:670 +msgid "Would you like to attempt to reload the previous session's downloads?" +msgstr "" + +#: src/delugegtk.py:679 +msgid "Choose the download directory for" +msgstr "" + +#: src/delugegtk.py:692 src/delugegtk.py:704 +msgid "Connections" +msgstr "연결목록" + +#: src/delugegtk.py:703 +msgid "Deluge Bittorrent Client" +msgstr "Deluge 비트토런트 받기 프로그램" + +#: src/delugegtk.py:891 +msgid "Add torrent from URL" +msgstr "" + +#: src/delugegtk.py:895 +msgid "Enter the URL of the .torrent to download" +msgstr "" + +#: src/delugegtk.py:959 +msgid "Warning - all downloaded files for this torrent will be deleted!" +msgstr "경고 - 내려받은 이 토런트의 파일들이 전부 지워지게 됩니다!" + +#: src/dgtk.py:71 +msgid "Choose a .torrent file" +msgstr "" + +#: src/dgtk.py:76 +msgid "Torrent files" +msgstr "" + +#: src/dgtk.py:80 +msgid "All files" +msgstr "모든 파일" + +#: src/dgtk.py:97 +msgid "Choose a download directory" +msgstr "" \ No newline at end of file diff --git a/encryption/po/la.po b/encryption/po/la.po new file mode 100644 index 000000000..4d81e8b56 --- /dev/null +++ b/encryption/po/la.po @@ -0,0 +1,465 @@ +# Latin translation for deluge +# Copyright (c) 2007 Rosetta Contributors and Canonical Ltd 2007 +# This file is distributed under the same license as the deluge package. +# FIRST AUTHOR , 2007. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: deluge\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2007-03-06 16:19-0500\n" +"PO-Revision-Date: 2007-01-22 20:00+0000\n" +"Last-Translator: Matthias Benkard \n" +"Language-Team: Latin \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Rosetta-Export-Date: 2007-03-18 15:58+0000\n" + +#: glade/delugegtk.glade:93 +msgid "Estimated Time Remaining:" +msgstr "" + +#: glade/delugegtk.glade:131 +msgid "Peers:" +msgstr "" + +#: glade/delugegtk.glade:145 +msgid "Seeders:" +msgstr "" + +#: glade/delugegtk.glade:157 +msgid "Total Size:" +msgstr "" + +#: glade/delugegtk.glade:169 +msgid "Total Downloaded:" +msgstr "" + +#: glade/delugegtk.glade:181 +msgid "Downloaded this session:" +msgstr "" + +#: glade/delugegtk.glade:229 +msgid "Tracker:" +msgstr "" + +#: glade/delugegtk.glade:241 +msgid "Tracker Response:" +msgstr "" + +#: glade/delugegtk.glade:253 +msgid "Tracker Status:" +msgstr "" + +#: glade/delugegtk.glade:277 +msgid "Next Announce:" +msgstr "" + +#: glade/delugegtk.glade:303 +msgid "Pieces:" +msgstr "" + +#: glade/delugegtk.glade:317 +msgid "Total Uploaded:" +msgstr "" + +#: glade/delugegtk.glade:355 +msgid "Share Ratio:" +msgstr "" + +#: glade/delugegtk.glade:369 +msgid "Uploaded This Session:" +msgstr "" + +#: glade/delugegtk.glade:431 +msgid "Use compact storage allocation:" +msgstr "" + +#: glade/delugegtk.glade:455 +msgid "Download Rate:" +msgstr "" + +#: glade/delugegtk.glade:479 +msgid "Upload Rate:" +msgstr "" + +#: glade/delugegtk.glade:522 glade/delugegtk.glade:746 +msgid "Details" +msgstr "" + +#: glade/delugegtk.glade:549 glade/delugegtk.glade:790 +#: glade/dgtkpopups.glade:37 src/delugegtk.py:352 +msgid "Peers" +msgstr "Socii" + +#: glade/delugegtk.glade:578 +msgid "Files" +msgstr "" + +#: glade/delugegtk.glade:613 +msgid "_File" +msgstr "" + +#: glade/delugegtk.glade:620 glade/delugegtk.glade:901 src/delugegtk.py:174 +msgid "Add Torrent" +msgstr "" + +#: glade/delugegtk.glade:636 +msgid "Add Torrent from URL" +msgstr "" + +#: glade/delugegtk.glade:644 glade/delugegtk.glade:914 +#: glade/dgtkpopups.glade:81 +msgid "Remove Torrent" +msgstr "" + +#: glade/delugegtk.glade:658 +msgid "Clear Completed" +msgstr "" + +#: glade/delugegtk.glade:692 +msgid "_Edit" +msgstr "" + +#: glade/delugegtk.glade:709 glade/dgtkpopups.glade:221 +#: glade/dgtkpref.glade:697 src/delugegtk.py:177 +msgid "Plugins" +msgstr "" + +#: glade/delugegtk.glade:729 +msgid "_View" +msgstr "" + +#: glade/delugegtk.glade:737 +msgid "Toolbar" +msgstr "" + +#: glade/delugegtk.glade:755 +msgid "Columns" +msgstr "" + +#: glade/delugegtk.glade:763 glade/dgtkpopups.glade:10 src/delugegtk.py:349 +#: src/delugegtk.py:479 +msgid "Size" +msgstr "Magnitudo" + +#: glade/delugegtk.glade:772 glade/dgtkpopups.glade:19 src/delugegtk.py:350 +msgid "Status" +msgstr "" + +#: glade/delugegtk.glade:781 glade/dgtkpopups.glade:28 src/delugegtk.py:351 +msgid "Seeders" +msgstr "Donantes" + +#: glade/delugegtk.glade:799 src/delugegtk.py:353 src/delugegtk.py:477 +#: src/delugegtk.py:692 src/delugegtk.py:704 +msgid "Download" +msgstr "Arcessere" + +#: glade/delugegtk.glade:808 src/delugegtk.py:354 src/delugegtk.py:693 +#: src/delugegtk.py:705 +msgid "Upload" +msgstr "Donare" + +#: glade/delugegtk.glade:817 glade/dgtkpopups.glade:64 src/delugegtk.py:355 +msgid "Time Remaining" +msgstr "" + +#: glade/delugegtk.glade:826 glade/dgtkpopups.glade:73 +msgid "Share Ratio" +msgstr "" + +#: glade/delugegtk.glade:843 +msgid "_Help" +msgstr "" + +#: glade/delugegtk.glade:927 +msgid "Clear Finished Torrents" +msgstr "" + +#: glade/delugegtk.glade:949 +msgid "Start / Pause" +msgstr "" + +#: glade/delugegtk.glade:962 +msgid "Queue Torrent Up" +msgstr "" + +#: glade/delugegtk.glade:975 +msgid "Queue Torrent Down" +msgstr "" + +#: glade/dgtkpopups.glade:46 +msgid "Download Speed" +msgstr "" + +#: glade/dgtkpopups.glade:55 +msgid "Upload Speed" +msgstr "" + +#: glade/dgtkpopups.glade:97 +msgid "" +"Are you sure you want to remove the selected torrent(s) from Deluge?" +msgstr "" + +#: glade/dgtkpopups.glade:107 +msgid "Delete downloaded files" +msgstr "" + +#: glade/dgtkpopups.glade:167 +msgid "Show/Hide" +msgstr "" + +#: glade/dgtkpopups.glade:175 +msgid "Add a Torrent..." +msgstr "" + +#: glade/dgtkpopups.glade:191 src/delugegtk.py:175 +msgid "Clear Finished" +msgstr "" + +#: glade/dgtkpopups.glade:212 glade/dgtkpref.glade:672 +msgid "gtk-preferences" +msgstr "" + +#: glade/dgtkpopups.glade:242 +msgid "gtk-quit" +msgstr "" + +#: glade/dgtkpref.glade:8 +msgid "Preferences Dialog" +msgstr "" + +#: glade/dgtkpref.glade:53 +msgid "Enable system tray icon" +msgstr "" + +#: glade/dgtkpref.glade:65 +msgid "Minimize to tray on close" +msgstr "" + +#: glade/dgtkpref.glade:80 +msgid "Options" +msgstr "" + +#: glade/dgtkpref.glade:115 +msgid "Save all downloads to:" +msgstr "" + +#: glade/dgtkpref.glade:127 +msgid "Ask me where to save each download" +msgstr "" + +#: glade/dgtkpref.glade:139 +msgid "Save Location" +msgstr "" + +#: glade/dgtkpref.glade:180 +msgid "" +"Stop seeding torrents when\n" +"their share ratio reaches:" +msgstr "" + +#: glade/dgtkpref.glade:190 +msgid "Seeding" +msgstr "Datur" + +#: glade/dgtkpref.glade:209 +msgid "Use compact storage allocation" +msgstr "" + +#: glade/dgtkpref.glade:216 +msgid "Storage" +msgstr "" + +#: glade/dgtkpref.glade:246 +msgid "General" +msgstr "" + +#: glade/dgtkpref.glade:280 +msgid "Try from:" +msgstr "" + +#: glade/dgtkpref.glade:286 +msgid "to:" +msgstr "" + +#: glade/dgtkpref.glade:320 +msgid "Active port:" +msgstr "" + +#: glade/dgtkpref.glade:332 +msgid "0000" +msgstr "" + +#: glade/dgtkpref.glade:343 +msgid "Test Port" +msgstr "" + +#: glade/dgtkpref.glade:357 +msgid "TCP Port" +msgstr "" + +#: glade/dgtkpref.glade:380 +msgid "(-1 is unlimited)" +msgstr "" + +#: glade/dgtkpref.glade:390 glade/dgtkpref.glade:511 +msgid "KB/s" +msgstr "" + +#: glade/dgtkpref.glade:436 +msgid "Maximum Download Rate:" +msgstr "" + +#: glade/dgtkpref.glade:447 +msgid "Upload Slots" +msgstr "" + +#: glade/dgtkpref.glade:458 +msgid "Maximum Connections" +msgstr "" + +#: glade/dgtkpref.glade:485 +msgid "Maximum Upload Rate:" +msgstr "" + +#: glade/dgtkpref.glade:548 +msgid "Bandwidth " +msgstr "" + +#: glade/dgtkpref.glade:576 +msgid "Network" +msgstr "" + +#: glade/dgtkpref.glade:598 +msgid "gtk-cancel" +msgstr "" + +#: glade/dgtkpref.glade:605 +msgid "gtk-ok" +msgstr "" + +#: glade/dgtkpref.glade:625 +msgid "Plugin Manager" +msgstr "" + +#: glade/dgtkpref.glade:718 +msgid "gtk-close" +msgstr "" + +#: src/dcommon.py:41 src/delugegtk.py:330 +msgid "Infinity" +msgstr "" + +#: src/dcommon.py:105 +msgid "Error: no webbrowser found" +msgstr "" + +#: src/deluge.py:332 +msgid "File was not found" +msgstr "" + +#: src/delugegtk.py:173 +msgid "Show / Hide Window" +msgstr "" + +#: src/delugegtk.py:250 +msgid "Plugin" +msgstr "" + +#: src/delugegtk.py:252 +msgid "Enabled" +msgstr "" + +#: src/delugegtk.py:340 +msgid "Unknown" +msgstr "Ignotum" + +#: src/delugegtk.py:348 +msgid "Name" +msgstr "Nomen" + +#: src/delugegtk.py:356 +msgid "Ratio" +msgstr "" + +#: src/delugegtk.py:465 +msgid "IP Address" +msgstr "" + +#: src/delugegtk.py:466 +msgid "Client" +msgstr "Emptor" + +#: src/delugegtk.py:467 +msgid "Percent Complete" +msgstr "" + +#: src/delugegtk.py:468 +msgid "Download Rate" +msgstr "" + +#: src/delugegtk.py:469 +msgid "Upload Rate" +msgstr "" + +#: src/delugegtk.py:478 +msgid "Filename" +msgstr "" + +#: src/delugegtk.py:480 +msgid "Offset" +msgstr "" + +#: src/delugegtk.py:668 +msgid "" +"For some reason, the previous state could not be loaded, so a blank state " +"has been loaded for you." +msgstr "" + +#: src/delugegtk.py:670 +msgid "Would you like to attempt to reload the previous session's downloads?" +msgstr "" + +#: src/delugegtk.py:679 +msgid "Choose the download directory for" +msgstr "" + +#: src/delugegtk.py:692 src/delugegtk.py:704 +msgid "Connections" +msgstr "Coniunctiones" + +#: src/delugegtk.py:703 +msgid "Deluge Bittorrent Client" +msgstr "Emptor Bittorrenti nominis Delugum" + +#: src/delugegtk.py:891 +msgid "Add torrent from URL" +msgstr "" + +#: src/delugegtk.py:895 +msgid "Enter the URL of the .torrent to download" +msgstr "" + +#: src/delugegtk.py:959 +msgid "Warning - all downloaded files for this torrent will be deleted!" +msgstr "Adverte! Omna data huius amnis delenda sunt!" + +#: src/dgtk.py:71 +msgid "Choose a .torrent file" +msgstr "" + +#: src/dgtk.py:76 +msgid "Torrent files" +msgstr "" + +#: src/dgtk.py:80 +msgid "All files" +msgstr "Omna data" + +#: src/dgtk.py:97 +msgid "Choose a download directory" +msgstr "" \ No newline at end of file diff --git a/encryption/po/lt.po b/encryption/po/lt.po new file mode 100644 index 000000000..9ae21ad40 --- /dev/null +++ b/encryption/po/lt.po @@ -0,0 +1,466 @@ +# Lithuanian translation for deluge +# Copyright (c) 2006 Rosetta Contributors and Canonical Ltd 2006 +# This file is distributed under the same license as the deluge package. +# FIRST AUTHOR , 2006. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: deluge\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2007-03-06 16:19-0500\n" +"PO-Revision-Date: 2007-02-25 13:40+0000\n" +"Last-Translator: Jonas Slivka \n" +"Language-Team: Lithuanian \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Rosetta-Export-Date: 2007-03-18 15:58+0000\n" + +#: glade/delugegtk.glade:93 +msgid "Estimated Time Remaining:" +msgstr "" + +#: glade/delugegtk.glade:131 +msgid "Peers:" +msgstr "" + +#: glade/delugegtk.glade:145 +msgid "Seeders:" +msgstr "" + +#: glade/delugegtk.glade:157 +msgid "Total Size:" +msgstr "Bendras dydis:" + +#: glade/delugegtk.glade:169 +msgid "Total Downloaded:" +msgstr "Iš viso parsiųsta:" + +#: glade/delugegtk.glade:181 +#, fuzzy +msgid "Downloaded this session:" +msgstr "Parsiųsta šios sesijos metu:" + +#: glade/delugegtk.glade:229 +msgid "Tracker:" +msgstr "" + +#: glade/delugegtk.glade:241 +msgid "Tracker Response:" +msgstr "" + +#: glade/delugegtk.glade:253 +msgid "Tracker Status:" +msgstr "" + +#: glade/delugegtk.glade:277 +msgid "Next Announce:" +msgstr "" + +#: glade/delugegtk.glade:303 +msgid "Pieces:" +msgstr "" + +#: glade/delugegtk.glade:317 +msgid "Total Uploaded:" +msgstr "Iš viso išsiųsta:" + +#: glade/delugegtk.glade:355 +msgid "Share Ratio:" +msgstr "Dalinimosi santykis:" + +#: glade/delugegtk.glade:369 +msgid "Uploaded This Session:" +msgstr "Išsiųsta šios sesijos metu:" + +#: glade/delugegtk.glade:431 +msgid "Use compact storage allocation:" +msgstr "Naudoti kompaktišką vietos paskirstymą:" + +#: glade/delugegtk.glade:455 +msgid "Download Rate:" +msgstr "Atsiuntimo greitis:" + +#: glade/delugegtk.glade:479 +msgid "Upload Rate:" +msgstr "Išsiuntimo greitis:" + +#: glade/delugegtk.glade:522 glade/delugegtk.glade:746 +msgid "Details" +msgstr "Išsami informacija" + +#: glade/delugegtk.glade:549 glade/delugegtk.glade:790 +#: glade/dgtkpopups.glade:37 src/delugegtk.py:352 +msgid "Peers" +msgstr "" + +#: glade/delugegtk.glade:578 +msgid "Files" +msgstr "Failai" + +#: glade/delugegtk.glade:613 +msgid "_File" +msgstr "_Failas" + +#: glade/delugegtk.glade:620 glade/delugegtk.glade:901 src/delugegtk.py:174 +msgid "Add Torrent" +msgstr "" + +#: glade/delugegtk.glade:636 +msgid "Add Torrent from URL" +msgstr "" + +#: glade/delugegtk.glade:644 glade/delugegtk.glade:914 +#: glade/dgtkpopups.glade:81 +msgid "Remove Torrent" +msgstr "" + +#: glade/delugegtk.glade:658 +msgid "Clear Completed" +msgstr "Išvalyti užbaigtus" + +#: glade/delugegtk.glade:692 +msgid "_Edit" +msgstr "_Redaguoti" + +#: glade/delugegtk.glade:709 glade/dgtkpopups.glade:221 +#: glade/dgtkpref.glade:697 src/delugegtk.py:177 +msgid "Plugins" +msgstr "Priedai" + +#: glade/delugegtk.glade:729 +msgid "_View" +msgstr "Ro_dymas" + +#: glade/delugegtk.glade:737 +msgid "Toolbar" +msgstr "Mygtukų juosta" + +#: glade/delugegtk.glade:755 +msgid "Columns" +msgstr "Skiltys" + +#: glade/delugegtk.glade:763 glade/dgtkpopups.glade:10 src/delugegtk.py:349 +#: src/delugegtk.py:479 +msgid "Size" +msgstr "Dydis" + +#: glade/delugegtk.glade:772 glade/dgtkpopups.glade:19 src/delugegtk.py:350 +msgid "Status" +msgstr "Būsena" + +#: glade/delugegtk.glade:781 glade/dgtkpopups.glade:28 src/delugegtk.py:351 +msgid "Seeders" +msgstr "" + +#: glade/delugegtk.glade:799 src/delugegtk.py:353 src/delugegtk.py:477 +#: src/delugegtk.py:692 src/delugegtk.py:704 +msgid "Download" +msgstr "Siuntimas" + +#: glade/delugegtk.glade:808 src/delugegtk.py:354 src/delugegtk.py:693 +#: src/delugegtk.py:705 +msgid "Upload" +msgstr "Išsiuntimas" + +#: glade/delugegtk.glade:817 glade/dgtkpopups.glade:64 src/delugegtk.py:355 +msgid "Time Remaining" +msgstr "Liko laiko" + +#: glade/delugegtk.glade:826 glade/dgtkpopups.glade:73 +msgid "Share Ratio" +msgstr "Dalinimosi santykis" + +#: glade/delugegtk.glade:843 +msgid "_Help" +msgstr "_Žinynas" + +#: glade/delugegtk.glade:927 +msgid "Clear Finished Torrents" +msgstr "" + +#: glade/delugegtk.glade:949 +msgid "Start / Pause" +msgstr "" + +#: glade/delugegtk.glade:962 +msgid "Queue Torrent Up" +msgstr "" + +#: glade/delugegtk.glade:975 +msgid "Queue Torrent Down" +msgstr "" + +#: glade/dgtkpopups.glade:46 +msgid "Download Speed" +msgstr "Parsiuntimo sparta" + +#: glade/dgtkpopups.glade:55 +msgid "Upload Speed" +msgstr "Išsiuntimo sparta" + +#: glade/dgtkpopups.glade:97 +msgid "" +"Are you sure you want to remove the selected torrent(s) from Deluge?" +msgstr "" + +#: glade/dgtkpopups.glade:107 +msgid "Delete downloaded files" +msgstr "Ištrinti parsiųstus failus" + +#: glade/dgtkpopups.glade:167 +msgid "Show/Hide" +msgstr "Rodyti/Slėpti" + +#: glade/dgtkpopups.glade:175 +msgid "Add a Torrent..." +msgstr "" + +#: glade/dgtkpopups.glade:191 src/delugegtk.py:175 +msgid "Clear Finished" +msgstr "Išvalyti užbaigtus" + +#: glade/dgtkpopups.glade:212 glade/dgtkpref.glade:672 +msgid "gtk-preferences" +msgstr "" + +#: glade/dgtkpopups.glade:242 +msgid "gtk-quit" +msgstr "" + +#: glade/dgtkpref.glade:8 +msgid "Preferences Dialog" +msgstr "" + +#: glade/dgtkpref.glade:53 +msgid "Enable system tray icon" +msgstr "Įjungti sistemos dėklo ženkliuką" + +#: glade/dgtkpref.glade:65 +msgid "Minimize to tray on close" +msgstr "Užveriant nukelti į sistemos dėklą" + +#: glade/dgtkpref.glade:80 +msgid "Options" +msgstr "Pasirinkimai" + +#: glade/dgtkpref.glade:115 +msgid "Save all downloads to:" +msgstr "" + +#: glade/dgtkpref.glade:127 +msgid "Ask me where to save each download" +msgstr "" + +#: glade/dgtkpref.glade:139 +msgid "Save Location" +msgstr "Išsaugojimo vieta" + +#: glade/dgtkpref.glade:180 +msgid "" +"Stop seeding torrents when\n" +"their share ratio reaches:" +msgstr "" + +#: glade/dgtkpref.glade:190 +msgid "Seeding" +msgstr "" + +#: glade/dgtkpref.glade:209 +msgid "Use compact storage allocation" +msgstr "" + +#: glade/dgtkpref.glade:216 +msgid "Storage" +msgstr "" + +#: glade/dgtkpref.glade:246 +msgid "General" +msgstr "Bendroji informacija" + +#: glade/dgtkpref.glade:280 +msgid "Try from:" +msgstr "" + +#: glade/dgtkpref.glade:286 +msgid "to:" +msgstr "" + +#: glade/dgtkpref.glade:320 +msgid "Active port:" +msgstr "Aktyvusis prievadas:" + +#: glade/dgtkpref.glade:332 +msgid "0000" +msgstr "" + +#: glade/dgtkpref.glade:343 +msgid "Test Port" +msgstr "Išbandyti prievadą" + +#: glade/dgtkpref.glade:357 +msgid "TCP Port" +msgstr "TCP prievadas" + +#: glade/dgtkpref.glade:380 +msgid "(-1 is unlimited)" +msgstr "" + +#: glade/dgtkpref.glade:390 glade/dgtkpref.glade:511 +msgid "KB/s" +msgstr "KB/s" + +#: glade/dgtkpref.glade:436 +msgid "Maximum Download Rate:" +msgstr "" + +#: glade/dgtkpref.glade:447 +msgid "Upload Slots" +msgstr "" + +#: glade/dgtkpref.glade:458 +msgid "Maximum Connections" +msgstr "" + +#: glade/dgtkpref.glade:485 +msgid "Maximum Upload Rate:" +msgstr "" + +#: glade/dgtkpref.glade:548 +msgid "Bandwidth " +msgstr "" + +#: glade/dgtkpref.glade:576 +msgid "Network" +msgstr "Tinklas" + +#: glade/dgtkpref.glade:598 +msgid "gtk-cancel" +msgstr "" + +#: glade/dgtkpref.glade:605 +msgid "gtk-ok" +msgstr "" + +#: glade/dgtkpref.glade:625 +msgid "Plugin Manager" +msgstr "Priedų tvarkyklė" + +#: glade/dgtkpref.glade:718 +msgid "gtk-close" +msgstr "" + +#: src/dcommon.py:41 src/delugegtk.py:330 +msgid "Infinity" +msgstr "Begalybė" + +#: src/dcommon.py:105 +msgid "Error: no webbrowser found" +msgstr "Klaida: nerasta jokios naršyklės" + +#: src/deluge.py:332 +msgid "File was not found" +msgstr "" + +#: src/delugegtk.py:173 +msgid "Show / Hide Window" +msgstr "" + +#: src/delugegtk.py:250 +msgid "Plugin" +msgstr "Priedas" + +#: src/delugegtk.py:252 +msgid "Enabled" +msgstr "" + +#: src/delugegtk.py:340 +msgid "Unknown" +msgstr "Nežinomas" + +#: src/delugegtk.py:348 +msgid "Name" +msgstr "Pavadinimas" + +#: src/delugegtk.py:356 +msgid "Ratio" +msgstr "" + +#: src/delugegtk.py:465 +msgid "IP Address" +msgstr "" + +#: src/delugegtk.py:466 +msgid "Client" +msgstr "Klientas" + +#: src/delugegtk.py:467 +msgid "Percent Complete" +msgstr "" + +#: src/delugegtk.py:468 +msgid "Download Rate" +msgstr "" + +#: src/delugegtk.py:469 +msgid "Upload Rate" +msgstr "" + +#: src/delugegtk.py:478 +msgid "Filename" +msgstr "" + +#: src/delugegtk.py:480 +msgid "Offset" +msgstr "" + +#: src/delugegtk.py:668 +msgid "" +"For some reason, the previous state could not be loaded, so a blank state " +"has been loaded for you." +msgstr "" + +#: src/delugegtk.py:670 +msgid "Would you like to attempt to reload the previous session's downloads?" +msgstr "" + +#: src/delugegtk.py:679 +msgid "Choose the download directory for" +msgstr "" + +#: src/delugegtk.py:692 src/delugegtk.py:704 +msgid "Connections" +msgstr "Prisijungimai" + +#: src/delugegtk.py:703 +msgid "Deluge Bittorrent Client" +msgstr "" + +#: src/delugegtk.py:891 +msgid "Add torrent from URL" +msgstr "" + +#: src/delugegtk.py:895 +msgid "Enter the URL of the .torrent to download" +msgstr "" + +#: src/delugegtk.py:959 +msgid "Warning - all downloaded files for this torrent will be deleted!" +msgstr "" + +#: src/dgtk.py:71 +msgid "Choose a .torrent file" +msgstr "" + +#: src/dgtk.py:76 +msgid "Torrent files" +msgstr "" + +#: src/dgtk.py:80 +msgid "All files" +msgstr "Visos bylos" + +#: src/dgtk.py:97 +msgid "Choose a download directory" +msgstr "" \ No newline at end of file diff --git a/encryption/po/lv.po b/encryption/po/lv.po new file mode 100644 index 000000000..bb6d952e1 --- /dev/null +++ b/encryption/po/lv.po @@ -0,0 +1,467 @@ +# Latvian translation for deluge +# Copyright (c) 2006 Rosetta Contributors and Canonical Ltd 2006 +# This file is distributed under the same license as the deluge package. +# FIRST AUTHOR , 2006. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: deluge\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2007-03-06 16:19-0500\n" +"PO-Revision-Date: 2006-11-16 18:18+0000\n" +"Last-Translator: c0nfidencal \n" +"Language-Team: Latvian \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Rosetta-Export-Date: 2007-03-18 15:58+0000\n" + +#: glade/delugegtk.glade:93 +msgid "Estimated Time Remaining:" +msgstr "" + +#: glade/delugegtk.glade:131 +msgid "Peers:" +msgstr "" + +#: glade/delugegtk.glade:145 +msgid "Seeders:" +msgstr "" + +#: glade/delugegtk.glade:157 +msgid "Total Size:" +msgstr "Kopējais izmērs:" + +#: glade/delugegtk.glade:169 +msgid "Total Downloaded:" +msgstr "Kopumā lejupielādēts:" + +#: glade/delugegtk.glade:181 +msgid "Downloaded this session:" +msgstr "Lejupielādēts šajā sesijā:" + +#: glade/delugegtk.glade:229 +msgid "Tracker:" +msgstr "Trakeris:" + +#: glade/delugegtk.glade:241 +msgid "Tracker Response:" +msgstr "Trakera atbilde:" + +#: glade/delugegtk.glade:253 +msgid "Tracker Status:" +msgstr "Trakera statuss:" + +#: glade/delugegtk.glade:277 +msgid "Next Announce:" +msgstr "Nākamā paziņošana:" + +#: glade/delugegtk.glade:303 +msgid "Pieces:" +msgstr "Gabaliņi:" + +#: glade/delugegtk.glade:317 +msgid "Total Uploaded:" +msgstr "Kopumā augšupielādēts:" + +#: glade/delugegtk.glade:355 +msgid "Share Ratio:" +msgstr "Dalīšanās attiecība:" + +#: glade/delugegtk.glade:369 +msgid "Uploaded This Session:" +msgstr "Augšupielādēts šajā sesijā:" + +#: glade/delugegtk.glade:431 +msgid "Use compact storage allocation:" +msgstr "Izmantot kompakto glabātavas piešķiršanu:" + +#: glade/delugegtk.glade:455 +msgid "Download Rate:" +msgstr "" + +#: glade/delugegtk.glade:479 +msgid "Upload Rate:" +msgstr "" + +#: glade/delugegtk.glade:522 glade/delugegtk.glade:746 +msgid "Details" +msgstr "" + +#: glade/delugegtk.glade:549 glade/delugegtk.glade:790 +#: glade/dgtkpopups.glade:37 src/delugegtk.py:352 +msgid "Peers" +msgstr "Iesaistītie" + +#: glade/delugegtk.glade:578 +msgid "Files" +msgstr "" + +#: glade/delugegtk.glade:613 +msgid "_File" +msgstr "_Fails" + +#: glade/delugegtk.glade:620 glade/delugegtk.glade:901 src/delugegtk.py:174 +msgid "Add Torrent" +msgstr "Pievienot torrentu" + +#: glade/delugegtk.glade:636 +msgid "Add Torrent from URL" +msgstr "" + +#: glade/delugegtk.glade:644 glade/delugegtk.glade:914 +#: glade/dgtkpopups.glade:81 +msgid "Remove Torrent" +msgstr "Aizvākt torrentu" + +#: glade/delugegtk.glade:658 +msgid "Clear Completed" +msgstr "" + +#: glade/delugegtk.glade:692 +msgid "_Edit" +msgstr "_Labot" + +#: glade/delugegtk.glade:709 glade/dgtkpopups.glade:221 +#: glade/dgtkpref.glade:697 src/delugegtk.py:177 +msgid "Plugins" +msgstr "" + +#: glade/delugegtk.glade:729 +msgid "_View" +msgstr "" + +#: glade/delugegtk.glade:737 +msgid "Toolbar" +msgstr "" + +#: glade/delugegtk.glade:755 +msgid "Columns" +msgstr "" + +#: glade/delugegtk.glade:763 glade/dgtkpopups.glade:10 src/delugegtk.py:349 +#: src/delugegtk.py:479 +msgid "Size" +msgstr "Izmērs" + +#: glade/delugegtk.glade:772 glade/dgtkpopups.glade:19 src/delugegtk.py:350 +msgid "Status" +msgstr "" + +#: glade/delugegtk.glade:781 glade/dgtkpopups.glade:28 src/delugegtk.py:351 +msgid "Seeders" +msgstr "Devēji" + +#: glade/delugegtk.glade:799 src/delugegtk.py:353 src/delugegtk.py:477 +#: src/delugegtk.py:692 src/delugegtk.py:704 +msgid "Download" +msgstr "Lejupielāde" + +#: glade/delugegtk.glade:808 src/delugegtk.py:354 src/delugegtk.py:693 +#: src/delugegtk.py:705 +msgid "Upload" +msgstr "Augšupielāde" + +#: glade/delugegtk.glade:817 glade/dgtkpopups.glade:64 src/delugegtk.py:355 +msgid "Time Remaining" +msgstr "" + +#: glade/delugegtk.glade:826 glade/dgtkpopups.glade:73 +msgid "Share Ratio" +msgstr "Dalīšanās koeficients" + +#: glade/delugegtk.glade:843 +msgid "_Help" +msgstr "_Palīdzība" + +#: glade/delugegtk.glade:927 +msgid "Clear Finished Torrents" +msgstr "Novākt pabeigtos torrentus" + +#: glade/delugegtk.glade:949 +msgid "Start / Pause" +msgstr "" + +#: glade/delugegtk.glade:962 +msgid "Queue Torrent Up" +msgstr "Torrentu rinda augšā" + +#: glade/delugegtk.glade:975 +msgid "Queue Torrent Down" +msgstr "Torrentu rinda lejā" + +#: glade/dgtkpopups.glade:46 +msgid "Download Speed" +msgstr "" + +#: glade/dgtkpopups.glade:55 +msgid "Upload Speed" +msgstr "" + +#: glade/dgtkpopups.glade:97 +msgid "" +"Are you sure you want to remove the selected torrent(s) from Deluge?" +msgstr "" +"Vai tu esi pārliecināts, ka gribi aizvākt izvēlēto(s) torrentu(s) no " +"Deluge?" + +#: glade/dgtkpopups.glade:107 +msgid "Delete downloaded files" +msgstr "Dzēst lejupielādētos failus" + +#: glade/dgtkpopups.glade:167 +msgid "Show/Hide" +msgstr "" + +#: glade/dgtkpopups.glade:175 +msgid "Add a Torrent..." +msgstr "" + +#: glade/dgtkpopups.glade:191 src/delugegtk.py:175 +msgid "Clear Finished" +msgstr "Novākt pabeigtos" + +#: glade/dgtkpopups.glade:212 glade/dgtkpref.glade:672 +msgid "gtk-preferences" +msgstr "gtk-preferences" + +#: glade/dgtkpopups.glade:242 +msgid "gtk-quit" +msgstr "" + +#: glade/dgtkpref.glade:8 +msgid "Preferences Dialog" +msgstr "" + +#: glade/dgtkpref.glade:53 +msgid "Enable system tray icon" +msgstr "" + +#: glade/dgtkpref.glade:65 +msgid "Minimize to tray on close" +msgstr "" + +#: glade/dgtkpref.glade:80 +msgid "Options" +msgstr "" + +#: glade/dgtkpref.glade:115 +msgid "Save all downloads to:" +msgstr "Saglabāt visas lejupielādes iekš:" + +#: glade/dgtkpref.glade:127 +msgid "Ask me where to save each download" +msgstr "" + +#: glade/dgtkpref.glade:139 +msgid "Save Location" +msgstr "" + +#: glade/dgtkpref.glade:180 +msgid "" +"Stop seeding torrents when\n" +"their share ratio reaches:" +msgstr "" + +#: glade/dgtkpref.glade:190 +msgid "Seeding" +msgstr "Dodu" + +#: glade/dgtkpref.glade:209 +msgid "Use compact storage allocation" +msgstr "" + +#: glade/dgtkpref.glade:216 +msgid "Storage" +msgstr "" + +#: glade/dgtkpref.glade:246 +msgid "General" +msgstr "" + +#: glade/dgtkpref.glade:280 +msgid "Try from:" +msgstr "Mēģināt no:" + +#: glade/dgtkpref.glade:286 +msgid "to:" +msgstr "" + +#: glade/dgtkpref.glade:320 +msgid "Active port:" +msgstr "Aktīvais ports:" + +#: glade/dgtkpref.glade:332 +msgid "0000" +msgstr "" + +#: glade/dgtkpref.glade:343 +msgid "Test Port" +msgstr "Notestēt portu" + +#: glade/dgtkpref.glade:357 +msgid "TCP Port" +msgstr "" + +#: glade/dgtkpref.glade:380 +msgid "(-1 is unlimited)" +msgstr "" + +#: glade/dgtkpref.glade:390 glade/dgtkpref.glade:511 +msgid "KB/s" +msgstr "" + +#: glade/dgtkpref.glade:436 +msgid "Maximum Download Rate:" +msgstr "Maksimālā lejupielādes norma:" + +#: glade/dgtkpref.glade:447 +msgid "Upload Slots" +msgstr "" + +#: glade/dgtkpref.glade:458 +msgid "Maximum Connections" +msgstr "" + +#: glade/dgtkpref.glade:485 +msgid "Maximum Upload Rate:" +msgstr "Maksimālā augšupielādes norma:" + +#: glade/dgtkpref.glade:548 +msgid "Bandwidth " +msgstr "" + +#: glade/dgtkpref.glade:576 +msgid "Network" +msgstr "Tīkls" + +#: glade/dgtkpref.glade:598 +msgid "gtk-cancel" +msgstr "gtk-cancel" + +#: glade/dgtkpref.glade:605 +msgid "gtk-ok" +msgstr "gtk-ok" + +#: glade/dgtkpref.glade:625 +msgid "Plugin Manager" +msgstr "Spraudņu menedžeris" + +#: glade/dgtkpref.glade:718 +msgid "gtk-close" +msgstr "" + +#: src/dcommon.py:41 src/delugegtk.py:330 +msgid "Infinity" +msgstr "" + +#: src/dcommon.py:105 +msgid "Error: no webbrowser found" +msgstr "" + +#: src/deluge.py:332 +msgid "File was not found" +msgstr "" + +#: src/delugegtk.py:173 +msgid "Show / Hide Window" +msgstr "" + +#: src/delugegtk.py:250 +msgid "Plugin" +msgstr "" + +#: src/delugegtk.py:252 +msgid "Enabled" +msgstr "" + +#: src/delugegtk.py:340 +msgid "Unknown" +msgstr "Nezināms" + +#: src/delugegtk.py:348 +msgid "Name" +msgstr "Nosaukums" + +#: src/delugegtk.py:356 +msgid "Ratio" +msgstr "" + +#: src/delugegtk.py:465 +msgid "IP Address" +msgstr "" + +#: src/delugegtk.py:466 +msgid "Client" +msgstr "Klients" + +#: src/delugegtk.py:467 +msgid "Percent Complete" +msgstr "" + +#: src/delugegtk.py:468 +msgid "Download Rate" +msgstr "" + +#: src/delugegtk.py:469 +msgid "Upload Rate" +msgstr "" + +#: src/delugegtk.py:478 +msgid "Filename" +msgstr "" + +#: src/delugegtk.py:480 +msgid "Offset" +msgstr "" + +#: src/delugegtk.py:668 +msgid "" +"For some reason, the previous state could not be loaded, so a blank state " +"has been loaded for you." +msgstr "" + +#: src/delugegtk.py:670 +msgid "Would you like to attempt to reload the previous session's downloads?" +msgstr "" + +#: src/delugegtk.py:679 +msgid "Choose the download directory for" +msgstr "" + +#: src/delugegtk.py:692 src/delugegtk.py:704 +msgid "Connections" +msgstr "Konekcijas" + +#: src/delugegtk.py:703 +msgid "Deluge Bittorrent Client" +msgstr "Deluge Bittorrenta Klients" + +#: src/delugegtk.py:891 +msgid "Add torrent from URL" +msgstr "" + +#: src/delugegtk.py:895 +msgid "Enter the URL of the .torrent to download" +msgstr "" + +#: src/delugegtk.py:959 +msgid "Warning - all downloaded files for this torrent will be deleted!" +msgstr "Uzmanību - visi lejupielādētie faili šim torrentam tiks dzēsti!" + +#: src/dgtk.py:71 +msgid "Choose a .torrent file" +msgstr "" + +#: src/dgtk.py:76 +msgid "Torrent files" +msgstr "" + +#: src/dgtk.py:80 +msgid "All files" +msgstr "Visi faili" + +#: src/dgtk.py:97 +msgid "Choose a download directory" +msgstr "" \ No newline at end of file diff --git a/encryption/po/nb.po b/encryption/po/nb.po new file mode 100644 index 000000000..ee8fdaf39 --- /dev/null +++ b/encryption/po/nb.po @@ -0,0 +1,472 @@ +# Norwegian Bokmål translation for deluge +# Copyright (c) 2006 Rosetta Contributors and Canonical Ltd 2006 +# This file is distributed under the same license as the deluge package. +# FIRST AUTHOR , 2006. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: deluge\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2007-03-06 16:19-0500\n" +"PO-Revision-Date: 2007-03-17 17:41+0000\n" +"Last-Translator: Stian24 \n" +"Language-Team: Norwegian Bokmål \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Rosetta-Export-Date: 2007-03-18 15:58+0000\n" + +#: glade/delugegtk.glade:93 +msgid "Estimated Time Remaining:" +msgstr "Beregnet Tid Som Gjenstår:" + +#: glade/delugegtk.glade:131 +msgid "Peers:" +msgstr "Nedlastere:" + +#: glade/delugegtk.glade:145 +msgid "Seeders:" +msgstr "Seendere:" + +#: glade/delugegtk.glade:157 +msgid "Total Size:" +msgstr "Total Størrelse" + +#: glade/delugegtk.glade:169 +msgid "Total Downloaded:" +msgstr "Totalt Nedlastet:" + +#: glade/delugegtk.glade:181 +msgid "Downloaded this session:" +msgstr "Lastet ned denne sesjonen:" + +#: glade/delugegtk.glade:229 +msgid "Tracker:" +msgstr "Tracker" + +#: glade/delugegtk.glade:241 +msgid "Tracker Response:" +msgstr "Trackers Svar:" + +#: glade/delugegtk.glade:253 +msgid "Tracker Status:" +msgstr "Tracker Status:" + +#: glade/delugegtk.glade:277 +msgid "Next Announce:" +msgstr " Neste Annonsering:" + +#: glade/delugegtk.glade:303 +msgid "Pieces:" +msgstr "Deler:" + +#: glade/delugegtk.glade:317 +msgid "Total Uploaded:" +msgstr "Totalt Opplastet:" + +#: glade/delugegtk.glade:355 +msgid "Share Ratio:" +msgstr "Delingsgrad:" + +#: glade/delugegtk.glade:369 +msgid "Uploaded This Session:" +msgstr "Opplastet denne sesjonen" + +#: glade/delugegtk.glade:431 +#, fuzzy +msgid "Use compact storage allocation:" +msgstr "Bruk kompakt lagringsplass" + +#: glade/delugegtk.glade:455 +msgid "Download Rate:" +msgstr "Nedlastings Hastighet:" + +#: glade/delugegtk.glade:479 +msgid "Upload Rate:" +msgstr "Opplastings Hastighet:" + +#: glade/delugegtk.glade:522 glade/delugegtk.glade:746 +msgid "Details" +msgstr "Detaljer" + +#: glade/delugegtk.glade:549 glade/delugegtk.glade:790 +#: glade/dgtkpopups.glade:37 src/delugegtk.py:352 +msgid "Peers" +msgstr "Leechere" + +#: glade/delugegtk.glade:578 +msgid "Files" +msgstr "Filer" + +#: glade/delugegtk.glade:613 +msgid "_File" +msgstr "_Fil" + +#: glade/delugegtk.glade:620 glade/delugegtk.glade:901 src/delugegtk.py:174 +msgid "Add Torrent" +msgstr "Legg til Torrent" + +#: glade/delugegtk.glade:636 +msgid "Add Torrent from URL" +msgstr "Legg til Torrent fra URL" + +#: glade/delugegtk.glade:644 glade/delugegtk.glade:914 +#: glade/dgtkpopups.glade:81 +msgid "Remove Torrent" +msgstr "Fjern Torrent" + +#: glade/delugegtk.glade:658 +msgid "Clear Completed" +msgstr "Fjern Fullførte" + +#: glade/delugegtk.glade:692 +msgid "_Edit" +msgstr "_Rediger" + +#: glade/delugegtk.glade:709 glade/dgtkpopups.glade:221 +#: glade/dgtkpref.glade:697 src/delugegtk.py:177 +msgid "Plugins" +msgstr "Programtillegg" + +#: glade/delugegtk.glade:729 +msgid "_View" +msgstr "_Vis" + +#: glade/delugegtk.glade:737 +msgid "Toolbar" +msgstr "Verktøylinje" + +#: glade/delugegtk.glade:755 +msgid "Columns" +msgstr "Kolonner" + +#: glade/delugegtk.glade:763 glade/dgtkpopups.glade:10 src/delugegtk.py:349 +#: src/delugegtk.py:479 +msgid "Size" +msgstr "Størrelse" + +#: glade/delugegtk.glade:772 glade/dgtkpopups.glade:19 src/delugegtk.py:350 +msgid "Status" +msgstr "Status" + +#: glade/delugegtk.glade:781 glade/dgtkpopups.glade:28 src/delugegtk.py:351 +msgid "Seeders" +msgstr "Seedere" + +#: glade/delugegtk.glade:799 src/delugegtk.py:353 src/delugegtk.py:477 +#: src/delugegtk.py:692 src/delugegtk.py:704 +msgid "Download" +msgstr "Nedlastning" + +#: glade/delugegtk.glade:808 src/delugegtk.py:354 src/delugegtk.py:693 +#: src/delugegtk.py:705 +msgid "Upload" +msgstr "Opplastning" + +#: glade/delugegtk.glade:817 glade/dgtkpopups.glade:64 src/delugegtk.py:355 +msgid "Time Remaining" +msgstr "Tid som gjenstår" + +#: glade/delugegtk.glade:826 glade/dgtkpopups.glade:73 +msgid "Share Ratio" +msgstr "Delings forhold" + +#: glade/delugegtk.glade:843 +msgid "_Help" +msgstr "_Hjelp" + +#: glade/delugegtk.glade:927 +msgid "Clear Finished Torrents" +msgstr "Fjern Ferdige Torrent'er" + +#: glade/delugegtk.glade:949 +msgid "Start / Pause" +msgstr "Start / Pause" + +#: glade/delugegtk.glade:962 +msgid "Queue Torrent Up" +msgstr "Flytt Torrent Opp I Kø" + +#: glade/delugegtk.glade:975 +msgid "Queue Torrent Down" +msgstr "Flytt Torrent Ned I Kø" + +#: glade/dgtkpopups.glade:46 +msgid "Download Speed" +msgstr "Nedlastingshastighet" + +#: glade/dgtkpopups.glade:55 +msgid "Upload Speed" +msgstr "Opplastningshastighet" + +#: glade/dgtkpopups.glade:97 +msgid "" +"Are you sure you want to remove the selected torrent(s) from Deluge?" +msgstr "" +"Er du sikker på at du vil fjerne valgte torrent(er) fra Deluge?" + +#: glade/dgtkpopups.glade:107 +msgid "Delete downloaded files" +msgstr "Slett nedlastede filer" + +#: glade/dgtkpopups.glade:167 +msgid "Show/Hide" +msgstr "Vis/Skjul" + +#: glade/dgtkpopups.glade:175 +msgid "Add a Torrent..." +msgstr "Legg til en Torrent..." + +#: glade/dgtkpopups.glade:191 src/delugegtk.py:175 +msgid "Clear Finished" +msgstr "Fjern Ferdige" + +#: glade/dgtkpopups.glade:212 glade/dgtkpref.glade:672 +msgid "gtk-preferences" +msgstr "gtk-preferences" + +#: glade/dgtkpopups.glade:242 +msgid "gtk-quit" +msgstr "gtk-quit" + +#: glade/dgtkpref.glade:8 +msgid "Preferences Dialog" +msgstr "Egenskaper" + +#: glade/dgtkpref.glade:53 +msgid "Enable system tray icon" +msgstr "Bruk systemikonfeltet" + +#: glade/dgtkpref.glade:65 +msgid "Minimize to tray on close" +msgstr "Minimer til menylinje ved avslutt" + +#: glade/dgtkpref.glade:80 +msgid "Options" +msgstr "Instillinger" + +#: glade/dgtkpref.glade:115 +msgid "Save all downloads to:" +msgstr "Lagre alle nedlastinger til:" + +#: glade/dgtkpref.glade:127 +msgid "Ask me where to save each download" +msgstr "Spør hvor jeg skal lagre hver nedlasting" + +#: glade/dgtkpref.glade:139 +msgid "Save Location" +msgstr "Lagringsplassen" + +#: glade/dgtkpref.glade:180 +msgid "" +"Stop seeding torrents when\n" +"their share ratio reaches:" +msgstr "Stopp deling av torrents når deling rate når:" + +#: glade/dgtkpref.glade:190 +msgid "Seeding" +msgstr "Seeder" + +#: glade/dgtkpref.glade:209 +msgid "Use compact storage allocation" +msgstr "Bruk kompakt lagringsplass" + +#: glade/dgtkpref.glade:216 +msgid "Storage" +msgstr "Lagring" + +#: glade/dgtkpref.glade:246 +msgid "General" +msgstr "Generelt" + +#: glade/dgtkpref.glade:280 +msgid "Try from:" +msgstr "Prøv fra:" + +#: glade/dgtkpref.glade:286 +msgid "to:" +msgstr "til:" + +#: glade/dgtkpref.glade:320 +msgid "Active port:" +msgstr "Aktiv port:" + +#: glade/dgtkpref.glade:332 +msgid "0000" +msgstr "0000" + +#: glade/dgtkpref.glade:343 +msgid "Test Port" +msgstr "Test Port" + +#: glade/dgtkpref.glade:357 +msgid "TCP Port" +msgstr "TCP Port" + +#: glade/dgtkpref.glade:380 +msgid "(-1 is unlimited)" +msgstr "(-1 = ingen grense)" + +#: glade/dgtkpref.glade:390 glade/dgtkpref.glade:511 +msgid "KB/s" +msgstr "KB/s" + +#: glade/dgtkpref.glade:436 +msgid "Maximum Download Rate:" +msgstr "Maksimum Nedlastings Hastighet:" + +#: glade/dgtkpref.glade:447 +msgid "Upload Slots" +msgstr "Last opp del" + +#: glade/dgtkpref.glade:458 +msgid "Maximum Connections" +msgstr "Maksimal antall tilkoblinger" + +#: glade/dgtkpref.glade:485 +msgid "Maximum Upload Rate:" +msgstr "Maksimum Opplastings Hastighet:" + +#: glade/dgtkpref.glade:548 +msgid "Bandwidth " +msgstr "Båndbredde " + +#: glade/dgtkpref.glade:576 +msgid "Network" +msgstr "Nettverk" + +#: glade/dgtkpref.glade:598 +msgid "gtk-cancel" +msgstr "gtk-cancel" + +#: glade/dgtkpref.glade:605 +msgid "gtk-ok" +msgstr "gtk-ok" + +#: glade/dgtkpref.glade:625 +msgid "Plugin Manager" +msgstr "Modulbehandler" + +#: glade/dgtkpref.glade:718 +msgid "gtk-close" +msgstr "gtk-lukke" + +#: src/dcommon.py:41 src/delugegtk.py:330 +msgid "Infinity" +msgstr "Evig" + +#: src/dcommon.py:105 +msgid "Error: no webbrowser found" +msgstr "Feil: Ingen nettleser funnet" + +#: src/deluge.py:332 +msgid "File was not found" +msgstr "Filen ble ikke funnet" + +#: src/delugegtk.py:173 +msgid "Show / Hide Window" +msgstr "Vis / Skjul vindu" + +#: src/delugegtk.py:250 +msgid "Plugin" +msgstr "Programtillegg" + +#: src/delugegtk.py:252 +msgid "Enabled" +msgstr "Aktiv" + +#: src/delugegtk.py:340 +msgid "Unknown" +msgstr "Ukjent" + +#: src/delugegtk.py:348 +msgid "Name" +msgstr "Navn" + +#: src/delugegtk.py:356 +msgid "Ratio" +msgstr "Rate" + +#: src/delugegtk.py:465 +msgid "IP Address" +msgstr "IP Adresse" + +#: src/delugegtk.py:466 +msgid "Client" +msgstr "Klient" + +#: src/delugegtk.py:467 +msgid "Percent Complete" +msgstr "Prosent Ferdig" + +#: src/delugegtk.py:468 +msgid "Download Rate" +msgstr "Nedlastingsrate" + +#: src/delugegtk.py:469 +msgid "Upload Rate" +msgstr "Opplastingsrate" + +#: src/delugegtk.py:478 +msgid "Filename" +msgstr "Filnavn" + +#: src/delugegtk.py:480 +msgid "Offset" +msgstr "Forskyvning:" + +#: src/delugegtk.py:668 +#, fuzzy +msgid "" +"For some reason, the previous state could not be loaded, so a blank state " +"has been loaded for you." +msgstr "" +"Siden den forrige status kunne ikke bli brukt, så en ny status har blitt " +"laget for deg" + +#: src/delugegtk.py:670 +#, fuzzy +msgid "Would you like to attempt to reload the previous session's downloads?" +msgstr "Vil du prøve å re laste forrige session nedlastning" + +#: src/delugegtk.py:679 +msgid "Choose the download directory for" +msgstr "Velg nedlastingsmappe for" + +#: src/delugegtk.py:692 src/delugegtk.py:704 +msgid "Connections" +msgstr "Tilkoblinger" + +#: src/delugegtk.py:703 +msgid "Deluge Bittorrent Client" +msgstr "Deluge Bittorrent Client" + +#: src/delugegtk.py:891 +msgid "Add torrent from URL" +msgstr "Legg til en torrent fra URL" + +#: src/delugegtk.py:895 +msgid "Enter the URL of the .torrent to download" +msgstr "Skriv inn URL til torrenten du vil laste ned" + +#: src/delugegtk.py:959 +msgid "Warning - all downloaded files for this torrent will be deleted!" +msgstr "" +"Advarsel - alle nedlastede filer for denne torrenten vil bli slettet!" + +#: src/dgtk.py:71 +msgid "Choose a .torrent file" +msgstr "Velg en torrent fil" + +#: src/dgtk.py:76 +msgid "Torrent files" +msgstr "Torent filer" + +#: src/dgtk.py:80 +msgid "All files" +msgstr "Alle filer" + +#: src/dgtk.py:97 +msgid "Choose a download directory" +msgstr "Velg lagringsplass" \ No newline at end of file diff --git a/encryption/po/nl.po b/encryption/po/nl.po new file mode 100644 index 000000000..73bc2b240 --- /dev/null +++ b/encryption/po/nl.po @@ -0,0 +1,471 @@ +# Dutch translation for deluge +# Copyright (c) 2006 Rosetta Contributors and Canonical Ltd 2006 +# This file is distributed under the same license as the deluge package. +# FIRST AUTHOR , 2006. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: deluge\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2007-03-06 16:19-0500\n" +"PO-Revision-Date: 2007-02-27 11:00+0000\n" +"Last-Translator: mithras86 \n" +"Language-Team: Dutch \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Rosetta-Export-Date: 2007-03-18 15:58+0000\n" + +#: glade/delugegtk.glade:93 +msgid "Estimated Time Remaining:" +msgstr "ETA:" + +#: glade/delugegtk.glade:131 +msgid "Peers:" +msgstr "Peers:" + +#: glade/delugegtk.glade:145 +msgid "Seeders:" +msgstr "Seeders:" + +#: glade/delugegtk.glade:157 +msgid "Total Size:" +msgstr "Totale omvang:" + +#: glade/delugegtk.glade:169 +msgid "Total Downloaded:" +msgstr "Totaal gedownload:" + +#: glade/delugegtk.glade:181 +msgid "Downloaded this session:" +msgstr "Deze sessie gedownload:" + +#: glade/delugegtk.glade:229 +msgid "Tracker:" +msgstr "Tracker:" + +#: glade/delugegtk.glade:241 +msgid "Tracker Response:" +msgstr "Antwoord van de Tracker:" + +#: glade/delugegtk.glade:253 +msgid "Tracker Status:" +msgstr "Tracker Status:" + +#: glade/delugegtk.glade:277 +msgid "Next Announce:" +msgstr "Volgende aankondiging:" + +#: glade/delugegtk.glade:303 +msgid "Pieces:" +msgstr "Delen:" + +#: glade/delugegtk.glade:317 +msgid "Total Uploaded:" +msgstr "Totaal geupload:" + +#: glade/delugegtk.glade:355 +msgid "Share Ratio:" +msgstr "Deel ratio" + +#: glade/delugegtk.glade:369 +msgid "Uploaded This Session:" +msgstr "Geupload deze sessie:" + +#: glade/delugegtk.glade:431 +msgid "Use compact storage allocation:" +msgstr "Gebruik compact bestandsopslag:" + +#: glade/delugegtk.glade:455 +msgid "Download Rate:" +msgstr "Download ratio:" + +#: glade/delugegtk.glade:479 +msgid "Upload Rate:" +msgstr "Upload ratio:" + +#: glade/delugegtk.glade:522 glade/delugegtk.glade:746 +msgid "Details" +msgstr "Bijzonderheden" + +#: glade/delugegtk.glade:549 glade/delugegtk.glade:790 +#: glade/dgtkpopups.glade:37 src/delugegtk.py:352 +msgid "Peers" +msgstr "Peers" + +#: glade/delugegtk.glade:578 +msgid "Files" +msgstr "Bestanden" + +#: glade/delugegtk.glade:613 +msgid "_File" +msgstr "_Bestand" + +#: glade/delugegtk.glade:620 glade/delugegtk.glade:901 src/delugegtk.py:174 +msgid "Add Torrent" +msgstr "Torrent toevoegen" + +#: glade/delugegtk.glade:636 +msgid "Add Torrent from URL" +msgstr "Torrent toevoegen vanaf URL" + +#: glade/delugegtk.glade:644 glade/delugegtk.glade:914 +#: glade/dgtkpopups.glade:81 +msgid "Remove Torrent" +msgstr "Torrent verwijderen" + +#: glade/delugegtk.glade:658 +msgid "Clear Completed" +msgstr "Opschoning voltooid" + +#: glade/delugegtk.glade:692 +msgid "_Edit" +msgstr "Be_werken" + +#: glade/delugegtk.glade:709 glade/dgtkpopups.glade:221 +#: glade/dgtkpref.glade:697 src/delugegtk.py:177 +msgid "Plugins" +msgstr "Plug-ins" + +#: glade/delugegtk.glade:729 +msgid "_View" +msgstr "_Overzicht" + +#: glade/delugegtk.glade:737 +msgid "Toolbar" +msgstr "Werkbalk" + +#: glade/delugegtk.glade:755 +msgid "Columns" +msgstr "Kolommen" + +#: glade/delugegtk.glade:763 glade/dgtkpopups.glade:10 src/delugegtk.py:349 +#: src/delugegtk.py:479 +msgid "Size" +msgstr "Grootte" + +#: glade/delugegtk.glade:772 glade/dgtkpopups.glade:19 src/delugegtk.py:350 +msgid "Status" +msgstr "Status" + +#: glade/delugegtk.glade:781 glade/dgtkpopups.glade:28 src/delugegtk.py:351 +msgid "Seeders" +msgstr "Seeders" + +#: glade/delugegtk.glade:799 src/delugegtk.py:353 src/delugegtk.py:477 +#: src/delugegtk.py:692 src/delugegtk.py:704 +msgid "Download" +msgstr "Download" + +#: glade/delugegtk.glade:808 src/delugegtk.py:354 src/delugegtk.py:693 +#: src/delugegtk.py:705 +msgid "Upload" +msgstr "Upload" + +#: glade/delugegtk.glade:817 glade/dgtkpopups.glade:64 src/delugegtk.py:355 +msgid "Time Remaining" +msgstr "Overgebleven Tijd" + +#: glade/delugegtk.glade:826 glade/dgtkpopups.glade:73 +msgid "Share Ratio" +msgstr "Delingsratio" + +#: glade/delugegtk.glade:843 +msgid "_Help" +msgstr "_Hulp" + +#: glade/delugegtk.glade:927 +msgid "Clear Finished Torrents" +msgstr "Voltooide torrents verwijderen" + +#: glade/delugegtk.glade:949 +msgid "Start / Pause" +msgstr "" + +#: glade/delugegtk.glade:962 +msgid "Queue Torrent Up" +msgstr "Torrent in wachtrij omhoog" + +#: glade/delugegtk.glade:975 +msgid "Queue Torrent Down" +msgstr "Torrent in wachtrij omlaag" + +#: glade/dgtkpopups.glade:46 +msgid "Download Speed" +msgstr "Download snelheid" + +#: glade/dgtkpopups.glade:55 +msgid "Upload Speed" +msgstr "Upload snelheid" + +#: glade/dgtkpopups.glade:97 +msgid "" +"Are you sure you want to remove the selected torrent(s) from Deluge?" +msgstr "" +"Weet u zeker dat u de geselcteerde torrent(s) uit Deluge wilt " +"verwijderen?" + +#: glade/dgtkpopups.glade:107 +msgid "Delete downloaded files" +msgstr "Gedownloade bestanden verwijderen" + +#: glade/dgtkpopups.glade:167 +msgid "Show/Hide" +msgstr "Weergeven/verbergen" + +#: glade/dgtkpopups.glade:175 +msgid "Add a Torrent..." +msgstr "Torrent toevoegen" + +#: glade/dgtkpopups.glade:191 src/delugegtk.py:175 +msgid "Clear Finished" +msgstr "Voltooide torrents verwijderen" + +#: glade/dgtkpopups.glade:212 glade/dgtkpref.glade:672 +msgid "gtk-preferences" +msgstr "gtk-preferences" + +#: glade/dgtkpopups.glade:242 +msgid "gtk-quit" +msgstr "gtk-afsluiten" + +#: glade/dgtkpref.glade:8 +msgid "Preferences Dialog" +msgstr "Voorkeurdialoog" + +#: glade/dgtkpref.glade:53 +msgid "Enable system tray icon" +msgstr "Systeemvakpictogram inschakelen" + +#: glade/dgtkpref.glade:65 +msgid "Minimize to tray on close" +msgstr "Minimaliseer naar tray bij afsluiten" + +#: glade/dgtkpref.glade:80 +msgid "Options" +msgstr "Opties" + +#: glade/dgtkpref.glade:115 +msgid "Save all downloads to:" +msgstr "Alle downloads opslaan in:" + +#: glade/dgtkpref.glade:127 +msgid "Ask me where to save each download" +msgstr "Vraag mij waar elke download op te slaan" + +#: glade/dgtkpref.glade:139 +msgid "Save Location" +msgstr "Opslaglocatie" + +#: glade/dgtkpref.glade:180 +msgid "" +"Stop seeding torrents when\n" +"their share ratio reaches:" +msgstr "" +"Stop seeden van torrent wanneer\n" +"deelratio de volgende waarde bereikt:" + +#: glade/dgtkpref.glade:190 +msgid "Seeding" +msgstr "Seeding" + +#: glade/dgtkpref.glade:209 +msgid "Use compact storage allocation" +msgstr "Gebruik compacte opslagallocatie" + +#: glade/dgtkpref.glade:216 +msgid "Storage" +msgstr "Opslag" + +#: glade/dgtkpref.glade:246 +msgid "General" +msgstr "Standaard" + +#: glade/dgtkpref.glade:280 +msgid "Try from:" +msgstr "Probeer van:" + +#: glade/dgtkpref.glade:286 +msgid "to:" +msgstr "tot:" + +#: glade/dgtkpref.glade:320 +msgid "Active port:" +msgstr "Actieve poort:" + +#: glade/dgtkpref.glade:332 +msgid "0000" +msgstr "0000" + +#: glade/dgtkpref.glade:343 +msgid "Test Port" +msgstr "Test poort" + +#: glade/dgtkpref.glade:357 +msgid "TCP Port" +msgstr "TCP-poort" + +#: glade/dgtkpref.glade:380 +msgid "(-1 is unlimited)" +msgstr "(-1 is oneindig)" + +#: glade/dgtkpref.glade:390 glade/dgtkpref.glade:511 +msgid "KB/s" +msgstr "KB/s" + +#: glade/dgtkpref.glade:436 +msgid "Maximum Download Rate:" +msgstr "Maximum download snelheid:" + +#: glade/dgtkpref.glade:447 +msgid "Upload Slots" +msgstr "" + +#: glade/dgtkpref.glade:458 +msgid "Maximum Connections" +msgstr "" + +#: glade/dgtkpref.glade:485 +msgid "Maximum Upload Rate:" +msgstr "Maximum upload snelheid" + +#: glade/dgtkpref.glade:548 +msgid "Bandwidth " +msgstr "Bandbreedte " + +#: glade/dgtkpref.glade:576 +msgid "Network" +msgstr "Netwerk" + +#: glade/dgtkpref.glade:598 +msgid "gtk-cancel" +msgstr "gtk-cancel" + +#: glade/dgtkpref.glade:605 +msgid "gtk-ok" +msgstr "gtk-ok" + +#: glade/dgtkpref.glade:625 +msgid "Plugin Manager" +msgstr "Pluginbeheerder" + +#: glade/dgtkpref.glade:718 +msgid "gtk-close" +msgstr "gtk-sluiten" + +#: src/dcommon.py:41 src/delugegtk.py:330 +msgid "Infinity" +msgstr "Oneindig" + +#: src/dcommon.py:105 +msgid "Error: no webbrowser found" +msgstr "Error: webbrowser niet gevonden" + +#: src/deluge.py:332 +msgid "File was not found" +msgstr "" + +#: src/delugegtk.py:173 +msgid "Show / Hide Window" +msgstr "" + +#: src/delugegtk.py:250 +msgid "Plugin" +msgstr "Plugin" + +#: src/delugegtk.py:252 +msgid "Enabled" +msgstr "Geactiveerd" + +#: src/delugegtk.py:340 +msgid "Unknown" +msgstr "Onbekend" + +#: src/delugegtk.py:348 +msgid "Name" +msgstr "Naam" + +#: src/delugegtk.py:356 +msgid "Ratio" +msgstr "Verhouding" + +#: src/delugegtk.py:465 +msgid "IP Address" +msgstr "IP-adres" + +#: src/delugegtk.py:466 +msgid "Client" +msgstr "Client" + +#: src/delugegtk.py:467 +msgid "Percent Complete" +msgstr "Percentage voltooid" + +#: src/delugegtk.py:468 +msgid "Download Rate" +msgstr "Download ratio" + +#: src/delugegtk.py:469 +msgid "Upload Rate" +msgstr "Upload ratio" + +#: src/delugegtk.py:478 +msgid "Filename" +msgstr "Bestandsnaam" + +#: src/delugegtk.py:480 +msgid "Offset" +msgstr "Verschuiving" + +#: src/delugegtk.py:668 +msgid "" +"For some reason, the previous state could not be loaded, so a blank state " +"has been loaded for you." +msgstr "" + +#: src/delugegtk.py:670 +msgid "Would you like to attempt to reload the previous session's downloads?" +msgstr "" + +#: src/delugegtk.py:679 +msgid "Choose the download directory for" +msgstr "" + +#: src/delugegtk.py:692 src/delugegtk.py:704 +msgid "Connections" +msgstr "Verbindingen" + +#: src/delugegtk.py:703 +msgid "Deluge Bittorrent Client" +msgstr "Deluge Bittorrent Client" + +#: src/delugegtk.py:891 +msgid "Add torrent from URL" +msgstr "Torrent toevoegen van URL" + +#: src/delugegtk.py:895 +msgid "Enter the URL of the .torrent to download" +msgstr "Voer URL in van .torrent bestand om te downloaden" + +#: src/delugegtk.py:959 +msgid "Warning - all downloaded files for this torrent will be deleted!" +msgstr "" +"Waarschuwing - alle gedownloadde bestanden van deze torrent zullen " +"verwijderd worden!" + +#: src/dgtk.py:71 +msgid "Choose a .torrent file" +msgstr "Kies een .torrent bestand" + +#: src/dgtk.py:76 +msgid "Torrent files" +msgstr "Torrent-bestanden" + +#: src/dgtk.py:80 +msgid "All files" +msgstr "Alle bestanden" + +#: src/dgtk.py:97 +msgid "Choose a download directory" +msgstr "Kies een opslaglocatie" \ No newline at end of file diff --git a/encryption/po/pl.po b/encryption/po/pl.po new file mode 100644 index 000000000..bd71a1745 --- /dev/null +++ b/encryption/po/pl.po @@ -0,0 +1,468 @@ +# Polish translation for deluge +# Copyright (c) 2006 Rosetta Contributors and Canonical Ltd 2006 +# This file is distributed under the same license as the deluge package. +# FIRST AUTHOR , 2006. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: deluge\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2007-03-06 16:19-0500\n" +"PO-Revision-Date: 2007-03-11 19:47+0000\n" +"Last-Translator: Arkadiusz Kalinowski \n" +"Language-Team: Polish \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Rosetta-Export-Date: 2007-03-12 21:18+0000\n" + +#: glade/delugegtk.glade:93 +msgid "Estimated Time Remaining:" +msgstr "Czas do zakończenia:" + +#: glade/delugegtk.glade:131 +msgid "Peers:" +msgstr "Pobierających:" + +#: glade/delugegtk.glade:145 +msgid "Seeders:" +msgstr "Udostępniających:" + +#: glade/delugegtk.glade:157 +msgid "Total Size:" +msgstr "Rozmiar:" + +#: glade/delugegtk.glade:169 +msgid "Total Downloaded:" +msgstr "Pobrano w sumie:" + +#: glade/delugegtk.glade:181 +msgid "Downloaded this session:" +msgstr "Pobrane podczas tej sesji:" + +#: glade/delugegtk.glade:229 +msgid "Tracker:" +msgstr "Tracker:" + +#: glade/delugegtk.glade:241 +msgid "Tracker Response:" +msgstr "Odpowiedź trackera:" + +#: glade/delugegtk.glade:253 +msgid "Tracker Status:" +msgstr "Status trackera:" + +#: glade/delugegtk.glade:277 +msgid "Next Announce:" +msgstr "Następna zapowiedź:" + +#: glade/delugegtk.glade:303 +msgid "Pieces:" +msgstr "Części:" + +#: glade/delugegtk.glade:317 +msgid "Total Uploaded:" +msgstr "Wysłano w sumie:" + +#: glade/delugegtk.glade:355 +msgid "Share Ratio:" +msgstr "Udział w %:" + +#: glade/delugegtk.glade:369 +msgid "Uploaded This Session:" +msgstr "Wysłano w tej sesji:" + +#: glade/delugegtk.glade:431 +msgid "Use compact storage allocation:" +msgstr "Oszczędne przechowywanie danych:" + +#: glade/delugegtk.glade:455 +msgid "Download Rate:" +msgstr "Prędkość pobierania:" + +#: glade/delugegtk.glade:479 +msgid "Upload Rate:" +msgstr "Prędkość wysyłania:" + +#: glade/delugegtk.glade:522 glade/delugegtk.glade:746 +msgid "Details" +msgstr "Szczegóły" + +#: glade/delugegtk.glade:549 glade/delugegtk.glade:790 +#: glade/dgtkpopups.glade:37 src/delugegtk.py:352 +msgid "Peers" +msgstr "Pobierających" + +#: glade/delugegtk.glade:578 +msgid "Files" +msgstr "Pliki" + +#: glade/delugegtk.glade:613 +msgid "_File" +msgstr "_Plik" + +#: glade/delugegtk.glade:620 glade/delugegtk.glade:901 src/delugegtk.py:174 +msgid "Add Torrent" +msgstr "Dodaj plik torrent" + +#: glade/delugegtk.glade:636 +msgid "Add Torrent from URL" +msgstr "Dodaj .torrent z URL" + +#: glade/delugegtk.glade:644 glade/delugegtk.glade:914 +#: glade/dgtkpopups.glade:81 +msgid "Remove Torrent" +msgstr "Usuń torrent" + +#: glade/delugegtk.glade:658 +msgid "Clear Completed" +msgstr "Czyść zakończone" + +#: glade/delugegtk.glade:692 +msgid "_Edit" +msgstr "_Edycja" + +#: glade/delugegtk.glade:709 glade/dgtkpopups.glade:221 +#: glade/dgtkpref.glade:697 src/delugegtk.py:177 +msgid "Plugins" +msgstr "Wtyczki" + +#: glade/delugegtk.glade:729 +msgid "_View" +msgstr "_Widok" + +#: glade/delugegtk.glade:737 +msgid "Toolbar" +msgstr "Pasek narzędziowy" + +#: glade/delugegtk.glade:755 +msgid "Columns" +msgstr "Kolumny" + +#: glade/delugegtk.glade:763 glade/dgtkpopups.glade:10 src/delugegtk.py:349 +#: src/delugegtk.py:479 +msgid "Size" +msgstr "Rozmiar" + +#: glade/delugegtk.glade:772 glade/dgtkpopups.glade:19 src/delugegtk.py:350 +msgid "Status" +msgstr "Stan" + +#: glade/delugegtk.glade:781 glade/dgtkpopups.glade:28 src/delugegtk.py:351 +msgid "Seeders" +msgstr "Udostępniających" + +#: glade/delugegtk.glade:799 src/delugegtk.py:353 src/delugegtk.py:477 +#: src/delugegtk.py:692 src/delugegtk.py:704 +msgid "Download" +msgstr "Pobieranie" + +#: glade/delugegtk.glade:808 src/delugegtk.py:354 src/delugegtk.py:693 +#: src/delugegtk.py:705 +msgid "Upload" +msgstr "Wysyłanie" + +#: glade/delugegtk.glade:817 glade/dgtkpopups.glade:64 src/delugegtk.py:355 +msgid "Time Remaining" +msgstr "Pozostały czas" + +#: glade/delugegtk.glade:826 glade/dgtkpopups.glade:73 +msgid "Share Ratio" +msgstr "Ratio" + +#: glade/delugegtk.glade:843 +msgid "_Help" +msgstr "Pomo_c" + +#: glade/delugegtk.glade:927 +msgid "Clear Finished Torrents" +msgstr "Usuń zakończone" + +#: glade/delugegtk.glade:949 +msgid "Start / Pause" +msgstr "Rozpocznij / Wstrzymaj" + +#: glade/delugegtk.glade:962 +msgid "Queue Torrent Up" +msgstr "Przenieś o jedno miejsce w kolejce w górę" + +#: glade/delugegtk.glade:975 +msgid "Queue Torrent Down" +msgstr "Przenieś o jedno miejsce w kolejce w dół" + +#: glade/dgtkpopups.glade:46 +msgid "Download Speed" +msgstr "Prędkość pobierania" + +#: glade/dgtkpopups.glade:55 +msgid "Upload Speed" +msgstr "Wysyłanie" + +#: glade/dgtkpopups.glade:97 +msgid "" +"Are you sure you want to remove the selected torrent(s) from Deluge?" +msgstr "" +"Czy jesteś pewien, że chcesz usunąć wskazane torrenty z Deluge?" + +#: glade/dgtkpopups.glade:107 +msgid "Delete downloaded files" +msgstr "Skasuj pobrane pliki" + +#: glade/dgtkpopups.glade:167 +msgid "Show/Hide" +msgstr "Pokaż/Ukryj" + +#: glade/dgtkpopups.glade:175 +msgid "Add a Torrent..." +msgstr "Dodaj plik .torrent" + +#: glade/dgtkpopups.glade:191 src/delugegtk.py:175 +msgid "Clear Finished" +msgstr "Usuń zakończone" + +#: glade/dgtkpopups.glade:212 glade/dgtkpref.glade:672 +msgid "gtk-preferences" +msgstr "gtk-preferences" + +#: glade/dgtkpopups.glade:242 +msgid "gtk-quit" +msgstr "gtk-quit" + +#: glade/dgtkpref.glade:8 +msgid "Preferences Dialog" +msgstr "Ustawienia" + +#: glade/dgtkpref.glade:53 +msgid "Enable system tray icon" +msgstr "Pokaż ikonę na tacce systemowej" + +#: glade/dgtkpref.glade:65 +msgid "Minimize to tray on close" +msgstr "Po zamknięciu minimalizuj do ikony na tacce systemowej" + +#: glade/dgtkpref.glade:80 +msgid "Options" +msgstr "Opcje" + +#: glade/dgtkpref.glade:115 +msgid "Save all downloads to:" +msgstr "Zapisz wszystkie pliki do folderu:" + +#: glade/dgtkpref.glade:127 +msgid "Ask me where to save each download" +msgstr "Pytaj przed zapisaniem każdego pobierania" + +#: glade/dgtkpref.glade:139 +msgid "Save Location" +msgstr "Zapisz położenie" + +#: glade/dgtkpref.glade:180 +msgid "" +"Stop seeding torrents when\n" +"their share ratio reaches:" +msgstr "Zatrzymaj udostępnianie gdy twój udział osiągnie:" + +#: glade/dgtkpref.glade:190 +msgid "Seeding" +msgstr "Wysyłanie" + +#: glade/dgtkpref.glade:209 +msgid "Use compact storage allocation" +msgstr "Uzywaj oszczędnego przechowywania danych" + +#: glade/dgtkpref.glade:216 +msgid "Storage" +msgstr "Miejsce zapisu" + +#: glade/dgtkpref.glade:246 +msgid "General" +msgstr "Ogólne" + +#: glade/dgtkpref.glade:280 +msgid "Try from:" +msgstr "Spróbuj z:" + +#: glade/dgtkpref.glade:286 +msgid "to:" +msgstr "do:" + +#: glade/dgtkpref.glade:320 +msgid "Active port:" +msgstr "Aktywny port:" + +#: glade/dgtkpref.glade:332 +msgid "0000" +msgstr "0000" + +#: glade/dgtkpref.glade:343 +msgid "Test Port" +msgstr "Testuj port" + +#: glade/dgtkpref.glade:357 +msgid "TCP Port" +msgstr "Port TCP" + +#: glade/dgtkpref.glade:380 +msgid "(-1 is unlimited)" +msgstr "(-1 oznacza brak ograniczeń)" + +#: glade/dgtkpref.glade:390 glade/dgtkpref.glade:511 +msgid "KB/s" +msgstr "KB/s" + +#: glade/dgtkpref.glade:436 +msgid "Maximum Download Rate:" +msgstr "Maksymalna prędkość pobierania:" + +#: glade/dgtkpref.glade:447 +#, fuzzy +msgid "Upload Slots" +msgstr "Pozycje wysyłania" + +#: glade/dgtkpref.glade:458 +msgid "Maximum Connections" +msgstr "" + +#: glade/dgtkpref.glade:485 +msgid "Maximum Upload Rate:" +msgstr "Maksymalna prędkość wysyłania:" + +#: glade/dgtkpref.glade:548 +msgid "Bandwidth " +msgstr "Przepustowość " + +#: glade/dgtkpref.glade:576 +msgid "Network" +msgstr "Sieć" + +#: glade/dgtkpref.glade:598 +msgid "gtk-cancel" +msgstr "gtk-cancel" + +#: glade/dgtkpref.glade:605 +msgid "gtk-ok" +msgstr "gtk-ok" + +#: glade/dgtkpref.glade:625 +msgid "Plugin Manager" +msgstr "Menedżer wtyczek" + +#: glade/dgtkpref.glade:718 +msgid "gtk-close" +msgstr "gtk-close" + +#: src/dcommon.py:41 src/delugegtk.py:330 +msgid "Infinity" +msgstr "Nie do określenia" + +#: src/dcommon.py:105 +msgid "Error: no webbrowser found" +msgstr "Błąd: nie można uruchomić przeglądarki" + +#: src/deluge.py:332 +msgid "File was not found" +msgstr "" + +#: src/delugegtk.py:173 +msgid "Show / Hide Window" +msgstr "Pokaż / Ukryj Okno" + +#: src/delugegtk.py:250 +msgid "Plugin" +msgstr "Wtyczka" + +#: src/delugegtk.py:252 +msgid "Enabled" +msgstr "Aktywna" + +#: src/delugegtk.py:340 +msgid "Unknown" +msgstr "Nieznany" + +#: src/delugegtk.py:348 +msgid "Name" +msgstr "Nazwa" + +#: src/delugegtk.py:356 +msgid "Ratio" +msgstr "Ratio" + +#: src/delugegtk.py:465 +msgid "IP Address" +msgstr "Adres IP" + +#: src/delugegtk.py:466 +msgid "Client" +msgstr "Klient" + +#: src/delugegtk.py:467 +msgid "Percent Complete" +msgstr "Procent ukończenia" + +#: src/delugegtk.py:468 +msgid "Download Rate" +msgstr "Pobieranie" + +#: src/delugegtk.py:469 +msgid "Upload Rate" +msgstr "Wysyłanie" + +#: src/delugegtk.py:478 +msgid "Filename" +msgstr "Nazwa pliku" + +#: src/delugegtk.py:480 +msgid "Offset" +msgstr "Offset" + +#: src/delugegtk.py:668 +msgid "" +"For some reason, the previous state could not be loaded, so a blank state " +"has been loaded for you." +msgstr "" + +#: src/delugegtk.py:670 +msgid "Would you like to attempt to reload the previous session's downloads?" +msgstr "" + +#: src/delugegtk.py:679 +msgid "Choose the download directory for" +msgstr "" + +#: src/delugegtk.py:692 src/delugegtk.py:704 +msgid "Connections" +msgstr "Połączeń" + +#: src/delugegtk.py:703 +msgid "Deluge Bittorrent Client" +msgstr "Deluge - klient sieci Bittorrent" + +#: src/delugegtk.py:891 +msgid "Add torrent from URL" +msgstr "Dodaj plik .torrent" + +#: src/delugegtk.py:895 +msgid "Enter the URL of the .torrent to download" +msgstr "Wpisz URL prowadzący do pliku .torrent" + +#: src/delugegtk.py:959 +msgid "Warning - all downloaded files for this torrent will be deleted!" +msgstr "" +"Uwaga - wszystkie pliki ściągnięte dla tego torrenta zostaną skasowane!" + +#: src/dgtk.py:71 +msgid "Choose a .torrent file" +msgstr "Wybierz plik .torrent" + +#: src/dgtk.py:76 +msgid "Torrent files" +msgstr "Pliki .torrent" + +#: src/dgtk.py:80 +msgid "All files" +msgstr "Wszystkie pliki" + +#: src/dgtk.py:97 +msgid "Choose a download directory" +msgstr "Wskaż katalog, w którym zapisywane będą dane" \ No newline at end of file diff --git a/encryption/po/pt.po b/encryption/po/pt.po new file mode 100644 index 000000000..64f182501 --- /dev/null +++ b/encryption/po/pt.po @@ -0,0 +1,471 @@ +# Portuguese translation for deluge +# Copyright (c) 2006 Rosetta Contributors and Canonical Ltd 2006 +# This file is distributed under the same license as the deluge package. +# FIRST AUTHOR , 2006. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: deluge\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2007-03-06 16:19-0500\n" +"PO-Revision-Date: 2007-03-17 19:40+0000\n" +"Last-Translator: Pedro Brites Moita \n" +"Language-Team: Portuguese \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Rosetta-Export-Date: 2007-03-18 15:58+0000\n" + +#: glade/delugegtk.glade:93 +msgid "Estimated Time Remaining:" +msgstr "Tempo Remanescente Estimado:" + +#: glade/delugegtk.glade:131 +msgid "Peers:" +msgstr "Pares:" + +#: glade/delugegtk.glade:145 +msgid "Seeders:" +msgstr "Semeadores" + +#: glade/delugegtk.glade:157 +msgid "Total Size:" +msgstr "Tamanho Total:" + +#: glade/delugegtk.glade:169 +msgid "Total Downloaded:" +msgstr "Total Recebido" + +#: glade/delugegtk.glade:181 +msgid "Downloaded this session:" +msgstr "Recebido nesta sessão:" + +#: glade/delugegtk.glade:229 +msgid "Tracker:" +msgstr "Tracker:" + +#: glade/delugegtk.glade:241 +msgid "Tracker Response:" +msgstr "Resposta do Tracker:" + +#: glade/delugegtk.glade:253 +msgid "Tracker Status:" +msgstr "Estado do Tracker:" + +#: glade/delugegtk.glade:277 +msgid "Next Announce:" +msgstr "Próximo anuncio:" + +#: glade/delugegtk.glade:303 +#, fuzzy +msgid "Pieces:" +msgstr "Partes:" + +#: glade/delugegtk.glade:317 +msgid "Total Uploaded:" +msgstr "Total Enviado:" + +#: glade/delugegtk.glade:355 +msgid "Share Ratio:" +msgstr "Rácio de Partilha" + +#: glade/delugegtk.glade:369 +msgid "Uploaded This Session:" +msgstr "Enviado Nesta Sessão" + +#: glade/delugegtk.glade:431 +msgid "Use compact storage allocation:" +msgstr "Usar compactação do espaço de reserva:" + +#: glade/delugegtk.glade:455 +msgid "Download Rate:" +msgstr " Rácio de download " + +#: glade/delugegtk.glade:479 +msgid "Upload Rate:" +msgstr " Rácio de envio " + +#: glade/delugegtk.glade:522 glade/delugegtk.glade:746 +msgid "Details" +msgstr "Detalhes" + +#: glade/delugegtk.glade:549 glade/delugegtk.glade:790 +#: glade/dgtkpopups.glade:37 src/delugegtk.py:352 +msgid "Peers" +msgstr "Pares" + +#: glade/delugegtk.glade:578 +msgid "Files" +msgstr "Ficheiros" + +#: glade/delugegtk.glade:613 +msgid "_File" +msgstr "_Ficheiro" + +#: glade/delugegtk.glade:620 glade/delugegtk.glade:901 src/delugegtk.py:174 +msgid "Add Torrent" +msgstr "Adicionar Torrent" + +#: glade/delugegtk.glade:636 +msgid "Add Torrent from URL" +msgstr "Adicionar Torrent de endereço" + +#: glade/delugegtk.glade:644 glade/delugegtk.glade:914 +#: glade/dgtkpopups.glade:81 +msgid "Remove Torrent" +msgstr "Remover Torrent" + +#: glade/delugegtk.glade:658 +msgid "Clear Completed" +msgstr "Limpar Completos" + +#: glade/delugegtk.glade:692 +msgid "_Edit" +msgstr "_Editar" + +#: glade/delugegtk.glade:709 glade/dgtkpopups.glade:221 +#: glade/dgtkpref.glade:697 src/delugegtk.py:177 +msgid "Plugins" +msgstr "Extensões" + +#: glade/delugegtk.glade:729 +msgid "_View" +msgstr "_Ver" + +#: glade/delugegtk.glade:737 +msgid "Toolbar" +msgstr "Barra de ferramentas" + +#: glade/delugegtk.glade:755 +msgid "Columns" +msgstr "Colunas" + +#: glade/delugegtk.glade:763 glade/dgtkpopups.glade:10 src/delugegtk.py:349 +#: src/delugegtk.py:479 +msgid "Size" +msgstr "Tamanho" + +#: glade/delugegtk.glade:772 glade/dgtkpopups.glade:19 src/delugegtk.py:350 +msgid "Status" +msgstr "Estado" + +#: glade/delugegtk.glade:781 glade/dgtkpopups.glade:28 src/delugegtk.py:351 +msgid "Seeders" +msgstr "Semeadores" + +#: glade/delugegtk.glade:799 src/delugegtk.py:353 src/delugegtk.py:477 +#: src/delugegtk.py:692 src/delugegtk.py:704 +msgid "Download" +msgstr "Recepção" + +#: glade/delugegtk.glade:808 src/delugegtk.py:354 src/delugegtk.py:693 +#: src/delugegtk.py:705 +msgid "Upload" +msgstr "Envio" + +#: glade/delugegtk.glade:817 glade/dgtkpopups.glade:64 src/delugegtk.py:355 +msgid "Time Remaining" +msgstr "Tempo Remanescente" + +#: glade/delugegtk.glade:826 glade/dgtkpopups.glade:73 +msgid "Share Ratio" +msgstr "Rácio de Partilha" + +#: glade/delugegtk.glade:843 +msgid "_Help" +msgstr "_Ajuda" + +#: glade/delugegtk.glade:927 +msgid "Clear Finished Torrents" +msgstr "Limpar os Torrents Concluídos" + +#: glade/delugegtk.glade:949 +msgid "Start / Pause" +msgstr "Começar / Interromper" + +#: glade/delugegtk.glade:962 +msgid "Queue Torrent Up" +msgstr "Mover o torrent para cima na fila" + +#: glade/delugegtk.glade:975 +msgid "Queue Torrent Down" +msgstr "Mover o torrent para baixo na fila" + +#: glade/dgtkpopups.glade:46 +msgid "Download Speed" +msgstr "Velocidade de download" + +#: glade/dgtkpopups.glade:55 +msgid "Upload Speed" +msgstr "Velocidade de envio" + +#: glade/dgtkpopups.glade:97 +msgid "" +"Are you sure you want to remove the selected torrent(s) from Deluge?" +msgstr "" +"Tem a certeza que quer remover o(s) torrent(s) seleccionados do " +"Deluge?" + +#: glade/dgtkpopups.glade:107 +msgid "Delete downloaded files" +msgstr "Apagar ficheiros recebidos" + +#: glade/dgtkpopups.glade:167 +msgid "Show/Hide" +msgstr "Mostrar/Ocultar" + +#: glade/dgtkpopups.glade:175 +msgid "Add a Torrent..." +msgstr "Adicionar um Torrent..." + +#: glade/dgtkpopups.glade:191 src/delugegtk.py:175 +msgid "Clear Finished" +msgstr "Limpar os Concluídos" + +#: glade/dgtkpopups.glade:212 glade/dgtkpref.glade:672 +msgid "gtk-preferences" +msgstr "gtk-preferences" + +#: glade/dgtkpopups.glade:242 +msgid "gtk-quit" +msgstr "gtk-quit" + +#: glade/dgtkpref.glade:8 +msgid "Preferences Dialog" +msgstr "Preferências" + +#: glade/dgtkpref.glade:53 +msgid "Enable system tray icon" +msgstr "Activar o ícone na bandeja do sistema" + +#: glade/dgtkpref.glade:65 +msgid "Minimize to tray on close" +msgstr "Minimizar para a bandeja do sistema" + +#: glade/dgtkpref.glade:80 +msgid "Options" +msgstr "Opções" + +#: glade/dgtkpref.glade:115 +msgid "Save all downloads to:" +msgstr "Guardar todas as recepções em:" + +#: glade/dgtkpref.glade:127 +msgid "Ask me where to save each download" +msgstr "Perguntar-me onde guardar cada download" + +#: glade/dgtkpref.glade:139 +msgid "Save Location" +msgstr "Localização de Gravação" + +#: glade/dgtkpref.glade:180 +msgid "" +"Stop seeding torrents when\n" +"their share ratio reaches:" +msgstr "" +"Parar de semear torrents quando\n" +"o seu rácio de partilha chegar a:" + +#: glade/dgtkpref.glade:190 +msgid "Seeding" +msgstr "A semear" + +#: glade/dgtkpref.glade:209 +msgid "Use compact storage allocation" +msgstr "Usar alocação compacta do espaço de armazenamento" + +#: glade/dgtkpref.glade:216 +msgid "Storage" +msgstr "Armazenamento" + +#: glade/dgtkpref.glade:246 +msgid "General" +msgstr "Geral" + +#: glade/dgtkpref.glade:280 +msgid "Try from:" +msgstr "" + +#: glade/dgtkpref.glade:286 +msgid "to:" +msgstr "para:" + +#: glade/dgtkpref.glade:320 +msgid "Active port:" +msgstr "Porto Activo:" + +#: glade/dgtkpref.glade:332 +msgid "0000" +msgstr "0000" + +#: glade/dgtkpref.glade:343 +msgid "Test Port" +msgstr "Testar Porto" + +#: glade/dgtkpref.glade:357 +msgid "TCP Port" +msgstr "Porto TCP" + +#: glade/dgtkpref.glade:380 +msgid "(-1 is unlimited)" +msgstr "(-1 é ilimitado)" + +#: glade/dgtkpref.glade:390 glade/dgtkpref.glade:511 +msgid "KB/s" +msgstr "" + +#: glade/dgtkpref.glade:436 +msgid "Maximum Download Rate:" +msgstr "Velocidade Máxima de Recepção:" + +#: glade/dgtkpref.glade:447 +msgid "Upload Slots" +msgstr "" + +#: glade/dgtkpref.glade:458 +msgid "Maximum Connections" +msgstr "" + +#: glade/dgtkpref.glade:485 +msgid "Maximum Upload Rate:" +msgstr "Velocidade Máxima de Envio:" + +#: glade/dgtkpref.glade:548 +msgid "Bandwidth " +msgstr "" + +#: glade/dgtkpref.glade:576 +msgid "Network" +msgstr "Rede" + +#: glade/dgtkpref.glade:598 +msgid "gtk-cancel" +msgstr "gtk-cancel" + +#: glade/dgtkpref.glade:605 +msgid "gtk-ok" +msgstr "gtk-ok" + +#: glade/dgtkpref.glade:625 +msgid "Plugin Manager" +msgstr "Gestor de Extensões" + +#: glade/dgtkpref.glade:718 +msgid "gtk-close" +msgstr "gtk-close" + +#: src/dcommon.py:41 src/delugegtk.py:330 +msgid "Infinity" +msgstr "Infinito" + +#: src/dcommon.py:105 +msgid "Error: no webbrowser found" +msgstr "" + +#: src/deluge.py:332 +msgid "File was not found" +msgstr "" + +#: src/delugegtk.py:173 +msgid "Show / Hide Window" +msgstr "Mostrar / Esconder Janela" + +#: src/delugegtk.py:250 +msgid "Plugin" +msgstr "Extensão" + +#: src/delugegtk.py:252 +msgid "Enabled" +msgstr "Activado" + +#: src/delugegtk.py:340 +msgid "Unknown" +msgstr "Desconhecido" + +#: src/delugegtk.py:348 +msgid "Name" +msgstr "Nome" + +#: src/delugegtk.py:356 +msgid "Ratio" +msgstr "Rácio" + +#: src/delugegtk.py:465 +msgid "IP Address" +msgstr "Endereço IP" + +#: src/delugegtk.py:466 +msgid "Client" +msgstr "Cliente" + +#: src/delugegtk.py:467 +msgid "Percent Complete" +msgstr "Percentagem Completa" + +#: src/delugegtk.py:468 +msgid "Download Rate" +msgstr "" + +#: src/delugegtk.py:469 +msgid "Upload Rate" +msgstr "" + +#: src/delugegtk.py:478 +msgid "Filename" +msgstr "" + +#: src/delugegtk.py:480 +msgid "Offset" +msgstr "Deslocamento" + +#: src/delugegtk.py:668 +msgid "" +"For some reason, the previous state could not be loaded, so a blank state " +"has been loaded for you." +msgstr "" + +#: src/delugegtk.py:670 +msgid "Would you like to attempt to reload the previous session's downloads?" +msgstr "" + +#: src/delugegtk.py:679 +msgid "Choose the download directory for" +msgstr "" + +#: src/delugegtk.py:692 src/delugegtk.py:704 +msgid "Connections" +msgstr "Ligações" + +#: src/delugegtk.py:703 +msgid "Deluge Bittorrent Client" +msgstr "Cliente de Bittorrent Deluge" + +#: src/delugegtk.py:891 +msgid "Add torrent from URL" +msgstr "" + +#: src/delugegtk.py:895 +msgid "Enter the URL of the .torrent to download" +msgstr "" + +#: src/delugegtk.py:959 +msgid "Warning - all downloaded files for this torrent will be deleted!" +msgstr "Aviso - Todos os ficheiros recebidos deste torrent vão ser apagados!" + +#: src/dgtk.py:71 +msgid "Choose a .torrent file" +msgstr "Escolher um ficheiro .torrent" + +#: src/dgtk.py:76 +msgid "Torrent files" +msgstr "Ficheiros Torrent" + +#: src/dgtk.py:80 +msgid "All files" +msgstr "Todos os ficheiros" + +#: src/dgtk.py:97 +#, fuzzy +msgid "Choose a download directory" +msgstr "Escolha um directório para download" \ No newline at end of file diff --git a/encryption/po/pt_BR.po b/encryption/po/pt_BR.po new file mode 100644 index 000000000..a1bcb493e --- /dev/null +++ b/encryption/po/pt_BR.po @@ -0,0 +1,467 @@ +# Portuguese (Brazil) translation for deluge +# Copyright (c) 2006 Rosetta Contributors and Canonical Ltd 2006 +# This file is distributed under the same license as the deluge package. +# FIRST AUTHOR , 2006. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: deluge\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2007-03-06 16:19-0500\n" +"PO-Revision-Date: 2007-02-25 17:37+0000\n" +"Last-Translator: Philipi \n" +"Language-Team: Portuguese (Brazil) \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Rosetta-Export-Date: 2007-03-18 15:58+0000\n" + +#: glade/delugegtk.glade:93 +msgid "Estimated Time Remaining:" +msgstr "" + +#: glade/delugegtk.glade:131 +msgid "Peers:" +msgstr "" + +#: glade/delugegtk.glade:145 +msgid "Seeders:" +msgstr "" + +#: glade/delugegtk.glade:157 +msgid "Total Size:" +msgstr "Tamanho Total:" + +#: glade/delugegtk.glade:169 +msgid "Total Downloaded:" +msgstr "Total Baixado:" + +#: glade/delugegtk.glade:181 +msgid "Downloaded this session:" +msgstr "Baixado nesta sessão:" + +#: glade/delugegtk.glade:229 +msgid "Tracker:" +msgstr "Rastreador:" + +#: glade/delugegtk.glade:241 +msgid "Tracker Response:" +msgstr "Resposta do Rastreador:" + +#: glade/delugegtk.glade:253 +msgid "Tracker Status:" +msgstr "Estado do Rastreador:" + +#: glade/delugegtk.glade:277 +msgid "Next Announce:" +msgstr "Próximo Anúncio:" + +#: glade/delugegtk.glade:303 +msgid "Pieces:" +msgstr "Partes:" + +#: glade/delugegtk.glade:317 +msgid "Total Uploaded:" +msgstr "Total Enviado:" + +#: glade/delugegtk.glade:355 +msgid "Share Ratio:" +msgstr "Razão de Compartilhamento:" + +#: glade/delugegtk.glade:369 +msgid "Uploaded This Session:" +msgstr "Enviado nesta Sessão:" + +#: glade/delugegtk.glade:431 +msgid "Use compact storage allocation:" +msgstr "Utilizar alocação de armazenamento compacto:" + +#: glade/delugegtk.glade:455 +msgid "Download Rate:" +msgstr "" + +#: glade/delugegtk.glade:479 +msgid "Upload Rate:" +msgstr "" + +#: glade/delugegtk.glade:522 glade/delugegtk.glade:746 +msgid "Details" +msgstr "Detalhes" + +#: glade/delugegtk.glade:549 glade/delugegtk.glade:790 +#: glade/dgtkpopups.glade:37 src/delugegtk.py:352 +msgid "Peers" +msgstr "Colegas" + +#: glade/delugegtk.glade:578 +msgid "Files" +msgstr "" + +#: glade/delugegtk.glade:613 +msgid "_File" +msgstr "_Arquivo" + +#: glade/delugegtk.glade:620 glade/delugegtk.glade:901 src/delugegtk.py:174 +msgid "Add Torrent" +msgstr "Adicionar Torrent" + +#: glade/delugegtk.glade:636 +msgid "Add Torrent from URL" +msgstr "Adicionar Torrent à partir de uma URL" + +#: glade/delugegtk.glade:644 glade/delugegtk.glade:914 +#: glade/dgtkpopups.glade:81 +msgid "Remove Torrent" +msgstr "Remover Torrent" + +#: glade/delugegtk.glade:658 +msgid "Clear Completed" +msgstr "Limpar downloads completos" + +#: glade/delugegtk.glade:692 +msgid "_Edit" +msgstr "_Editar" + +#: glade/delugegtk.glade:709 glade/dgtkpopups.glade:221 +#: glade/dgtkpref.glade:697 src/delugegtk.py:177 +msgid "Plugins" +msgstr "Plugins" + +#: glade/delugegtk.glade:729 +msgid "_View" +msgstr "_Visualizar" + +#: glade/delugegtk.glade:737 +msgid "Toolbar" +msgstr "Barra de Ferramentas" + +#: glade/delugegtk.glade:755 +msgid "Columns" +msgstr "Colunas" + +#: glade/delugegtk.glade:763 glade/dgtkpopups.glade:10 src/delugegtk.py:349 +#: src/delugegtk.py:479 +msgid "Size" +msgstr "Tamanho" + +#: glade/delugegtk.glade:772 glade/dgtkpopups.glade:19 src/delugegtk.py:350 +msgid "Status" +msgstr "" + +#: glade/delugegtk.glade:781 glade/dgtkpopups.glade:28 src/delugegtk.py:351 +msgid "Seeders" +msgstr "Sementes" + +#: glade/delugegtk.glade:799 src/delugegtk.py:353 src/delugegtk.py:477 +#: src/delugegtk.py:692 src/delugegtk.py:704 +msgid "Download" +msgstr "Download" + +#: glade/delugegtk.glade:808 src/delugegtk.py:354 src/delugegtk.py:693 +#: src/delugegtk.py:705 +msgid "Upload" +msgstr "Upload" + +#: glade/delugegtk.glade:817 glade/dgtkpopups.glade:64 src/delugegtk.py:355 +msgid "Time Remaining" +msgstr "" + +#: glade/delugegtk.glade:826 glade/dgtkpopups.glade:73 +msgid "Share Ratio" +msgstr "Taxa de Compartilhamento" + +#: glade/delugegtk.glade:843 +msgid "_Help" +msgstr "Aj_uda" + +#: glade/delugegtk.glade:927 +msgid "Clear Finished Torrents" +msgstr "Limpar Torrents Concluídos" + +#: glade/delugegtk.glade:949 +msgid "Start / Pause" +msgstr "" + +#: glade/delugegtk.glade:962 +msgid "Queue Torrent Up" +msgstr "Passar para cima" + +#: glade/delugegtk.glade:975 +msgid "Queue Torrent Down" +msgstr "Passar para baixo" + +#: glade/dgtkpopups.glade:46 +msgid "Download Speed" +msgstr "" + +#: glade/dgtkpopups.glade:55 +msgid "Upload Speed" +msgstr "" + +#: glade/dgtkpopups.glade:97 +msgid "" +"Are you sure you want to remove the selected torrent(s) from Deluge?" +msgstr "" +"Tem certeza que deseja remover o(s) torrent(s) selecionado(s) do " +"Deluge?" + +#: glade/dgtkpopups.glade:107 +msgid "Delete downloaded files" +msgstr "Apagar os arquivos baixados" + +#: glade/dgtkpopups.glade:167 +msgid "Show/Hide" +msgstr "Mostrar/Ocultar" + +#: glade/dgtkpopups.glade:175 +msgid "Add a Torrent..." +msgstr "Adicionar um Torrent" + +#: glade/dgtkpopups.glade:191 src/delugegtk.py:175 +msgid "Clear Finished" +msgstr "Limpar Concluídos" + +#: glade/dgtkpopups.glade:212 glade/dgtkpref.glade:672 +msgid "gtk-preferences" +msgstr "gtk-preferences" + +#: glade/dgtkpopups.glade:242 +msgid "gtk-quit" +msgstr "Sair" + +#: glade/dgtkpref.glade:8 +msgid "Preferences Dialog" +msgstr "Janela de Preferências" + +#: glade/dgtkpref.glade:53 +msgid "Enable system tray icon" +msgstr "Habilitar ícone na área de notificação" + +#: glade/dgtkpref.glade:65 +msgid "Minimize to tray on close" +msgstr "Minimizar para a área de notificação" + +#: glade/dgtkpref.glade:80 +msgid "Options" +msgstr "Opções" + +#: glade/dgtkpref.glade:115 +msgid "Save all downloads to:" +msgstr "Salvar todos os downloads em:" + +#: glade/dgtkpref.glade:127 +msgid "Ask me where to save each download" +msgstr "Perguntar onde salvar cada download" + +#: glade/dgtkpref.glade:139 +msgid "Save Location" +msgstr "Salvar local" + +#: glade/dgtkpref.glade:180 +msgid "" +"Stop seeding torrents when\n" +"their share ratio reaches:" +msgstr "" + +#: glade/dgtkpref.glade:190 +msgid "Seeding" +msgstr "Semeando" + +#: glade/dgtkpref.glade:209 +msgid "Use compact storage allocation" +msgstr "" + +#: glade/dgtkpref.glade:216 +msgid "Storage" +msgstr "" + +#: glade/dgtkpref.glade:246 +msgid "General" +msgstr "" + +#: glade/dgtkpref.glade:280 +msgid "Try from:" +msgstr "Tentar de:" + +#: glade/dgtkpref.glade:286 +msgid "to:" +msgstr "" + +#: glade/dgtkpref.glade:320 +msgid "Active port:" +msgstr "Porta Ativa:" + +#: glade/dgtkpref.glade:332 +msgid "0000" +msgstr "" + +#: glade/dgtkpref.glade:343 +msgid "Test Port" +msgstr "Testar Porta" + +#: glade/dgtkpref.glade:357 +msgid "TCP Port" +msgstr "" + +#: glade/dgtkpref.glade:380 +msgid "(-1 is unlimited)" +msgstr "" + +#: glade/dgtkpref.glade:390 glade/dgtkpref.glade:511 +msgid "KB/s" +msgstr "" + +#: glade/dgtkpref.glade:436 +msgid "Maximum Download Rate:" +msgstr "Taxa Máxima de Download:" + +#: glade/dgtkpref.glade:447 +msgid "Upload Slots" +msgstr "" + +#: glade/dgtkpref.glade:458 +msgid "Maximum Connections" +msgstr "" + +#: glade/dgtkpref.glade:485 +msgid "Maximum Upload Rate:" +msgstr "Taxa Máxima de Upload:" + +#: glade/dgtkpref.glade:548 +msgid "Bandwidth " +msgstr "Largura de banda " + +#: glade/dgtkpref.glade:576 +msgid "Network" +msgstr "Rede" + +#: glade/dgtkpref.glade:598 +msgid "gtk-cancel" +msgstr "gtk-cancel" + +#: glade/dgtkpref.glade:605 +msgid "gtk-ok" +msgstr "gtk-ok" + +#: glade/dgtkpref.glade:625 +msgid "Plugin Manager" +msgstr "Gerenciador de Plugins" + +#: glade/dgtkpref.glade:718 +msgid "gtk-close" +msgstr "Fechar" + +#: src/dcommon.py:41 src/delugegtk.py:330 +msgid "Infinity" +msgstr "Infinidade" + +#: src/dcommon.py:105 +msgid "Error: no webbrowser found" +msgstr "Erro: nenhum navegador encontrado" + +#: src/deluge.py:332 +msgid "File was not found" +msgstr "" + +#: src/delugegtk.py:173 +msgid "Show / Hide Window" +msgstr "" + +#: src/delugegtk.py:250 +msgid "Plugin" +msgstr "Plugin" + +#: src/delugegtk.py:252 +msgid "Enabled" +msgstr "Ativo" + +#: src/delugegtk.py:340 +msgid "Unknown" +msgstr "Desconhecido" + +#: src/delugegtk.py:348 +msgid "Name" +msgstr "Nome" + +#: src/delugegtk.py:356 +msgid "Ratio" +msgstr "Razão" + +#: src/delugegtk.py:465 +msgid "IP Address" +msgstr "Endereço IP" + +#: src/delugegtk.py:466 +msgid "Client" +msgstr "Cliente" + +#: src/delugegtk.py:467 +msgid "Percent Complete" +msgstr "Porcentagem concluída" + +#: src/delugegtk.py:468 +msgid "Download Rate" +msgstr "Taxa de download" + +#: src/delugegtk.py:469 +msgid "Upload Rate" +msgstr "" + +#: src/delugegtk.py:478 +msgid "Filename" +msgstr "" + +#: src/delugegtk.py:480 +msgid "Offset" +msgstr "" + +#: src/delugegtk.py:668 +msgid "" +"For some reason, the previous state could not be loaded, so a blank state " +"has been loaded for you." +msgstr "" + +#: src/delugegtk.py:670 +msgid "Would you like to attempt to reload the previous session's downloads?" +msgstr "" + +#: src/delugegtk.py:679 +msgid "Choose the download directory for" +msgstr "" + +#: src/delugegtk.py:692 src/delugegtk.py:704 +msgid "Connections" +msgstr "Conexões" + +#: src/delugegtk.py:703 +msgid "Deluge Bittorrent Client" +msgstr "Cliente Bittorrent Deluge" + +#: src/delugegtk.py:891 +msgid "Add torrent from URL" +msgstr "" + +#: src/delugegtk.py:895 +msgid "Enter the URL of the .torrent to download" +msgstr "" + +#: src/delugegtk.py:959 +msgid "Warning - all downloaded files for this torrent will be deleted!" +msgstr "Atenção - todos os arquivos recebidos deste torrent serão excluídos!" + +#: src/dgtk.py:71 +msgid "Choose a .torrent file" +msgstr "" + +#: src/dgtk.py:76 +msgid "Torrent files" +msgstr "" + +#: src/dgtk.py:80 +msgid "All files" +msgstr "Todos os arquivos" + +#: src/dgtk.py:97 +msgid "Choose a download directory" +msgstr "" \ No newline at end of file diff --git a/encryption/po/ru.po b/encryption/po/ru.po new file mode 100644 index 000000000..53f222333 --- /dev/null +++ b/encryption/po/ru.po @@ -0,0 +1,470 @@ +# Russian translation for deluge +# Copyright (c) 2006 Rosetta Contributors and Canonical Ltd 2006 +# This file is distributed under the same license as the deluge package. +# FIRST AUTHOR , 2006. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: deluge\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2007-03-06 16:19-0500\n" +"PO-Revision-Date: 2007-03-14 10:22+0000\n" +"Last-Translator: nyo \n" +"Language-Team: Russian \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Rosetta-Export-Date: 2007-03-18 15:58+0000\n" + +#: glade/delugegtk.glade:93 +msgid "Estimated Time Remaining:" +msgstr "Осталось до завершения:" + +#: glade/delugegtk.glade:131 +msgid "Peers:" +msgstr "Пиры:" + +#: glade/delugegtk.glade:145 +msgid "Seeders:" +msgstr "Сидеры:" + +#: glade/delugegtk.glade:157 +msgid "Total Size:" +msgstr "Полный размер:" + +#: glade/delugegtk.glade:169 +msgid "Total Downloaded:" +msgstr "Всего загружено:" + +#: glade/delugegtk.glade:181 +msgid "Downloaded this session:" +msgstr "Загружено за этот сеанс:" + +#: glade/delugegtk.glade:229 +msgid "Tracker:" +msgstr "Трекер:" + +#: glade/delugegtk.glade:241 +msgid "Tracker Response:" +msgstr "Ответ трекера:" + +#: glade/delugegtk.glade:253 +msgid "Tracker Status:" +msgstr "Статус трекера:" + +#: glade/delugegtk.glade:277 +msgid "Next Announce:" +msgstr "Следующий анонс:" + +#: glade/delugegtk.glade:303 +msgid "Pieces:" +msgstr "Части:" + +#: glade/delugegtk.glade:317 +msgid "Total Uploaded:" +msgstr "Всего роздано:" + +#: glade/delugegtk.glade:355 +msgid "Share Ratio:" +msgstr "Рейтинг:" + +#: glade/delugegtk.glade:369 +msgid "Uploaded This Session:" +msgstr "Роздано за сессию:" + +#: glade/delugegtk.glade:431 +msgid "Use compact storage allocation:" +msgstr "Использовать компактное резервирование:" + +#: glade/delugegtk.glade:455 +msgid "Download Rate:" +msgstr "Скорость закачки:" + +#: glade/delugegtk.glade:479 +msgid "Upload Rate:" +msgstr "Скорость раздачи:" + +#: glade/delugegtk.glade:522 glade/delugegtk.glade:746 +msgid "Details" +msgstr "Подробнее" + +#: glade/delugegtk.glade:549 glade/delugegtk.glade:790 +#: glade/dgtkpopups.glade:37 src/delugegtk.py:352 +msgid "Peers" +msgstr "Пиры" + +#: glade/delugegtk.glade:578 +msgid "Files" +msgstr "Файлы" + +#: glade/delugegtk.glade:613 +msgid "_File" +msgstr "_Файл" + +#: glade/delugegtk.glade:620 glade/delugegtk.glade:901 src/delugegtk.py:174 +msgid "Add Torrent" +msgstr "Добавить торрент" + +#: glade/delugegtk.glade:636 +msgid "Add Torrent from URL" +msgstr "Добавить торрент из URL" + +#: glade/delugegtk.glade:644 glade/delugegtk.glade:914 +#: glade/dgtkpopups.glade:81 +msgid "Remove Torrent" +msgstr "Удалить торрент" + +#: glade/delugegtk.glade:658 +msgid "Clear Completed" +msgstr "Очистить завершённые" + +#: glade/delugegtk.glade:692 +msgid "_Edit" +msgstr "_Правка" + +#: glade/delugegtk.glade:709 glade/dgtkpopups.glade:221 +#: glade/dgtkpref.glade:697 src/delugegtk.py:177 +msgid "Plugins" +msgstr "Дополнения" + +#: glade/delugegtk.glade:729 +msgid "_View" +msgstr "_Вид" + +#: glade/delugegtk.glade:737 +msgid "Toolbar" +msgstr "Панель" + +#: glade/delugegtk.glade:755 +msgid "Columns" +msgstr "Столбцы" + +#: glade/delugegtk.glade:763 glade/dgtkpopups.glade:10 src/delugegtk.py:349 +#: src/delugegtk.py:479 +msgid "Size" +msgstr "Размер" + +#: glade/delugegtk.glade:772 glade/dgtkpopups.glade:19 src/delugegtk.py:350 +msgid "Status" +msgstr "Статус" + +#: glade/delugegtk.glade:781 glade/dgtkpopups.glade:28 src/delugegtk.py:351 +msgid "Seeders" +msgstr "Раздающие" + +#: glade/delugegtk.glade:799 src/delugegtk.py:353 src/delugegtk.py:477 +#: src/delugegtk.py:692 src/delugegtk.py:704 +msgid "Download" +msgstr "Загрузка" + +#: glade/delugegtk.glade:808 src/delugegtk.py:354 src/delugegtk.py:693 +#: src/delugegtk.py:705 +msgid "Upload" +msgstr "Раздача" + +#: glade/delugegtk.glade:817 glade/dgtkpopups.glade:64 src/delugegtk.py:355 +msgid "Time Remaining" +msgstr "Оставшееся время" + +#: glade/delugegtk.glade:826 glade/dgtkpopups.glade:73 +msgid "Share Ratio" +msgstr "Рейтинг" + +#: glade/delugegtk.glade:843 +msgid "_Help" +msgstr "_Справка" + +#: glade/delugegtk.glade:927 +msgid "Clear Finished Torrents" +msgstr "Удалить завершённые торренты" + +#: glade/delugegtk.glade:949 +msgid "Start / Pause" +msgstr "Старт/Пауза" + +#: glade/delugegtk.glade:962 +msgid "Queue Torrent Up" +msgstr "Вверх в очереди" + +#: glade/delugegtk.glade:975 +msgid "Queue Torrent Down" +msgstr "Вниз в очереди" + +#: glade/dgtkpopups.glade:46 +msgid "Download Speed" +msgstr "Скорость скачивания" + +#: glade/dgtkpopups.glade:55 +msgid "Upload Speed" +msgstr "Скорость раздачи" + +#: glade/dgtkpopups.glade:97 +msgid "" +"Are you sure you want to remove the selected torrent(s) from Deluge?" +msgstr "Вы уверены, что хотите удалить выбранные торренты из Deluge?" + +#: glade/dgtkpopups.glade:107 +msgid "Delete downloaded files" +msgstr "Удалить скачаные файлы" + +#: glade/dgtkpopups.glade:167 +msgid "Show/Hide" +msgstr "Показать/Скрыть" + +#: glade/dgtkpopups.glade:175 +msgid "Add a Torrent..." +msgstr "Добавить торрент..." + +#: glade/dgtkpopups.glade:191 src/delugegtk.py:175 +msgid "Clear Finished" +msgstr "Удалить завершённые" + +#: glade/dgtkpopups.glade:212 glade/dgtkpref.glade:672 +msgid "gtk-preferences" +msgstr "настройки Gtk" + +#: glade/dgtkpopups.glade:242 +msgid "gtk-quit" +msgstr "Выход" + +#: glade/dgtkpref.glade:8 +msgid "Preferences Dialog" +msgstr "Меню настройки" + +#: glade/dgtkpref.glade:53 +msgid "Enable system tray icon" +msgstr "Включить иконку в системном лотке" + +#: glade/dgtkpref.glade:65 +msgid "Minimize to tray on close" +msgstr "Сворачивать в системный лоток при закрытии" + +#: glade/dgtkpref.glade:80 +msgid "Options" +msgstr "Параметры" + +#: glade/dgtkpref.glade:115 +msgid "Save all downloads to:" +msgstr "Сохранять все загрузки в:" + +#: glade/dgtkpref.glade:127 +msgid "Ask me where to save each download" +msgstr "Спрашивать куда сохранить каждую загрузку" + +#: glade/dgtkpref.glade:139 +msgid "Save Location" +msgstr "Место сохранения" + +#: glade/dgtkpref.glade:180 +msgid "" +"Stop seeding torrents when\n" +"their share ratio reaches:" +msgstr "" +"Прекратить раздачу торрентов\n" +"когда рейтинг достигнет:" + +#: glade/dgtkpref.glade:190 +msgid "Seeding" +msgstr "Раздача" + +#: glade/dgtkpref.glade:209 +msgid "Use compact storage allocation" +msgstr "Использовать компактное резервирование" + +#: glade/dgtkpref.glade:216 +msgid "Storage" +msgstr "Хранилище" + +#: glade/dgtkpref.glade:246 +msgid "General" +msgstr "Общее" + +#: glade/dgtkpref.glade:280 +msgid "Try from:" +msgstr "Пробовать с:" + +#: glade/dgtkpref.glade:286 +msgid "to:" +msgstr "в:" + +#: glade/dgtkpref.glade:320 +msgid "Active port:" +msgstr "Активный порт:" + +#: glade/dgtkpref.glade:332 +msgid "0000" +msgstr "0000" + +#: glade/dgtkpref.glade:343 +msgid "Test Port" +msgstr "Проверить порт" + +#: glade/dgtkpref.glade:357 +msgid "TCP Port" +msgstr "Порт TCP" + +#: glade/dgtkpref.glade:380 +msgid "(-1 is unlimited)" +msgstr "(-1 значит без ограничения)" + +#: glade/dgtkpref.glade:390 glade/dgtkpref.glade:511 +msgid "KB/s" +msgstr "КБ/с" + +#: glade/dgtkpref.glade:436 +msgid "Maximum Download Rate:" +msgstr "Максимальная скорость загрузки:" + +#: glade/dgtkpref.glade:447 +msgid "Upload Slots" +msgstr "Слоты раздачи" + +#: glade/dgtkpref.glade:458 +msgid "Maximum Connections" +msgstr "Максимум соединений" + +#: glade/dgtkpref.glade:485 +msgid "Maximum Upload Rate:" +msgstr "Максимальная скорость раздачи:" + +#: glade/dgtkpref.glade:548 +msgid "Bandwidth " +msgstr "Канал " + +#: glade/dgtkpref.glade:576 +msgid "Network" +msgstr "Сеть" + +#: glade/dgtkpref.glade:598 +msgid "gtk-cancel" +msgstr "Отмена" + +#: glade/dgtkpref.glade:605 +msgid "gtk-ok" +msgstr "ОК" + +#: glade/dgtkpref.glade:625 +msgid "Plugin Manager" +msgstr "Управление дополнениями" + +#: glade/dgtkpref.glade:718 +msgid "gtk-close" +msgstr "Закрыть" + +#: src/dcommon.py:41 src/delugegtk.py:330 +msgid "Infinity" +msgstr "Бесконечность" + +#: src/dcommon.py:105 +msgid "Error: no webbrowser found" +msgstr "Ошибка: не найден веб-браузер" + +#: src/deluge.py:332 +msgid "File was not found" +msgstr "Файл не найден" + +#: src/delugegtk.py:173 +msgid "Show / Hide Window" +msgstr "Показать/Скрыть окно" + +#: src/delugegtk.py:250 +msgid "Plugin" +msgstr "Дополнение" + +#: src/delugegtk.py:252 +msgid "Enabled" +msgstr "Включено" + +#: src/delugegtk.py:340 +msgid "Unknown" +msgstr "Неизвестно" + +#: src/delugegtk.py:348 +msgid "Name" +msgstr "Имя" + +#: src/delugegtk.py:356 +msgid "Ratio" +msgstr "Рейтинг" + +#: src/delugegtk.py:465 +msgid "IP Address" +msgstr "IP-адрес" + +#: src/delugegtk.py:466 +msgid "Client" +msgstr "Клиент" + +#: src/delugegtk.py:467 +msgid "Percent Complete" +msgstr "Процент готовности" + +#: src/delugegtk.py:468 +msgid "Download Rate" +msgstr "Скорость скачивания" + +#: src/delugegtk.py:469 +msgid "Upload Rate" +msgstr "Скорость раздачи" + +#: src/delugegtk.py:478 +msgid "Filename" +msgstr "Файл" + +#: src/delugegtk.py:480 +msgid "Offset" +msgstr "Смещение" + +#: src/delugegtk.py:668 +msgid "" +"For some reason, the previous state could not be loaded, so a blank state " +"has been loaded for you." +msgstr "" +"По какой-то причине, предыдущее состояние загрузить не удалось, поэтому было " +"загружено состояние по умолчанию." + +#: src/delugegtk.py:670 +#, fuzzy +msgid "Would you like to attempt to reload the previous session's downloads?" +msgstr "Вы хотите попытаться перезагрузить закачки предыдущей сессии?" + +#: src/delugegtk.py:679 +msgid "Choose the download directory for" +msgstr "Выберете папку для загрузок" + +#: src/delugegtk.py:692 src/delugegtk.py:704 +msgid "Connections" +msgstr "Соединения" + +#: src/delugegtk.py:703 +msgid "Deluge Bittorrent Client" +msgstr "Клиент Bittorrent Deluge" + +#: src/delugegtk.py:891 +msgid "Add torrent from URL" +msgstr "Добавить торрент из URL" + +#: src/delugegtk.py:895 +msgid "Enter the URL of the .torrent to download" +msgstr "Введите URL файла .torrent для скачивания" + +#: src/delugegtk.py:959 +msgid "Warning - all downloaded files for this torrent will be deleted!" +msgstr "Внимание - все скачанные файлы для этого торрента будут удалены!" + +#: src/dgtk.py:71 +msgid "Choose a .torrent file" +msgstr "Укажите файл .torrent" + +#: src/dgtk.py:76 +msgid "Torrent files" +msgstr "Файлы .torrent" + +#: src/dgtk.py:80 +msgid "All files" +msgstr "Все файлы" + +#: src/dgtk.py:97 +msgid "Choose a download directory" +msgstr "Укажите папку для сохранения" \ No newline at end of file diff --git a/encryption/po/sk.po b/encryption/po/sk.po new file mode 100644 index 000000000..b51f0c1a6 --- /dev/null +++ b/encryption/po/sk.po @@ -0,0 +1,478 @@ +# Slovak translation for deluge +# Copyright (c) 2007 Rosetta Contributors and Canonical Ltd 2007 +# This file is distributed under the same license as the deluge package. +# Martin , 2007. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: deluge\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2007-03-06 16:19-0500\n" +"PO-Revision-Date: 2007-03-13 15:26+0000\n" +"Last-Translator: Martin \n" +"Language-Team: Slovak \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Rosetta-Export-Date: 2007-03-18 15:58+0000\n" + +#: glade/delugegtk.glade:93 +msgid "Estimated Time Remaining:" +msgstr "Predpokladaný zostávajúci čas:" + +#: glade/delugegtk.glade:131 +msgid "Peers:" +msgstr "Peeri:" + +#: glade/delugegtk.glade:145 +msgid "Seeders:" +msgstr "Seederi:" + +#: glade/delugegtk.glade:157 +msgid "Total Size:" +msgstr "Celková veľkosť:" + +#: glade/delugegtk.glade:169 +msgid "Total Downloaded:" +msgstr "Celkom stiahnuté:" + +#: glade/delugegtk.glade:181 +msgid "Downloaded this session:" +msgstr "Stiahnuté počas tohoto sedenia:" + +#: glade/delugegtk.glade:229 +msgid "Tracker:" +msgstr "Tracker:" + +#: glade/delugegtk.glade:241 +msgid "Tracker Response:" +msgstr "Odpoveď trackeru:" + +#: glade/delugegtk.glade:253 +msgid "Tracker Status:" +msgstr "Stav trackeru:" + +#: glade/delugegtk.glade:277 +msgid "Next Announce:" +msgstr "Najbližšie oznámenie:" + +#: glade/delugegtk.glade:303 +msgid "Pieces:" +msgstr "Diely:" + +#: glade/delugegtk.glade:317 +msgid "Total Uploaded:" +msgstr "Celkom odoslané:" + +#: glade/delugegtk.glade:355 +msgid "Share Ratio:" +msgstr "Pomer zdieľania" + +#: glade/delugegtk.glade:369 +msgid "Uploaded This Session:" +msgstr "Odoslané počas tohoto sedenia:" + +#: glade/delugegtk.glade:431 +msgid "Use compact storage allocation:" +msgstr "Používať kompaktú alokáciu pamäte:" + +#: glade/delugegtk.glade:455 +msgid "Download Rate:" +msgstr "Miera sťahovania:" + +#: glade/delugegtk.glade:479 +msgid "Upload Rate:" +msgstr "Miera odosielania:" + +#: glade/delugegtk.glade:522 glade/delugegtk.glade:746 +msgid "Details" +msgstr "Podrobnosti" + +#: glade/delugegtk.glade:549 glade/delugegtk.glade:790 +#: glade/dgtkpopups.glade:37 src/delugegtk.py:352 +msgid "Peers" +msgstr "Peerov" + +#: glade/delugegtk.glade:578 +msgid "Files" +msgstr "Súbory" + +#: glade/delugegtk.glade:613 +msgid "_File" +msgstr "_Súbor" + +#: glade/delugegtk.glade:620 glade/delugegtk.glade:901 src/delugegtk.py:174 +msgid "Add Torrent" +msgstr "Pridať torrent" + +#: glade/delugegtk.glade:636 +msgid "Add Torrent from URL" +msgstr "Pridať torrent z URL" + +#: glade/delugegtk.glade:644 glade/delugegtk.glade:914 +#: glade/dgtkpopups.glade:81 +msgid "Remove Torrent" +msgstr "Odstrániť torrent" + +#: glade/delugegtk.glade:658 +msgid "Clear Completed" +msgstr "Vyčistiť dokončené" + +#: glade/delugegtk.glade:692 +msgid "_Edit" +msgstr "_Úpravy" + +#: glade/delugegtk.glade:709 glade/dgtkpopups.glade:221 +#: glade/dgtkpref.glade:697 src/delugegtk.py:177 +msgid "Plugins" +msgstr "Moduly" + +#: glade/delugegtk.glade:729 +msgid "_View" +msgstr "_Zobraziť" + +#: glade/delugegtk.glade:737 +msgid "Toolbar" +msgstr "Panel nástrojov" + +#: glade/delugegtk.glade:755 +msgid "Columns" +msgstr "Stĺpce" + +#: glade/delugegtk.glade:763 glade/dgtkpopups.glade:10 src/delugegtk.py:349 +#: src/delugegtk.py:479 +msgid "Size" +msgstr "Veľkosť" + +#: glade/delugegtk.glade:772 glade/dgtkpopups.glade:19 src/delugegtk.py:350 +msgid "Status" +msgstr "Stav" + +#: glade/delugegtk.glade:781 glade/dgtkpopups.glade:28 src/delugegtk.py:351 +msgid "Seeders" +msgstr "Seederov" + +#: glade/delugegtk.glade:799 src/delugegtk.py:353 src/delugegtk.py:477 +#: src/delugegtk.py:692 src/delugegtk.py:704 +msgid "Download" +msgstr "Sťahovanie" + +#: glade/delugegtk.glade:808 src/delugegtk.py:354 src/delugegtk.py:693 +#: src/delugegtk.py:705 +msgid "Upload" +msgstr "Odosielanie" + +#: glade/delugegtk.glade:817 glade/dgtkpopups.glade:64 src/delugegtk.py:355 +msgid "Time Remaining" +msgstr "Zostáva" + +#: glade/delugegtk.glade:826 glade/dgtkpopups.glade:73 +msgid "Share Ratio" +msgstr "Pomer zdieľania" + +#: glade/delugegtk.glade:843 +msgid "_Help" +msgstr "_Pomocník" + +#: glade/delugegtk.glade:927 +msgid "Clear Finished Torrents" +msgstr "Vyčistiť dokončené torrenty" + +#: glade/delugegtk.glade:949 +msgid "Start / Pause" +msgstr "Spustiť alebo prerušiť" + +#: glade/delugegtk.glade:962 +msgid "Queue Torrent Up" +msgstr "Vpred vo fronte" + +#: glade/delugegtk.glade:975 +msgid "Queue Torrent Down" +msgstr "Dozadu vo fronte" + +#: glade/dgtkpopups.glade:46 +msgid "Download Speed" +msgstr "Rýchlosť sťahovania" + +#: glade/dgtkpopups.glade:55 +msgid "Upload Speed" +msgstr "Rýchlosť odosielania" + +#: glade/dgtkpopups.glade:97 +msgid "" +"Are you sure you want to remove the selected torrent(s) from Deluge?" +msgstr "Naozaj chcete odstrániť vybrané torrenty z programu Deluge?" + +#: glade/dgtkpopups.glade:107 +msgid "Delete downloaded files" +msgstr "Zmazať stiahnuté súbory" + +#: glade/dgtkpopups.glade:167 +msgid "Show/Hide" +msgstr "Zobraziť/Skryť" + +#: glade/dgtkpopups.glade:175 +msgid "Add a Torrent..." +msgstr "Pridať torrent..." + +#: glade/dgtkpopups.glade:191 src/delugegtk.py:175 +msgid "Clear Finished" +msgstr "Vyčistiť dokončené" + +#: glade/dgtkpopups.glade:212 glade/dgtkpref.glade:672 +msgid "gtk-preferences" +msgstr "gtk-preferences" + +#: glade/dgtkpopups.glade:242 +msgid "gtk-quit" +msgstr "gtk-quit" + +#: glade/dgtkpref.glade:8 +msgid "Preferences Dialog" +msgstr "Okno nastavení" + +#: glade/dgtkpref.glade:53 +msgid "Enable system tray icon" +msgstr "Povoliť ikonu v systémovom paneli" + +#: glade/dgtkpref.glade:65 +msgid "Minimize to tray on close" +msgstr "Minimalizovať do panelu pri zatvorení" + +#: glade/dgtkpref.glade:80 +msgid "Options" +msgstr "Voľby" + +#: glade/dgtkpref.glade:115 +msgid "Save all downloads to:" +msgstr "Ukladať všetky sťahovania do:" + +#: glade/dgtkpref.glade:127 +msgid "Ask me where to save each download" +msgstr "Spýtať sa, kam uložiť jednotlivé sťahovania" + +#: glade/dgtkpref.glade:139 +msgid "Save Location" +msgstr "Uložiť umiestnenie" + +#: glade/dgtkpref.glade:180 +msgid "" +"Stop seeding torrents when\n" +"their share ratio reaches:" +msgstr "" +"Prestať seedovať torrenty, keď\n" +"ich pomer zdieľania dosiahne hodnotu:" + +#: glade/dgtkpref.glade:190 +msgid "Seeding" +msgstr "Seedovanie" + +#: glade/dgtkpref.glade:209 +msgid "Use compact storage allocation" +msgstr "Použiť kompaktú alokáciu pamäte" + +#: glade/dgtkpref.glade:216 +msgid "Storage" +msgstr "Pamäť" + +#: glade/dgtkpref.glade:246 +msgid "General" +msgstr "Všeobecné" + +#: glade/dgtkpref.glade:280 +msgid "Try from:" +msgstr "Skúšať od:" + +#: glade/dgtkpref.glade:286 +msgid "to:" +msgstr "do:" + +#: glade/dgtkpref.glade:320 +msgid "Active port:" +msgstr "Aktívny port:" + +#: glade/dgtkpref.glade:332 +msgid "0000" +msgstr "0000" + +#: glade/dgtkpref.glade:343 +msgid "Test Port" +msgstr "Testovať port" + +#: glade/dgtkpref.glade:357 +msgid "TCP Port" +msgstr "TCP port" + +#: glade/dgtkpref.glade:380 +msgid "(-1 is unlimited)" +msgstr "(-1 pre neobmedzené)" + +#: glade/dgtkpref.glade:390 glade/dgtkpref.glade:511 +msgid "KB/s" +msgstr "KB/s" + +#: glade/dgtkpref.glade:436 +msgid "Maximum Download Rate:" +msgstr "Maximálna miera sťahovania:" + +#: glade/dgtkpref.glade:447 +msgid "Upload Slots" +msgstr "Miesta pre odosielanie" + +#: glade/dgtkpref.glade:458 +msgid "Maximum Connections" +msgstr "Maximum pripojení" + +#: glade/dgtkpref.glade:485 +msgid "Maximum Upload Rate:" +msgstr "Maximálna miera odosielania:" + +#: glade/dgtkpref.glade:548 +msgid "Bandwidth " +msgstr "Pripojenie " + +#: glade/dgtkpref.glade:576 +msgid "Network" +msgstr "Sieť" + +#: glade/dgtkpref.glade:598 +msgid "gtk-cancel" +msgstr "gtk-cancel" + +#: glade/dgtkpref.glade:605 +msgid "gtk-ok" +msgstr "gtk-ok" + +#: glade/dgtkpref.glade:625 +msgid "Plugin Manager" +msgstr "Správca modulov" + +#: glade/dgtkpref.glade:718 +msgid "gtk-close" +msgstr "gtk-close" + +#: src/dcommon.py:41 src/delugegtk.py:330 +msgid "Infinity" +msgstr "Veľmi dlho" + +#: src/dcommon.py:105 +msgid "Error: no webbrowser found" +msgstr "Chyba: nebol nájdený webový prehliadač" + +#: src/deluge.py:332 +msgid "File was not found" +msgstr "Súbor nebol nájdený" + +#: src/delugegtk.py:173 +msgid "Show / Hide Window" +msgstr "Zobraziť / Skryť okno" + +#: src/delugegtk.py:250 +msgid "Plugin" +msgstr "Modul" + +#: src/delugegtk.py:252 +msgid "Enabled" +msgstr "Povolený" + +#: src/delugegtk.py:340 +msgid "Unknown" +msgstr "Neznámy" + +#: src/delugegtk.py:348 +msgid "Name" +msgstr "Názov" + +#: src/delugegtk.py:356 +msgid "Ratio" +msgstr "Pomer" + +#: src/delugegtk.py:465 +msgid "IP Address" +msgstr "IP adresa" + +#: src/delugegtk.py:466 +msgid "Client" +msgstr "Klient" + +#: src/delugegtk.py:467 +msgid "Percent Complete" +msgstr "Množstvo dát" + +#: src/delugegtk.py:468 +msgid "Download Rate" +msgstr "Miera sťahovania" + +#: src/delugegtk.py:469 +msgid "Upload Rate" +msgstr "Miera odosielania" + +#: src/delugegtk.py:478 +msgid "Filename" +msgstr "Názov súboru" + +#: src/delugegtk.py:480 +msgid "Offset" +msgstr "Odstup" + +#: src/delugegtk.py:668 +msgid "" +"For some reason, the previous state could not be loaded, so a blank state " +"has been loaded for you." +msgstr "" +"Predchádzajúci stav nebolo možné načítať, a preto bol načítaný prázdny stav." + +#: src/delugegtk.py:670 +msgid "Would you like to attempt to reload the previous session's downloads?" +msgstr "Chcete vykonať obnovu sťahovaní z predchádzajúceho sedenia?" + +#: src/delugegtk.py:679 +msgid "Choose the download directory for" +msgstr "Vyberte adresár na sťahovanie" + +#: src/delugegtk.py:692 src/delugegtk.py:704 +msgid "Connections" +msgstr "Pripojenia" + +#: src/delugegtk.py:703 +msgid "Deluge Bittorrent Client" +msgstr "Deluge - Klient siete bittorrent" + +#: src/delugegtk.py:891 +msgid "Add torrent from URL" +msgstr "Pridať torrent z URL" + +#: src/delugegtk.py:895 +msgid "Enter the URL of the .torrent to download" +msgstr "Zadajte URL adresu súboru .torrent, ktorý chcete stiahnuť" + +#: src/delugegtk.py:959 +msgid "Warning - all downloaded files for this torrent will be deleted!" +msgstr "" +"Upozornenie - všetky stiahnuté súbory z tohoto torrentu budú odstránené!" + +#: src/dgtk.py:71 +msgid "Choose a .torrent file" +msgstr "Vyberte súbor s príponou .torrent" + +#: src/dgtk.py:76 +msgid "Torrent files" +msgstr "Torrent súbory" + +#: src/dgtk.py:80 +msgid "All files" +msgstr "Všetky súbory" + +#: src/dgtk.py:97 +msgid "Choose a download directory" +msgstr "Vyberte priečinok, kam sťahovať dáta" + +#~ msgid "_Manage Plugins" +#~ msgstr "_Moduly" + +#~ msgid "Maximum number of Downloads:" +#~ msgstr "Maximálny počet sťahovaní:" + +#~ msgid "Progress" +#~ msgstr "Priebeh" \ No newline at end of file diff --git a/encryption/po/sl.po b/encryption/po/sl.po new file mode 100644 index 000000000..aa8548673 --- /dev/null +++ b/encryption/po/sl.po @@ -0,0 +1,465 @@ +# Slovenian translation for deluge +# Copyright (c) 2006 Rosetta Contributors and Canonical Ltd 2006 +# This file is distributed under the same license as the deluge package. +# FIRST AUTHOR , 2006. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: deluge\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2007-03-06 16:19-0500\n" +"PO-Revision-Date: 2007-03-02 19:19+0000\n" +"Last-Translator: DvoglavaZver \n" +"Language-Team: Slovenian \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Rosetta-Export-Date: 2007-03-18 15:58+0000\n" + +#: glade/delugegtk.glade:93 +msgid "Estimated Time Remaining:" +msgstr "Predviden Čas do Konca:" + +#: glade/delugegtk.glade:131 +msgid "Peers:" +msgstr "Soležniki:" + +#: glade/delugegtk.glade:145 +msgid "Seeders:" +msgstr "Semena:" + +#: glade/delugegtk.glade:157 +msgid "Total Size:" +msgstr "Skupna Velikost:" + +#: glade/delugegtk.glade:169 +msgid "Total Downloaded:" +msgstr "Skupaj prejeto:" + +#: glade/delugegtk.glade:181 +msgid "Downloaded this session:" +msgstr "Prejeto to sejo:" + +#: glade/delugegtk.glade:229 +msgid "Tracker:" +msgstr "Sledilnik:" + +#: glade/delugegtk.glade:241 +msgid "Tracker Response:" +msgstr "Odgovor Sledilnika:" + +#: glade/delugegtk.glade:253 +msgid "Tracker Status:" +msgstr "Stanje Sledilnika:" + +#: glade/delugegtk.glade:277 +msgid "Next Announce:" +msgstr "Naslednji Razglas:" + +#: glade/delugegtk.glade:303 +msgid "Pieces:" +msgstr "Kosi:" + +#: glade/delugegtk.glade:317 +msgid "Total Uploaded:" +msgstr "Skupno Oddano:" + +#: glade/delugegtk.glade:355 +msgid "Share Ratio:" +msgstr "Delilno Razmerje:" + +#: glade/delugegtk.glade:369 +msgid "Uploaded This Session:" +msgstr "Oddano To Sejo:" + +#: glade/delugegtk.glade:431 +msgid "Use compact storage allocation:" +msgstr "Uporabi kompaktno dodeljevanje pomnilnika:" + +#: glade/delugegtk.glade:455 +msgid "Download Rate:" +msgstr "Hitrost Prejemanja:" + +#: glade/delugegtk.glade:479 +msgid "Upload Rate:" +msgstr "Hitrost Oddajanja:" + +#: glade/delugegtk.glade:522 glade/delugegtk.glade:746 +msgid "Details" +msgstr "Podrobnosti" + +#: glade/delugegtk.glade:549 glade/delugegtk.glade:790 +#: glade/dgtkpopups.glade:37 src/delugegtk.py:352 +msgid "Peers" +msgstr "Vrstniki" + +#: glade/delugegtk.glade:578 +msgid "Files" +msgstr "Datoteke" + +#: glade/delugegtk.glade:613 +msgid "_File" +msgstr "_Datoteka" + +#: glade/delugegtk.glade:620 glade/delugegtk.glade:901 src/delugegtk.py:174 +msgid "Add Torrent" +msgstr "Dodaj torrent" + +#: glade/delugegtk.glade:636 +msgid "Add Torrent from URL" +msgstr "Dodaj torrent iz URL naslova" + +#: glade/delugegtk.glade:644 glade/delugegtk.glade:914 +#: glade/dgtkpopups.glade:81 +msgid "Remove Torrent" +msgstr "Odstrani torrent" + +#: glade/delugegtk.glade:658 +msgid "Clear Completed" +msgstr "Počisti končane prenose" + +#: glade/delugegtk.glade:692 +msgid "_Edit" +msgstr "_Uredi" + +#: glade/delugegtk.glade:709 glade/dgtkpopups.glade:221 +#: glade/dgtkpref.glade:697 src/delugegtk.py:177 +msgid "Plugins" +msgstr "Vtičniki" + +#: glade/delugegtk.glade:729 +msgid "_View" +msgstr "_Pogled" + +#: glade/delugegtk.glade:737 +msgid "Toolbar" +msgstr "Orodna vrstica" + +#: glade/delugegtk.glade:755 +msgid "Columns" +msgstr "Stolpci" + +#: glade/delugegtk.glade:763 glade/dgtkpopups.glade:10 src/delugegtk.py:349 +#: src/delugegtk.py:479 +msgid "Size" +msgstr "Velikost" + +#: glade/delugegtk.glade:772 glade/dgtkpopups.glade:19 src/delugegtk.py:350 +msgid "Status" +msgstr "Stanje" + +#: glade/delugegtk.glade:781 glade/dgtkpopups.glade:28 src/delugegtk.py:351 +msgid "Seeders" +msgstr "Sejalci" + +#: glade/delugegtk.glade:799 src/delugegtk.py:353 src/delugegtk.py:477 +#: src/delugegtk.py:692 src/delugegtk.py:704 +msgid "Download" +msgstr "Prejemanje" + +#: glade/delugegtk.glade:808 src/delugegtk.py:354 src/delugegtk.py:693 +#: src/delugegtk.py:705 +msgid "Upload" +msgstr "Oddajanje" + +#: glade/delugegtk.glade:817 glade/dgtkpopups.glade:64 src/delugegtk.py:355 +msgid "Time Remaining" +msgstr "Preostali čas" + +#: glade/delugegtk.glade:826 glade/dgtkpopups.glade:73 +msgid "Share Ratio" +msgstr "Delilno razmerje" + +#: glade/delugegtk.glade:843 +msgid "_Help" +msgstr "_Pomoč" + +#: glade/delugegtk.glade:927 +msgid "Clear Finished Torrents" +msgstr "Odstrani končane torrente" + +#: glade/delugegtk.glade:949 +msgid "Start / Pause" +msgstr "Zaženi / Premor" + +#: glade/delugegtk.glade:962 +msgid "Queue Torrent Up" +msgstr "Postavi v vrsti gor" + +#: glade/delugegtk.glade:975 +msgid "Queue Torrent Down" +msgstr "Postavi v vrsti dol" + +#: glade/dgtkpopups.glade:46 +msgid "Download Speed" +msgstr "Hitrost Prejemanja" + +#: glade/dgtkpopups.glade:55 +msgid "Upload Speed" +msgstr "Hitrost Oddajanja" + +#: glade/dgtkpopups.glade:97 +msgid "" +"Are you sure you want to remove the selected torrent(s) from Deluge?" +msgstr "Res želiš odstraniti izbran(e) torrent(e) iz Deluge?" + +#: glade/dgtkpopups.glade:107 +msgid "Delete downloaded files" +msgstr "Izbriši prenesene datoteke" + +#: glade/dgtkpopups.glade:167 +msgid "Show/Hide" +msgstr "Pokaži/Skrij" + +#: glade/dgtkpopups.glade:175 +msgid "Add a Torrent..." +msgstr "Dodaj Torrent..." + +#: glade/dgtkpopups.glade:191 src/delugegtk.py:175 +msgid "Clear Finished" +msgstr "Odstrani Končane" + +#: glade/dgtkpopups.glade:212 glade/dgtkpref.glade:672 +msgid "gtk-preferences" +msgstr "gtk-nastavitve" + +#: glade/dgtkpopups.glade:242 +msgid "gtk-quit" +msgstr "gtk-izhod" + +#: glade/dgtkpref.glade:8 +msgid "Preferences Dialog" +msgstr "Nastavitve" + +#: glade/dgtkpref.glade:53 +msgid "Enable system tray icon" +msgstr "Omogoči ikono v sistemski vrstici" + +#: glade/dgtkpref.glade:65 +msgid "Minimize to tray on close" +msgstr "Pomanjšaj v pladenj ob izhodu" + +#: glade/dgtkpref.glade:80 +msgid "Options" +msgstr "Možnosti" + +#: glade/dgtkpref.glade:115 +msgid "Save all downloads to:" +msgstr "Shrani vse prenose v:" + +#: glade/dgtkpref.glade:127 +msgid "Ask me where to save each download" +msgstr "Vprašaj me kam na shranim vsak prenos" + +#: glade/dgtkpref.glade:139 +msgid "Save Location" +msgstr "Shrani na Mesto" + +#: glade/dgtkpref.glade:180 +msgid "" +"Stop seeding torrents when\n" +"their share ratio reaches:" +msgstr "Prenehaj s sejanjem torrentov ko delilno razmerje doseže:" + +#: glade/dgtkpref.glade:190 +msgid "Seeding" +msgstr "Sejanje" + +#: glade/dgtkpref.glade:209 +msgid "Use compact storage allocation" +msgstr "Uporabi kompaktno dodeljevanje pomnilnika" + +#: glade/dgtkpref.glade:216 +msgid "Storage" +msgstr "Shramba" + +#: glade/dgtkpref.glade:246 +msgid "General" +msgstr "Splošno" + +#: glade/dgtkpref.glade:280 +msgid "Try from:" +msgstr "Poskusi iz:" + +#: glade/dgtkpref.glade:286 +msgid "to:" +msgstr "v:" + +#: glade/dgtkpref.glade:320 +msgid "Active port:" +msgstr "Aktivna Vrata:" + +#: glade/dgtkpref.glade:332 +msgid "0000" +msgstr "0000" + +#: glade/dgtkpref.glade:343 +msgid "Test Port" +msgstr "Testiraj Vrata" + +#: glade/dgtkpref.glade:357 +msgid "TCP Port" +msgstr "Vrata TCP" + +#: glade/dgtkpref.glade:380 +msgid "(-1 is unlimited)" +msgstr "(-1 je neomejeno)" + +#: glade/dgtkpref.glade:390 glade/dgtkpref.glade:511 +msgid "KB/s" +msgstr "KB/s" + +#: glade/dgtkpref.glade:436 +msgid "Maximum Download Rate:" +msgstr "Maks. hitrost prejemanja:" + +#: glade/dgtkpref.glade:447 +msgid "Upload Slots" +msgstr "Oddajna Mesta" + +#: glade/dgtkpref.glade:458 +msgid "Maximum Connections" +msgstr "" + +#: glade/dgtkpref.glade:485 +msgid "Maximum Upload Rate:" +msgstr "Maks. hitrost oddajanja:" + +#: glade/dgtkpref.glade:548 +msgid "Bandwidth " +msgstr "Pasovna Širina " + +#: glade/dgtkpref.glade:576 +msgid "Network" +msgstr "Omrežje" + +#: glade/dgtkpref.glade:598 +msgid "gtk-cancel" +msgstr "gtk-prekliči" + +#: glade/dgtkpref.glade:605 +msgid "gtk-ok" +msgstr "gtk-v redu" + +#: glade/dgtkpref.glade:625 +msgid "Plugin Manager" +msgstr "Upravljalec Vtičnikov" + +#: glade/dgtkpref.glade:718 +msgid "gtk-close" +msgstr "gtk-zapri" + +#: src/dcommon.py:41 src/delugegtk.py:330 +msgid "Infinity" +msgstr "Neskončno" + +#: src/dcommon.py:105 +msgid "Error: no webbrowser found" +msgstr "Napaka: spletni brskalnik ni najden" + +#: src/deluge.py:332 +msgid "File was not found" +msgstr "" + +#: src/delugegtk.py:173 +msgid "Show / Hide Window" +msgstr "Pokaži / Skrij Okno" + +#: src/delugegtk.py:250 +msgid "Plugin" +msgstr "Vtičnik" + +#: src/delugegtk.py:252 +msgid "Enabled" +msgstr "Omogočeno" + +#: src/delugegtk.py:340 +msgid "Unknown" +msgstr "Neznan" + +#: src/delugegtk.py:348 +msgid "Name" +msgstr "Ime" + +#: src/delugegtk.py:356 +msgid "Ratio" +msgstr "Razmerje" + +#: src/delugegtk.py:465 +msgid "IP Address" +msgstr "IP Naslov" + +#: src/delugegtk.py:466 +msgid "Client" +msgstr "Odjemalec" + +#: src/delugegtk.py:467 +msgid "Percent Complete" +msgstr "Odstotek zaključenosti" + +#: src/delugegtk.py:468 +msgid "Download Rate" +msgstr "Hitrost Prejemanja" + +#: src/delugegtk.py:469 +msgid "Upload Rate" +msgstr "Hitrost Oddajanja" + +#: src/delugegtk.py:478 +msgid "Filename" +msgstr "Ime datoteke" + +#: src/delugegtk.py:480 +msgid "Offset" +msgstr "Odmik" + +#: src/delugegtk.py:668 +msgid "" +"For some reason, the previous state could not be loaded, so a blank state " +"has been loaded for you." +msgstr "" + +#: src/delugegtk.py:670 +msgid "Would you like to attempt to reload the previous session's downloads?" +msgstr "" + +#: src/delugegtk.py:679 +msgid "Choose the download directory for" +msgstr "" + +#: src/delugegtk.py:692 src/delugegtk.py:704 +msgid "Connections" +msgstr "Povezave" + +#: src/delugegtk.py:703 +msgid "Deluge Bittorrent Client" +msgstr "Deluge Bittorrent Odjemalec" + +#: src/delugegtk.py:891 +msgid "Add torrent from URL" +msgstr "Dodaj torrent iz URL naslova" + +#: src/delugegtk.py:895 +msgid "Enter the URL of the .torrent to download" +msgstr "Vnesi URL naslov .torrent datoteke, ki jo želite prenesti" + +#: src/delugegtk.py:959 +msgid "Warning - all downloaded files for this torrent will be deleted!" +msgstr "Opozorilo - vse prejete datoteke za ta torrent bodo izbrisane!" + +#: src/dgtk.py:71 +msgid "Choose a .torrent file" +msgstr "Izberi .torrent datoteko" + +#: src/dgtk.py:76 +msgid "Torrent files" +msgstr "Torrent datoteke" + +#: src/dgtk.py:80 +msgid "All files" +msgstr "Vse datoteke" + +#: src/dgtk.py:97 +msgid "Choose a download directory" +msgstr "Izberi mapo za prenos" \ No newline at end of file diff --git a/encryption/po/sr.po b/encryption/po/sr.po new file mode 100644 index 000000000..57d46b85c --- /dev/null +++ b/encryption/po/sr.po @@ -0,0 +1,466 @@ +# Serbian translation for deluge +# Copyright (c) 2007 Rosetta Contributors and Canonical Ltd 2007 +# This file is distributed under the same license as the deluge package. +# FIRST AUTHOR , 2007. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: deluge\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2007-03-06 16:19-0500\n" +"PO-Revision-Date: 2007-02-12 16:29+0000\n" +"Last-Translator: kliklik \n" +"Language-Team: Serbian \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Rosetta-Export-Date: 2007-03-18 15:58+0000\n" + +#: glade/delugegtk.glade:93 +msgid "Estimated Time Remaining:" +msgstr "" + +#: glade/delugegtk.glade:131 +msgid "Peers:" +msgstr "" + +#: glade/delugegtk.glade:145 +msgid "Seeders:" +msgstr "" + +#: glade/delugegtk.glade:157 +msgid "Total Size:" +msgstr "" + +#: glade/delugegtk.glade:169 +msgid "Total Downloaded:" +msgstr "" + +#: glade/delugegtk.glade:181 +msgid "Downloaded this session:" +msgstr "" + +#: glade/delugegtk.glade:229 +msgid "Tracker:" +msgstr "" + +#: glade/delugegtk.glade:241 +msgid "Tracker Response:" +msgstr "" + +#: glade/delugegtk.glade:253 +msgid "Tracker Status:" +msgstr "" + +#: glade/delugegtk.glade:277 +msgid "Next Announce:" +msgstr "" + +#: glade/delugegtk.glade:303 +msgid "Pieces:" +msgstr "" + +#: glade/delugegtk.glade:317 +msgid "Total Uploaded:" +msgstr "" + +#: glade/delugegtk.glade:355 +msgid "Share Ratio:" +msgstr "" + +#: glade/delugegtk.glade:369 +msgid "Uploaded This Session:" +msgstr "" + +#: glade/delugegtk.glade:431 +msgid "Use compact storage allocation:" +msgstr "" + +#: glade/delugegtk.glade:455 +msgid "Download Rate:" +msgstr "" + +#: glade/delugegtk.glade:479 +msgid "Upload Rate:" +msgstr "" + +#: glade/delugegtk.glade:522 glade/delugegtk.glade:746 +msgid "Details" +msgstr "" + +#: glade/delugegtk.glade:549 glade/delugegtk.glade:790 +#: glade/dgtkpopups.glade:37 src/delugegtk.py:352 +msgid "Peers" +msgstr "Vršnjaci" + +#: glade/delugegtk.glade:578 +msgid "Files" +msgstr "" + +#: glade/delugegtk.glade:613 +msgid "_File" +msgstr "" + +#: glade/delugegtk.glade:620 glade/delugegtk.glade:901 src/delugegtk.py:174 +msgid "Add Torrent" +msgstr "" + +#: glade/delugegtk.glade:636 +msgid "Add Torrent from URL" +msgstr "" + +#: glade/delugegtk.glade:644 glade/delugegtk.glade:914 +#: glade/dgtkpopups.glade:81 +msgid "Remove Torrent" +msgstr "" + +#: glade/delugegtk.glade:658 +msgid "Clear Completed" +msgstr "" + +#: glade/delugegtk.glade:692 +msgid "_Edit" +msgstr "" + +#: glade/delugegtk.glade:709 glade/dgtkpopups.glade:221 +#: glade/dgtkpref.glade:697 src/delugegtk.py:177 +msgid "Plugins" +msgstr "" + +#: glade/delugegtk.glade:729 +msgid "_View" +msgstr "" + +#: glade/delugegtk.glade:737 +msgid "Toolbar" +msgstr "" + +#: glade/delugegtk.glade:755 +msgid "Columns" +msgstr "" + +#: glade/delugegtk.glade:763 glade/dgtkpopups.glade:10 src/delugegtk.py:349 +#: src/delugegtk.py:479 +msgid "Size" +msgstr "Veličina" + +#: glade/delugegtk.glade:772 glade/dgtkpopups.glade:19 src/delugegtk.py:350 +msgid "Status" +msgstr "" + +#: glade/delugegtk.glade:781 glade/dgtkpopups.glade:28 src/delugegtk.py:351 +#, fuzzy +msgid "Seeders" +msgstr "Sejači" + +#: glade/delugegtk.glade:799 src/delugegtk.py:353 src/delugegtk.py:477 +#: src/delugegtk.py:692 src/delugegtk.py:704 +msgid "Download" +msgstr "" + +#: glade/delugegtk.glade:808 src/delugegtk.py:354 src/delugegtk.py:693 +#: src/delugegtk.py:705 +msgid "Upload" +msgstr "" + +#: glade/delugegtk.glade:817 glade/dgtkpopups.glade:64 src/delugegtk.py:355 +msgid "Time Remaining" +msgstr "" + +#: glade/delugegtk.glade:826 glade/dgtkpopups.glade:73 +msgid "Share Ratio" +msgstr "" + +#: glade/delugegtk.glade:843 +msgid "_Help" +msgstr "" + +#: glade/delugegtk.glade:927 +msgid "Clear Finished Torrents" +msgstr "" + +#: glade/delugegtk.glade:949 +msgid "Start / Pause" +msgstr "" + +#: glade/delugegtk.glade:962 +msgid "Queue Torrent Up" +msgstr "" + +#: glade/delugegtk.glade:975 +msgid "Queue Torrent Down" +msgstr "" + +#: glade/dgtkpopups.glade:46 +msgid "Download Speed" +msgstr "" + +#: glade/dgtkpopups.glade:55 +msgid "Upload Speed" +msgstr "" + +#: glade/dgtkpopups.glade:97 +msgid "" +"Are you sure you want to remove the selected torrent(s) from Deluge?" +msgstr "" + +#: glade/dgtkpopups.glade:107 +msgid "Delete downloaded files" +msgstr "" + +#: glade/dgtkpopups.glade:167 +msgid "Show/Hide" +msgstr "" + +#: glade/dgtkpopups.glade:175 +msgid "Add a Torrent..." +msgstr "" + +#: glade/dgtkpopups.glade:191 src/delugegtk.py:175 +msgid "Clear Finished" +msgstr "" + +#: glade/dgtkpopups.glade:212 glade/dgtkpref.glade:672 +msgid "gtk-preferences" +msgstr "" + +#: glade/dgtkpopups.glade:242 +msgid "gtk-quit" +msgstr "" + +#: glade/dgtkpref.glade:8 +msgid "Preferences Dialog" +msgstr "" + +#: glade/dgtkpref.glade:53 +msgid "Enable system tray icon" +msgstr "" + +#: glade/dgtkpref.glade:65 +msgid "Minimize to tray on close" +msgstr "" + +#: glade/dgtkpref.glade:80 +msgid "Options" +msgstr "" + +#: glade/dgtkpref.glade:115 +msgid "Save all downloads to:" +msgstr "" + +#: glade/dgtkpref.glade:127 +msgid "Ask me where to save each download" +msgstr "" + +#: glade/dgtkpref.glade:139 +msgid "Save Location" +msgstr "" + +#: glade/dgtkpref.glade:180 +msgid "" +"Stop seeding torrents when\n" +"their share ratio reaches:" +msgstr "" + +#: glade/dgtkpref.glade:190 +msgid "Seeding" +msgstr "" + +#: glade/dgtkpref.glade:209 +msgid "Use compact storage allocation" +msgstr "" + +#: glade/dgtkpref.glade:216 +msgid "Storage" +msgstr "" + +#: glade/dgtkpref.glade:246 +msgid "General" +msgstr "" + +#: glade/dgtkpref.glade:280 +msgid "Try from:" +msgstr "" + +#: glade/dgtkpref.glade:286 +msgid "to:" +msgstr "" + +#: glade/dgtkpref.glade:320 +msgid "Active port:" +msgstr "" + +#: glade/dgtkpref.glade:332 +msgid "0000" +msgstr "" + +#: glade/dgtkpref.glade:343 +msgid "Test Port" +msgstr "" + +#: glade/dgtkpref.glade:357 +msgid "TCP Port" +msgstr "" + +#: glade/dgtkpref.glade:380 +msgid "(-1 is unlimited)" +msgstr "" + +#: glade/dgtkpref.glade:390 glade/dgtkpref.glade:511 +msgid "KB/s" +msgstr "" + +#: glade/dgtkpref.glade:436 +msgid "Maximum Download Rate:" +msgstr "" + +#: glade/dgtkpref.glade:447 +msgid "Upload Slots" +msgstr "" + +#: glade/dgtkpref.glade:458 +msgid "Maximum Connections" +msgstr "" + +#: glade/dgtkpref.glade:485 +msgid "Maximum Upload Rate:" +msgstr "" + +#: glade/dgtkpref.glade:548 +msgid "Bandwidth " +msgstr "" + +#: glade/dgtkpref.glade:576 +msgid "Network" +msgstr "" + +#: glade/dgtkpref.glade:598 +msgid "gtk-cancel" +msgstr "" + +#: glade/dgtkpref.glade:605 +msgid "gtk-ok" +msgstr "" + +#: glade/dgtkpref.glade:625 +msgid "Plugin Manager" +msgstr "" + +#: glade/dgtkpref.glade:718 +msgid "gtk-close" +msgstr "" + +#: src/dcommon.py:41 src/delugegtk.py:330 +msgid "Infinity" +msgstr "" + +#: src/dcommon.py:105 +msgid "Error: no webbrowser found" +msgstr "" + +#: src/deluge.py:332 +msgid "File was not found" +msgstr "" + +#: src/delugegtk.py:173 +msgid "Show / Hide Window" +msgstr "" + +#: src/delugegtk.py:250 +msgid "Plugin" +msgstr "" + +#: src/delugegtk.py:252 +msgid "Enabled" +msgstr "" + +#: src/delugegtk.py:340 +msgid "Unknown" +msgstr "" + +#: src/delugegtk.py:348 +msgid "Name" +msgstr "Naziv" + +#: src/delugegtk.py:356 +msgid "Ratio" +msgstr "" + +#: src/delugegtk.py:465 +msgid "IP Address" +msgstr "" + +#: src/delugegtk.py:466 +msgid "Client" +msgstr "" + +#: src/delugegtk.py:467 +msgid "Percent Complete" +msgstr "" + +#: src/delugegtk.py:468 +msgid "Download Rate" +msgstr "" + +#: src/delugegtk.py:469 +msgid "Upload Rate" +msgstr "" + +#: src/delugegtk.py:478 +msgid "Filename" +msgstr "" + +#: src/delugegtk.py:480 +msgid "Offset" +msgstr "" + +#: src/delugegtk.py:668 +msgid "" +"For some reason, the previous state could not be loaded, so a blank state " +"has been loaded for you." +msgstr "" + +#: src/delugegtk.py:670 +msgid "Would you like to attempt to reload the previous session's downloads?" +msgstr "" + +#: src/delugegtk.py:679 +msgid "Choose the download directory for" +msgstr "" + +#: src/delugegtk.py:692 src/delugegtk.py:704 +msgid "Connections" +msgstr "" + +#: src/delugegtk.py:703 +msgid "Deluge Bittorrent Client" +msgstr "" + +#: src/delugegtk.py:891 +msgid "Add torrent from URL" +msgstr "" + +#: src/delugegtk.py:895 +msgid "Enter the URL of the .torrent to download" +msgstr "" + +#: src/delugegtk.py:959 +msgid "Warning - all downloaded files for this torrent will be deleted!" +msgstr "" + +#: src/dgtk.py:71 +msgid "Choose a .torrent file" +msgstr "" + +#: src/dgtk.py:76 +msgid "Torrent files" +msgstr "" + +#: src/dgtk.py:80 +msgid "All files" +msgstr "" + +#: src/dgtk.py:97 +msgid "Choose a download directory" +msgstr "" \ No newline at end of file diff --git a/encryption/po/sv.po b/encryption/po/sv.po new file mode 100644 index 000000000..edaed64b3 --- /dev/null +++ b/encryption/po/sv.po @@ -0,0 +1,754 @@ +# translation of PACKAGE. +# Copyright (C) 2006 THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# <>, 2006. +# , fuzzy +# <>, 2006. +# +# +msgid "" +msgstr "" +"Project-Id-Version: deluge\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2007-03-06 16:19-0500\n" +"PO-Revision-Date: 2007-03-15 21:51+0000\n" +"Last-Translator: Jonas Granqvist \n" +"Language-Team: Swedish\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Rosetta-Export-Date: 2007-03-18 15:58+0000\n" + +#: glade/delugegtk.glade:93 +msgid "Estimated Time Remaining:" +msgstr "Beräknad återstående tid" + +#: glade/delugegtk.glade:131 +msgid "Peers:" +msgstr "Jämlikar" + +#: glade/delugegtk.glade:145 +msgid "Seeders:" +msgstr "Seedare" + +#: glade/delugegtk.glade:157 +msgid "Total Size:" +msgstr "Storlek totalt:" + +#: glade/delugegtk.glade:169 +msgid "Total Downloaded:" +msgstr "Nedladdat totalt:" + +#: glade/delugegtk.glade:181 +msgid "Downloaded this session:" +msgstr "Nedladdat denna gång:" + +#: glade/delugegtk.glade:229 +msgid "Tracker:" +msgstr "Sökare:" + +#: glade/delugegtk.glade:241 +msgid "Tracker Response:" +msgstr "Sökarrespons:" + +#: glade/delugegtk.glade:253 +msgid "Tracker Status:" +msgstr "Sökarstatus:" + +#: glade/delugegtk.glade:277 +msgid "Next Announce:" +msgstr "Nästa meddelande:" + +#: glade/delugegtk.glade:303 +msgid "Pieces:" +msgstr "Delar:" + +#: glade/delugegtk.glade:317 +msgid "Total Uploaded:" +msgstr "Uppladdat totalt:" + +#: glade/delugegtk.glade:355 +msgid "Share Ratio:" +msgstr "Delningsgrad:" + +#: glade/delugegtk.glade:369 +msgid "Uploaded This Session:" +msgstr "Uppladdat denna session:" + +#: glade/delugegtk.glade:431 +msgid "Use compact storage allocation:" +msgstr "Använd kompakt lagringsallokering:" + +#: glade/delugegtk.glade:455 +msgid "Download Rate:" +msgstr "Nedladdningshastighet:" + +#: glade/delugegtk.glade:479 +msgid "Upload Rate:" +msgstr "Uppladdningshastighet:" + +#: glade/delugegtk.glade:522 glade/delugegtk.glade:746 +msgid "Details" +msgstr "Detaljer" + +#: glade/delugegtk.glade:549 glade/delugegtk.glade:790 +#: glade/dgtkpopups.glade:37 src/delugegtk.py:352 +msgid "Peers" +msgstr "Jämlikar" + +#: glade/delugegtk.glade:578 +msgid "Files" +msgstr "Filer" + +#: glade/delugegtk.glade:613 +msgid "_File" +msgstr "_Arkiv" + +#: glade/delugegtk.glade:620 glade/delugegtk.glade:901 src/delugegtk.py:174 +msgid "Add Torrent" +msgstr "Lägg till Torrent" + +#: glade/delugegtk.glade:636 +msgid "Add Torrent from URL" +msgstr "Lägg till Torrent från URL" + +#: glade/delugegtk.glade:644 glade/delugegtk.glade:914 +#: glade/dgtkpopups.glade:81 +msgid "Remove Torrent" +msgstr "Ta bort Torrent" + +#: glade/delugegtk.glade:658 +msgid "Clear Completed" +msgstr "Rensa färdiga" + +#: glade/delugegtk.glade:692 +msgid "_Edit" +msgstr "_Redigera" + +#: glade/delugegtk.glade:709 glade/dgtkpopups.glade:221 +#: glade/dgtkpref.glade:697 src/delugegtk.py:177 +msgid "Plugins" +msgstr "Insticksmoduler" + +#: glade/delugegtk.glade:729 +msgid "_View" +msgstr "_Visa" + +#: glade/delugegtk.glade:737 +msgid "Toolbar" +msgstr "Verktygsfält" + +#: glade/delugegtk.glade:755 +msgid "Columns" +msgstr "Kolumner" + +#: glade/delugegtk.glade:763 glade/dgtkpopups.glade:10 src/delugegtk.py:349 +#: src/delugegtk.py:479 +msgid "Size" +msgstr "Storlek" + +#: glade/delugegtk.glade:772 glade/dgtkpopups.glade:19 src/delugegtk.py:350 +msgid "Status" +msgstr "Status" + +#: glade/delugegtk.glade:781 glade/dgtkpopups.glade:28 src/delugegtk.py:351 +msgid "Seeders" +msgstr "Seedare" + +#: glade/delugegtk.glade:799 src/delugegtk.py:353 src/delugegtk.py:477 +#: src/delugegtk.py:692 src/delugegtk.py:704 +msgid "Download" +msgstr "Nedladdning" + +#: glade/delugegtk.glade:808 src/delugegtk.py:354 src/delugegtk.py:693 +#: src/delugegtk.py:705 +msgid "Upload" +msgstr "Uppladdning" + +#: glade/delugegtk.glade:817 glade/dgtkpopups.glade:64 src/delugegtk.py:355 +msgid "Time Remaining" +msgstr "Återstående tid" + +#: glade/delugegtk.glade:826 glade/dgtkpopups.glade:73 +msgid "Share Ratio" +msgstr "Delningsproportion" + +#: glade/delugegtk.glade:843 +msgid "_Help" +msgstr "_Hjälp" + +#: glade/delugegtk.glade:927 +msgid "Clear Finished Torrents" +msgstr "Ta bort färdiga Torrent-filer" + +#: glade/delugegtk.glade:949 +msgid "Start / Pause" +msgstr "Starta / Pausa" + +#: glade/delugegtk.glade:962 +msgid "Queue Torrent Up" +msgstr "Flytta Torrent uppåt i kön" + +#: glade/delugegtk.glade:975 +msgid "Queue Torrent Down" +msgstr "Flytta Torrent nedåt i kön" + +#: glade/dgtkpopups.glade:46 +msgid "Download Speed" +msgstr "Nedladdningshastighet" + +#: glade/dgtkpopups.glade:55 +msgid "Upload Speed" +msgstr "Uppladdningshastighet" + +#: glade/dgtkpopups.glade:97 +msgid "" +"Are you sure you want to remove the selected torrent(s) from Deluge?" +msgstr "Är du säker att du vill ta bort valda torrent(s) från Deluge?" + +#: glade/dgtkpopups.glade:107 +msgid "Delete downloaded files" +msgstr "Radera nedladdade filer" + +#: glade/dgtkpopups.glade:167 +msgid "Show/Hide" +msgstr "Visa/Dölj" + +#: glade/dgtkpopups.glade:175 +msgid "Add a Torrent..." +msgstr "Lägg till en Torrent..." + +#: glade/dgtkpopups.glade:191 src/delugegtk.py:175 +msgid "Clear Finished" +msgstr "Ta bort färdiga" + +#: glade/dgtkpopups.glade:212 glade/dgtkpref.glade:672 +msgid "gtk-preferences" +msgstr "gtk-preferences" + +#: glade/dgtkpopups.glade:242 +msgid "gtk-quit" +msgstr "gtk-quit" + +#: glade/dgtkpref.glade:8 +msgid "Preferences Dialog" +msgstr "Inställningsdialogrutan" + +#: glade/dgtkpref.glade:53 +msgid "Enable system tray icon" +msgstr "Aktivera ikon i systembrickan" + +#: glade/dgtkpref.glade:65 +msgid "Minimize to tray on close" +msgstr "Minimera till systembrickan vid stängning" + +#: glade/dgtkpref.glade:80 +msgid "Options" +msgstr "Inställningar" + +#: glade/dgtkpref.glade:115 +msgid "Save all downloads to:" +msgstr "Spara alla nedladdningar till:" + +#: glade/dgtkpref.glade:127 +msgid "Ask me where to save each download" +msgstr "Fråga var varje nedladdning ska sparas" + +#: glade/dgtkpref.glade:139 +msgid "Save Location" +msgstr "Sparplats" + +#: glade/dgtkpref.glade:180 +msgid "" +"Stop seeding torrents when\n" +"their share ratio reaches:" +msgstr "" +"Sluta seeda Torrenter när\n" +"deras delningsproportion når:" + +#: glade/dgtkpref.glade:190 +msgid "Seeding" +msgstr "Seedar" + +#: glade/dgtkpref.glade:209 +msgid "Use compact storage allocation" +msgstr "Använd kompakt lagringsallokering" + +#: glade/dgtkpref.glade:216 +msgid "Storage" +msgstr "Lagring" + +#: glade/dgtkpref.glade:246 +msgid "General" +msgstr "Generell" + +#: glade/dgtkpref.glade:280 +msgid "Try from:" +msgstr "Försök från:" + +#: glade/dgtkpref.glade:286 +msgid "to:" +msgstr "till:" + +#: glade/dgtkpref.glade:320 +msgid "Active port:" +msgstr "Aktiv port:" + +#: glade/dgtkpref.glade:332 +msgid "0000" +msgstr "0000" + +#: glade/dgtkpref.glade:343 +msgid "Test Port" +msgstr "Testa port" + +#: glade/dgtkpref.glade:357 +msgid "TCP Port" +msgstr "TCP-port" + +#: glade/dgtkpref.glade:380 +msgid "(-1 is unlimited)" +msgstr "(-1 är obegränsad)" + +#: glade/dgtkpref.glade:390 glade/dgtkpref.glade:511 +msgid "KB/s" +msgstr "Kibyte/s" + +#: glade/dgtkpref.glade:436 +msgid "Maximum Download Rate:" +msgstr "Högsta nedladdningshastighet:" + +#: glade/dgtkpref.glade:447 +msgid "Upload Slots" +msgstr "Uppladdningsplatser" + +#: glade/dgtkpref.glade:458 +msgid "Maximum Connections" +msgstr "Maximalt antal anslutningar" + +#: glade/dgtkpref.glade:485 +msgid "Maximum Upload Rate:" +msgstr "Högsta uppladdningshastighet:" + +#: glade/dgtkpref.glade:548 +msgid "Bandwidth " +msgstr "Bandbredd " + +#: glade/dgtkpref.glade:576 +msgid "Network" +msgstr "Nätverk" + +#: glade/dgtkpref.glade:598 +msgid "gtk-cancel" +msgstr "gtk-cancel" + +#: glade/dgtkpref.glade:605 +msgid "gtk-ok" +msgstr "gtk-ok" + +#: glade/dgtkpref.glade:625 +msgid "Plugin Manager" +msgstr "Insticksmodulshanterare" + +#: glade/dgtkpref.glade:718 +msgid "gtk-close" +msgstr "gtk-close" + +#: src/dcommon.py:41 src/delugegtk.py:330 +msgid "Infinity" +msgstr "Oändlighet" + +#: src/dcommon.py:105 +msgid "Error: no webbrowser found" +msgstr "Fel: ingen webbläsare funnen" + +#: src/deluge.py:332 +msgid "File was not found" +msgstr "Fil hittades inte" + +#: src/delugegtk.py:173 +msgid "Show / Hide Window" +msgstr "Visa / Göm fönster" + +#: src/delugegtk.py:250 +msgid "Plugin" +msgstr "Insticksmodul" + +#: src/delugegtk.py:252 +msgid "Enabled" +msgstr "Aktiverad" + +#: src/delugegtk.py:340 +msgid "Unknown" +msgstr "Okänd" + +#: src/delugegtk.py:348 +msgid "Name" +msgstr "Namn" + +#: src/delugegtk.py:356 +msgid "Ratio" +msgstr "Förhållande" + +#: src/delugegtk.py:465 +msgid "IP Address" +msgstr "IP-adress" + +#: src/delugegtk.py:466 +msgid "Client" +msgstr "Klient" + +#: src/delugegtk.py:467 +msgid "Percent Complete" +msgstr "Procent färdig" + +#: src/delugegtk.py:468 +msgid "Download Rate" +msgstr "Nedladdningshastighet" + +#: src/delugegtk.py:469 +msgid "Upload Rate" +msgstr "Uppladdningshastighet" + +#: src/delugegtk.py:478 +msgid "Filename" +msgstr "Filnamn" + +#: src/delugegtk.py:480 +msgid "Offset" +msgstr "Förskjutning" + +#: src/delugegtk.py:668 +msgid "" +"For some reason, the previous state could not be loaded, so a blank state " +"has been loaded for you." +msgstr "" +"Föregående tillstund kunde inte laddas. Ett tomt tillstånd har laddats åt " +"dig." + +#: src/delugegtk.py:670 +msgid "Would you like to attempt to reload the previous session's downloads?" +msgstr "Ladda in föregående sessions nedladdningar?" + +#: src/delugegtk.py:679 +msgid "Choose the download directory for" +msgstr "Välj nedladdningskatalog för" + +#: src/delugegtk.py:692 src/delugegtk.py:704 +msgid "Connections" +msgstr "Anslutningar" + +#: src/delugegtk.py:703 +msgid "Deluge Bittorrent Client" +msgstr "Deluge BitTorrent-klient" + +#: src/delugegtk.py:891 +msgid "Add torrent from URL" +msgstr "Lägg till Torrent från URL" + +#: src/delugegtk.py:895 +msgid "Enter the URL of the .torrent to download" +msgstr "Lägg till URL till .torrent som skall laddas ned" + +#: src/delugegtk.py:959 +msgid "Warning - all downloaded files for this torrent will be deleted!" +msgstr "" +"Varning - alla nerladdade filer för denna Torrent kommer att raderas!" + +#: src/dgtk.py:71 +msgid "Choose a .torrent file" +msgstr "Välj en .torrent-fil" + +#: src/dgtk.py:76 +msgid "Torrent files" +msgstr "Torrentfil" + +#: src/dgtk.py:80 +msgid "All files" +msgstr "Alla filer" + +#: src/dgtk.py:97 +msgid "Choose a download directory" +msgstr "Välj katalog för nedladdning" + +#~ msgid "D/L" +#~ msgstr "Nedladdning" + +#~ msgid "U/L" +#~ msgstr "Uppladdning" + +#~ msgid "ETA" +#~ msgstr "Uppskattad tid kvar" + +#~ msgid "Share %" +#~ msgstr "Delning %" + +#~ msgid "path" +#~ msgstr "Plats" + +#~ msgid "size" +#~ msgstr "Storlek" + +#~ msgid "offset" +#~ msgstr "Offset" + +#~ msgid "download?" +#~ msgstr "Ladda ned?" + +#~ msgid "IP" +#~ msgstr "IP-adress" + +#~ msgid "[none incoming]" +#~ msgstr "[inga inkommande]" + +#~ msgid "Connections: " +#~ msgstr "Anslutningar: " + +#~ msgid "Download Speed:" +#~ msgstr "Nedladdningshastighet:" + +#~ msgid "Upload Speed:" +#~ msgstr "Uppladdningshastighet:" + +#~ msgid "" +#~ "Shutting down torrent engine (may take time to notify trackers we are " +#~ "leaving)..." +#~ msgstr "" +#~ "Stänger ner programmet (det kan ta ett tag att avsluta alla anslutningar)" + +#~ msgid "Select Torrent" +#~ msgstr "Välj Torrent" + +#~ msgid "infinity" +#~ msgstr "Oändligt" + +#~ msgid "1 second" +#~ msgstr "1 sekund" + +#~ msgid "seconds" +#~ msgstr "sekunder" + +#~ msgid "1 minute" +#~ msgstr "1 minut" + +#~ msgid "minutes" +#~ msgstr "minuter" + +#~ msgid "1 hour" +#~ msgstr "1 timma" + +#~ msgid "hours" +#~ msgstr "timmar" + +#~ msgid "1 day" +#~ msgstr "1 dag" + +#~ msgid "days" +#~ msgstr "dagar" + +#~ msgid "1 week" +#~ msgstr "1 vecka" + +#~ msgid "weeks" +#~ msgstr "veckor" + +#~ msgid "Search string" +#~ msgstr "Söksträng" + +#~ msgid "Application Name" +#~ msgstr "Applikationsnamn" + +#~ msgid "translator-credits" +#~ msgstr "Petter Viklund" + +#~ msgid "Add a Torrent" +#~ msgstr "Lägg till Torrent" + +#~ msgid "Deluge Support _Forums" +#~ msgstr "Deluga support_forum" + +#~ msgid "Force Pause/Resume Torrent" +#~ msgstr "Pausa/Fortsätt Torrentnedladdning" + +#~ msgid "Manage engines" +#~ msgstr "Hantera motorer" + +#~ msgid "Open the online support forums in your web browser" +#~ msgstr "Öppna online-supportforum i webbläsare" + +#~ msgid "Search..." +#~ msgstr "Sök..." + +#~ msgid "Select a bittorrent search engine" +#~ msgstr "Välj BitTorrent-sökmotor" + +#~ msgid "Show/Hide the Details Pane" +#~ msgstr "Visa/Dölj detaljpanelen" + +#~ msgid "Translate This Application..." +#~ msgstr "Översätt det här programmet" + +#~ msgid "Update Tracker" +#~ msgstr "Uppdatera Tracker" + +#~ msgid "_Update Tracker" +#~ msgstr "_Uppdatera Tracker" + +#~ msgid "Bandwidth (KB/s):" +#~ msgstr "Bandbredd (KB/s):" + +#~ msgid "Save Location:" +#~ msgstr "Spara sökväg:" + +#~ msgid "Sharing:" +#~ msgstr "Delning:" + +#~ msgid "TCP Port Range:" +#~ msgstr "Spännvid för TCP-portar:" + +#~ msgid "Automatically stop seeding torrents when their share ratio reaches:" +#~ msgstr "Sluta seeda Torrent när delningsproportionen når:" + +#~ msgid "Deluge Preferences" +#~ msgstr "Deluge-inställningar" + +#~ msgid "From:" +#~ msgstr "Från:" + +#~ msgid "Maximum number of Downloads:" +#~ msgstr "Högsta antalet nedladdningar:" + +#~ msgid "Maximum number of Uploads:" +#~ msgstr "Högsta antalet uppladdningar:" + +#~ msgid "Select a save location for each download" +#~ msgstr "Välj sparsökväg för varje nedladdning" + +#~ msgid "To:" +#~ msgstr "Till:" + +#, no-c-format +#~ msgid "" +#~ "Add a new search engine by entering a Name and a URL. For Name, enter the " +#~ "name of the search engine to be used. For URL, enter the url of the seach " +#~ "page. The user's search query will be added to the end of the url, or " +#~ "replace any instances of %%query%% in the URL.\n" +#~ "For example, a Google search would be:\n" +#~ "Name: Google\n" +#~ "URL: http://www.google.com/search?hl=en" +#~ msgstr "" +#~ "Skriv in sökmotorns Namn samt en Länk till den. Länken skall peka på " +#~ "sökmotorns söksida. Din sökförfrågan läggs sedan till i slutet av " +#~ "Länkadressen, eller ersätter %%query%% i den om detta hittas.\n" +#~ "Här är ett exempel på hur det skulle se om man vill använda sig av Googles " +#~ "sökmotor:\n" +#~ "Namn: Google\n" +#~ "Länk: http://www.google.com/search?hl=se" + +#~ msgid "Help" +#~ msgstr "Hjälp" + +#~ msgid "Manage Search Plugins" +#~ msgstr "Hantera sök-insticksmoduler" + +#~ msgid "Name:" +#~ msgstr "Namn:" + +#~ msgid "URL:" +#~ msgstr "Adress:" + +#~ msgid "gtk-add" +#~ msgstr "gtk-add" + +#~ msgid "gtk-remove" +#~ msgstr "gtk-remove" + +#~ msgid "Force Pause" +#~ msgstr "Tvinga paus" + +#~ msgid "Update Trackers" +#~ msgstr "Uppdatera Trackrar" + +#~ msgid "Total Size" +#~ msgstr "Sammanlagd storlek" + +#~ msgid "Pieces" +#~ msgstr "Delar" + +#~ msgid "Total Downloaded" +#~ msgstr "Sammanlagt nedladdat" + +#~ msgid "Total Uploaded" +#~ msgstr "Sammanlagt uppladdat" + +#~ msgid "Percentage Done" +#~ msgstr "Procentandel klar" + +#~ msgid "Downloaded This Session" +#~ msgstr "Nedladdat vid detta tillfälle" + +#~ msgid "Uploaded This Session" +#~ msgstr "Uppladdat vid detta tillfälle" + +#~ msgid "Tracker" +#~ msgstr "Tracker" + +#~ msgid "Tracker Response" +#~ msgstr "Trackersvar" + +#~ msgid "Tracker Status" +#~ msgstr "Trackerstatus" + +#~ msgid "Next Announce" +#~ msgstr "Nästa annonsering" + +#~ msgid "Paused" +#~ msgstr "Pausad" + +#~ msgid "Queued" +#~ msgstr "Köad" + +#~ msgid "Waiting to check" +#~ msgstr "Väntar på att kontrollera" + +#~ msgid "Checking" +#~ msgstr "Kontrollerar" + +#~ msgid "Downloading (T?)" +#~ msgstr "Laddar ned (T?)" + +#~ msgid "Downloading Meta" +#~ msgstr "Laddar ned meta" + +#~ msgid "Downloading" +#~ msgstr "Laddar ned" + +#~ msgid "Finished" +#~ msgstr "Färdig" + +#~ msgid "Allocating" +#~ msgstr "Allokerar" + +#~ msgid "N/A" +#~ msgstr "Ej tillgänglig" + +#~ msgid "INF" +#~ msgstr "INF" + +#~ msgid "Invalid request" +#~ msgstr "Ogiltig begäran" + +#~ msgid "File error" +#~ msgstr "Filfel" + +#~ msgid "Hash failed" +#~ msgstr "Hash misslyckades" + +#~ msgid "Peer banned" +#~ msgstr "Jämlike bannlyst" + +#~ msgid "Fastresume rejected" +#~ msgstr "Snabbstart avslagen" + +#~ msgid "Event" +#~ msgstr "Händelse" \ No newline at end of file diff --git a/encryption/po/tr.po b/encryption/po/tr.po new file mode 100644 index 000000000..cb74173c4 --- /dev/null +++ b/encryption/po/tr.po @@ -0,0 +1,469 @@ +# Turkish translation for deluge +# Copyright (c) 2006 Rosetta Contributors and Canonical Ltd 2006 +# This file is distributed under the same license as the deluge package. +# FIRST AUTHOR , 2006. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: deluge\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2007-03-06 16:19-0500\n" +"PO-Revision-Date: 2007-03-09 22:33+0000\n" +"Last-Translator: webdr \n" +"Language-Team: Turkish \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Rosetta-Export-Date: 2007-03-18 15:58+0000\n" + +#: glade/delugegtk.glade:93 +msgid "Estimated Time Remaining:" +msgstr "Kalan Zaman:" + +#: glade/delugegtk.glade:131 +msgid "Peers:" +msgstr "Eşler:" + +#: glade/delugegtk.glade:145 +msgid "Seeders:" +msgstr "Kaynaklar:" + +#: glade/delugegtk.glade:157 +msgid "Total Size:" +msgstr "Toplam Boyut:" + +#: glade/delugegtk.glade:169 +msgid "Total Downloaded:" +msgstr "Toplam İndirilen:" + +#: glade/delugegtk.glade:181 +msgid "Downloaded this session:" +msgstr "Bu oturumda gönderilen:" + +#: glade/delugegtk.glade:229 +msgid "Tracker:" +msgstr "İzleyici:" + +#: glade/delugegtk.glade:241 +msgid "Tracker Response:" +msgstr "İzleyici Cevabı:" + +#: glade/delugegtk.glade:253 +msgid "Tracker Status:" +msgstr "İzleyici Durumu:" + +#: glade/delugegtk.glade:277 +msgid "Next Announce:" +msgstr "Sonraki Duyuru:" + +#: glade/delugegtk.glade:303 +msgid "Pieces:" +msgstr "Parçalar:" + +#: glade/delugegtk.glade:317 +msgid "Total Uploaded:" +msgstr "Toplam Gönderilen:" + +#: glade/delugegtk.glade:355 +msgid "Share Ratio:" +msgstr "Paylaşım Oranı:" + +#: glade/delugegtk.glade:369 +msgid "Uploaded This Session:" +msgstr "Bu Oturumda Gönderilen:" + +#: glade/delugegtk.glade:431 +msgid "Use compact storage allocation:" +msgstr "Katı depolama ataması kullan:" + +#: glade/delugegtk.glade:455 +msgid "Download Rate:" +msgstr "İndirme Hızı:" + +#: glade/delugegtk.glade:479 +msgid "Upload Rate:" +msgstr "Gönderme Hızı:" + +#: glade/delugegtk.glade:522 glade/delugegtk.glade:746 +msgid "Details" +msgstr "Ayrıntılar" + +#: glade/delugegtk.glade:549 glade/delugegtk.glade:790 +#: glade/dgtkpopups.glade:37 src/delugegtk.py:352 +msgid "Peers" +msgstr "Eşler" + +#: glade/delugegtk.glade:578 +msgid "Files" +msgstr "Dosyalar" + +#: glade/delugegtk.glade:613 +msgid "_File" +msgstr "_Dosya" + +#: glade/delugegtk.glade:620 glade/delugegtk.glade:901 src/delugegtk.py:174 +msgid "Add Torrent" +msgstr "Torrent Ekle" + +#: glade/delugegtk.glade:636 +msgid "Add Torrent from URL" +msgstr "URL'den Torrent Ekle" + +#: glade/delugegtk.glade:644 glade/delugegtk.glade:914 +#: glade/dgtkpopups.glade:81 +msgid "Remove Torrent" +msgstr "Torrent Kaldır" + +#: glade/delugegtk.glade:658 +msgid "Clear Completed" +msgstr "Tamamlananları Temizle" + +#: glade/delugegtk.glade:692 +msgid "_Edit" +msgstr "_Düzen" + +#: glade/delugegtk.glade:709 glade/dgtkpopups.glade:221 +#: glade/dgtkpref.glade:697 src/delugegtk.py:177 +msgid "Plugins" +msgstr "Eklentiler" + +#: glade/delugegtk.glade:729 +msgid "_View" +msgstr "_Görünüm" + +#: glade/delugegtk.glade:737 +msgid "Toolbar" +msgstr "Araç Çubuğu" + +#: glade/delugegtk.glade:755 +msgid "Columns" +msgstr "Sütunlar" + +#: glade/delugegtk.glade:763 glade/dgtkpopups.glade:10 src/delugegtk.py:349 +#: src/delugegtk.py:479 +msgid "Size" +msgstr "Boyut" + +#: glade/delugegtk.glade:772 glade/dgtkpopups.glade:19 src/delugegtk.py:350 +msgid "Status" +msgstr "Durum" + +#: glade/delugegtk.glade:781 glade/dgtkpopups.glade:28 src/delugegtk.py:351 +msgid "Seeders" +msgstr "Kaynaklar" + +#: glade/delugegtk.glade:799 src/delugegtk.py:353 src/delugegtk.py:477 +#: src/delugegtk.py:692 src/delugegtk.py:704 +msgid "Download" +msgstr "İndir" + +#: glade/delugegtk.glade:808 src/delugegtk.py:354 src/delugegtk.py:693 +#: src/delugegtk.py:705 +msgid "Upload" +msgstr "Gönder" + +#: glade/delugegtk.glade:817 glade/dgtkpopups.glade:64 src/delugegtk.py:355 +msgid "Time Remaining" +msgstr "Kalan Süre" + +#: glade/delugegtk.glade:826 glade/dgtkpopups.glade:73 +msgid "Share Ratio" +msgstr "Paylaşım Oranı" + +#: glade/delugegtk.glade:843 +msgid "_Help" +msgstr "_Yardım" + +#: glade/delugegtk.glade:927 +msgid "Clear Finished Torrents" +msgstr "Biten Torrentleri Temizle" + +#: glade/delugegtk.glade:949 +msgid "Start / Pause" +msgstr "Başlat / Duraklat" + +#: glade/delugegtk.glade:962 +msgid "Queue Torrent Up" +msgstr "Sıradaki Torrent Yukarı" + +#: glade/delugegtk.glade:975 +msgid "Queue Torrent Down" +msgstr "Sıradaki Torrent Aşağı" + +#: glade/dgtkpopups.glade:46 +msgid "Download Speed" +msgstr "İndirme Hızı" + +#: glade/dgtkpopups.glade:55 +msgid "Upload Speed" +msgstr "Gönderme Hızı" + +#: glade/dgtkpopups.glade:97 +msgid "" +"Are you sure you want to remove the selected torrent(s) from Deluge?" +msgstr "" +"Seçilen torrent(ler)i Deluge'den kaldırmak istediğinizden emin " +"misiniz?" + +#: glade/dgtkpopups.glade:107 +msgid "Delete downloaded files" +msgstr "İndirilmiş dosyaları sil" + +#: glade/dgtkpopups.glade:167 +msgid "Show/Hide" +msgstr "Göster/Gizle" + +#: glade/dgtkpopups.glade:175 +msgid "Add a Torrent..." +msgstr "Torrent Ekle..." + +#: glade/dgtkpopups.glade:191 src/delugegtk.py:175 +msgid "Clear Finished" +msgstr "Bitenleri Temizle" + +#: glade/dgtkpopups.glade:212 glade/dgtkpref.glade:672 +msgid "gtk-preferences" +msgstr "gtk-tercihleri" + +#: glade/dgtkpopups.glade:242 +msgid "gtk-quit" +msgstr "gtk-çıkış" + +#: glade/dgtkpref.glade:8 +msgid "Preferences Dialog" +msgstr "Tercihler" + +#: glade/dgtkpref.glade:53 +msgid "Enable system tray icon" +msgstr "Sistem tepsisi simgesini etkinleştir" + +#: glade/dgtkpref.glade:65 +msgid "Minimize to tray on close" +msgstr "Kapattığımda simge durumuna küçült" + +#: glade/dgtkpref.glade:80 +msgid "Options" +msgstr "Ayarlar" + +#: glade/dgtkpref.glade:115 +msgid "Save all downloads to:" +msgstr "Tüm indirmeleri kaydet:" + +#: glade/dgtkpref.glade:127 +msgid "Ask me where to save each download" +msgstr "Her indirmeyi nereye kaydedeceğimi sor" + +#: glade/dgtkpref.glade:139 +msgid "Save Location" +msgstr "Kayıt Konumu" + +#: glade/dgtkpref.glade:180 +msgid "" +"Stop seeding torrents when\n" +"their share ratio reaches:" +msgstr "" +"Kaynak olunan torrentlerin\n" +"paylaşım oranı bu değere ulaşınca durdur:" + +#: glade/dgtkpref.glade:190 +msgid "Seeding" +msgstr "Kaynak/Gönderiliyor" + +#: glade/dgtkpref.glade:209 +msgid "Use compact storage allocation" +msgstr "Katı depolama ataması kullan" + +#: glade/dgtkpref.glade:216 +msgid "Storage" +msgstr "Saklama" + +#: glade/dgtkpref.glade:246 +msgid "General" +msgstr "Genel" + +#: glade/dgtkpref.glade:280 +msgid "Try from:" +msgstr "Burdan dene:" + +#: glade/dgtkpref.glade:286 +msgid "to:" +msgstr "buraya:" + +#: glade/dgtkpref.glade:320 +msgid "Active port:" +msgstr "Aktif port:" + +#: glade/dgtkpref.glade:332 +msgid "0000" +msgstr "0000" + +#: glade/dgtkpref.glade:343 +msgid "Test Port" +msgstr "Portu Test Et" + +#: glade/dgtkpref.glade:357 +msgid "TCP Port" +msgstr "TCP Portu" + +#: glade/dgtkpref.glade:380 +msgid "(-1 is unlimited)" +msgstr "(-1 sınırsız demektir)" + +#: glade/dgtkpref.glade:390 glade/dgtkpref.glade:511 +msgid "KB/s" +msgstr "KB/s" + +#: glade/dgtkpref.glade:436 +msgid "Maximum Download Rate:" +msgstr "En Yüksek İndirme Hızı:" + +#: glade/dgtkpref.glade:447 +msgid "Upload Slots" +msgstr "Gönderme Slotları" + +#: glade/dgtkpref.glade:458 +msgid "Maximum Connections" +msgstr "" + +#: glade/dgtkpref.glade:485 +msgid "Maximum Upload Rate:" +msgstr "En Yüksek Gönderme Hızı:" + +#: glade/dgtkpref.glade:548 +msgid "Bandwidth " +msgstr "Band Genişliği " + +#: glade/dgtkpref.glade:576 +msgid "Network" +msgstr "Ağ" + +#: glade/dgtkpref.glade:598 +msgid "gtk-cancel" +msgstr "gtk-İptal" + +#: glade/dgtkpref.glade:605 +msgid "gtk-ok" +msgstr "gtk-Tamam" + +#: glade/dgtkpref.glade:625 +msgid "Plugin Manager" +msgstr "Eklenti Yöneticisi" + +#: glade/dgtkpref.glade:718 +msgid "gtk-close" +msgstr "gtk-kapat" + +#: src/dcommon.py:41 src/delugegtk.py:330 +msgid "Infinity" +msgstr "Sonsuzluk" + +#: src/dcommon.py:105 +msgid "Error: no webbrowser found" +msgstr "Hata: tarayıcı bulunamadı" + +#: src/deluge.py:332 +msgid "File was not found" +msgstr "" + +#: src/delugegtk.py:173 +msgid "Show / Hide Window" +msgstr "Pencereyi Göster / Gizle" + +#: src/delugegtk.py:250 +msgid "Plugin" +msgstr "Eklenti" + +#: src/delugegtk.py:252 +msgid "Enabled" +msgstr "Etkin" + +#: src/delugegtk.py:340 +msgid "Unknown" +msgstr "Bilinmeyen" + +#: src/delugegtk.py:348 +msgid "Name" +msgstr "İsim" + +#: src/delugegtk.py:356 +msgid "Ratio" +msgstr "Oran" + +#: src/delugegtk.py:465 +msgid "IP Address" +msgstr "IP Adresi" + +#: src/delugegtk.py:466 +msgid "Client" +msgstr "İstemci:" + +#: src/delugegtk.py:467 +msgid "Percent Complete" +msgstr "Tamamlanma Yüzdesi" + +#: src/delugegtk.py:468 +msgid "Download Rate" +msgstr "İndirme Hızı" + +#: src/delugegtk.py:469 +msgid "Upload Rate" +msgstr "Gönderme Hızı" + +#: src/delugegtk.py:478 +msgid "Filename" +msgstr "Dosya adı" + +#: src/delugegtk.py:480 +msgid "Offset" +msgstr "Konum" + +#: src/delugegtk.py:668 +msgid "" +"For some reason, the previous state could not be loaded, so a blank state " +"has been loaded for you." +msgstr "" + +#: src/delugegtk.py:670 +msgid "Would you like to attempt to reload the previous session's downloads?" +msgstr "" + +#: src/delugegtk.py:679 +msgid "Choose the download directory for" +msgstr "" + +#: src/delugegtk.py:692 src/delugegtk.py:704 +msgid "Connections" +msgstr "Bağlantılar" + +#: src/delugegtk.py:703 +msgid "Deluge Bittorrent Client" +msgstr "Deluge Bittorrent İstemcisi" + +#: src/delugegtk.py:891 +msgid "Add torrent from URL" +msgstr "URL'den torrent ekle" + +#: src/delugegtk.py:895 +msgid "Enter the URL of the .torrent to download" +msgstr "İndireceğiniz .torrent'in URL'sini girin" + +#: src/delugegtk.py:959 +msgid "Warning - all downloaded files for this torrent will be deleted!" +msgstr "Uyarı - Bu torrente ait indirilen dosyaların tümü silinecek!" + +#: src/dgtk.py:71 +msgid "Choose a .torrent file" +msgstr "Bir .torrent dosyası seçin" + +#: src/dgtk.py:76 +msgid "Torrent files" +msgstr "Torrent Dosyaları" + +#: src/dgtk.py:80 +msgid "All files" +msgstr "Tüm dosyalar" + +#: src/dgtk.py:97 +msgid "Choose a download directory" +msgstr "Bir indirme dizini seçin" \ No newline at end of file diff --git a/encryption/po/zh_CN.po b/encryption/po/zh_CN.po new file mode 100644 index 000000000..fd6602e93 --- /dev/null +++ b/encryption/po/zh_CN.po @@ -0,0 +1,472 @@ +# Chinese (China) translation for deluge +# Copyright (c) 2006 Rosetta Contributors and Canonical Ltd 2006 +# This file is distributed under the same license as the deluge package. +# FIRST AUTHOR , 2006. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: deluge\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2007-03-06 16:19-0500\n" +"PO-Revision-Date: 2007-03-14 18:15+0000\n" +"Last-Translator: darh00 \n" +"Language-Team: Chinese (China) \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Rosetta-Export-Date: 2007-03-18 15:58+0000\n" + +#: glade/delugegtk.glade:93 +msgid "Estimated Time Remaining:" +msgstr "估计剩余时间" + +#: glade/delugegtk.glade:131 +msgid "Peers:" +msgstr "连接数" + +#: glade/delugegtk.glade:145 +msgid "Seeders:" +msgstr "种子数" + +#: glade/delugegtk.glade:157 +msgid "Total Size:" +msgstr "总大小" + +#: glade/delugegtk.glade:169 +msgid "Total Downloaded:" +msgstr "总下载量" + +#: glade/delugegtk.glade:181 +msgid "Downloaded this session:" +msgstr "该会话下载量" + +#: glade/delugegtk.glade:229 +msgid "Tracker:" +msgstr "" + +#: glade/delugegtk.glade:241 +msgid "Tracker Response:" +msgstr "Tracker 响应:" + +#: glade/delugegtk.glade:253 +msgid "Tracker Status:" +msgstr "Tracker 状态:" + +#: glade/delugegtk.glade:277 +msgid "Next Announce:" +msgstr "" + +#: glade/delugegtk.glade:303 +msgid "Pieces:" +msgstr "块:" + +#: glade/delugegtk.glade:317 +msgid "Total Uploaded:" +msgstr "总上传量" + +#: glade/delugegtk.glade:355 +msgid "Share Ratio:" +msgstr "分享率" + +#: glade/delugegtk.glade:369 +msgid "Uploaded This Session:" +msgstr "该会话上传量" + +#: glade/delugegtk.glade:431 +#, fuzzy +msgid "Use compact storage allocation:" +msgstr "使用紧凑的存储分配" + +#: glade/delugegtk.glade:455 +msgid "Download Rate:" +msgstr "下载速度" + +#: glade/delugegtk.glade:479 +#, fuzzy +msgid "Upload Rate:" +msgstr "上传速度" + +#: glade/delugegtk.glade:522 glade/delugegtk.glade:746 +msgid "Details" +msgstr "详细信息" + +#: glade/delugegtk.glade:549 glade/delugegtk.glade:790 +#: glade/dgtkpopups.glade:37 src/delugegtk.py:352 +msgid "Peers" +msgstr "连接数" + +#: glade/delugegtk.glade:578 +msgid "Files" +msgstr "" + +#: glade/delugegtk.glade:613 +msgid "_File" +msgstr "文件" + +#: glade/delugegtk.glade:620 glade/delugegtk.glade:901 src/delugegtk.py:174 +msgid "Add Torrent" +msgstr "添加种子" + +#: glade/delugegtk.glade:636 +msgid "Add Torrent from URL" +msgstr "从网址添加种子" + +#: glade/delugegtk.glade:644 glade/delugegtk.glade:914 +#: glade/dgtkpopups.glade:81 +msgid "Remove Torrent" +msgstr "删除种子" + +#: glade/delugegtk.glade:658 +msgid "Clear Completed" +msgstr "清除已完成的文件" + +#: glade/delugegtk.glade:692 +msgid "_Edit" +msgstr "编辑" + +#: glade/delugegtk.glade:709 glade/dgtkpopups.glade:221 +#: glade/dgtkpref.glade:697 src/delugegtk.py:177 +msgid "Plugins" +msgstr "" + +#: glade/delugegtk.glade:729 +msgid "_View" +msgstr "" + +#: glade/delugegtk.glade:737 +msgid "Toolbar" +msgstr "工具栏" + +#: glade/delugegtk.glade:755 +msgid "Columns" +msgstr "" + +#: glade/delugegtk.glade:763 glade/dgtkpopups.glade:10 src/delugegtk.py:349 +#: src/delugegtk.py:479 +msgid "Size" +msgstr "大小" + +#: glade/delugegtk.glade:772 glade/dgtkpopups.glade:19 src/delugegtk.py:350 +msgid "Status" +msgstr "状态" + +#: glade/delugegtk.glade:781 glade/dgtkpopups.glade:28 src/delugegtk.py:351 +msgid "Seeders" +msgstr "种子数" + +#: glade/delugegtk.glade:799 src/delugegtk.py:353 src/delugegtk.py:477 +#: src/delugegtk.py:692 src/delugegtk.py:704 +msgid "Download" +msgstr "下载" + +#: glade/delugegtk.glade:808 src/delugegtk.py:354 src/delugegtk.py:693 +#: src/delugegtk.py:705 +msgid "Upload" +msgstr "上传" + +#: glade/delugegtk.glade:817 glade/dgtkpopups.glade:64 src/delugegtk.py:355 +msgid "Time Remaining" +msgstr "剩余时间" + +#: glade/delugegtk.glade:826 glade/dgtkpopups.glade:73 +msgid "Share Ratio" +msgstr "分享率" + +#: glade/delugegtk.glade:843 +msgid "_Help" +msgstr "帮助" + +#: glade/delugegtk.glade:927 +msgid "Clear Finished Torrents" +msgstr "清除已完成种子" + +#: glade/delugegtk.glade:949 +msgid "Start / Pause" +msgstr "开始 / 停止" + +#: glade/delugegtk.glade:962 +msgid "Queue Torrent Up" +msgstr "上移" + +#: glade/delugegtk.glade:975 +msgid "Queue Torrent Down" +msgstr "下移" + +#: glade/dgtkpopups.glade:46 +msgid "Download Speed" +msgstr "下载速度" + +#: glade/dgtkpopups.glade:55 +msgid "Upload Speed" +msgstr "上传速度" + +#: glade/dgtkpopups.glade:97 +#, fuzzy +msgid "" +"Are you sure you want to remove the selected torrent(s) from Deluge?" +msgstr "您确定要删除选中的种子吗?" + +#: glade/dgtkpopups.glade:107 +msgid "Delete downloaded files" +msgstr "删除已下载的文件" + +#: glade/dgtkpopups.glade:167 +msgid "Show/Hide" +msgstr "显示/隐藏" + +#: glade/dgtkpopups.glade:175 +msgid "Add a Torrent..." +msgstr "添加种子..." + +#: glade/dgtkpopups.glade:191 src/delugegtk.py:175 +msgid "Clear Finished" +msgstr "清除已完成的" + +#: glade/dgtkpopups.glade:212 glade/dgtkpref.glade:672 +msgid "gtk-preferences" +msgstr "gtk-设定" + +#: glade/dgtkpopups.glade:242 +msgid "gtk-quit" +msgstr "" + +#: glade/dgtkpref.glade:8 +msgid "Preferences Dialog" +msgstr "偏好对话框" + +#: glade/dgtkpref.glade:53 +msgid "Enable system tray icon" +msgstr "启用系统托盘图标" + +#: glade/dgtkpref.glade:65 +msgid "Minimize to tray on close" +msgstr "关闭后最小化到系统托盘" + +#: glade/dgtkpref.glade:80 +msgid "Options" +msgstr "选项" + +#: glade/dgtkpref.glade:115 +msgid "Save all downloads to:" +msgstr "默认所有文件保存在:" + +#: glade/dgtkpref.glade:127 +msgid "Ask me where to save each download" +msgstr "每次下载前询问我保存路径" + +#: glade/dgtkpref.glade:139 +msgid "Save Location" +msgstr "保存路径" + +#: glade/dgtkpref.glade:180 +msgid "" +"Stop seeding torrents when\n" +"their share ratio reaches:" +msgstr "当分享率达到时停止做种" + +#: glade/dgtkpref.glade:190 +msgid "Seeding" +msgstr "做种" + +#: glade/dgtkpref.glade:209 +#, fuzzy +msgid "Use compact storage allocation" +msgstr "使用紧凑的存储分配" + +#: glade/dgtkpref.glade:216 +msgid "Storage" +msgstr "存储" + +#: glade/dgtkpref.glade:246 +msgid "General" +msgstr "" + +#: glade/dgtkpref.glade:280 +msgid "Try from:" +msgstr "" + +#: glade/dgtkpref.glade:286 +msgid "to:" +msgstr "" + +#: glade/dgtkpref.glade:320 +#, fuzzy +msgid "Active port:" +msgstr "活动端口:" + +#: glade/dgtkpref.glade:332 +msgid "0000" +msgstr "" + +#: glade/dgtkpref.glade:343 +msgid "Test Port" +msgstr "测试端口" + +#: glade/dgtkpref.glade:357 +msgid "TCP Port" +msgstr "TCP 端口" + +#: glade/dgtkpref.glade:380 +#, fuzzy +msgid "(-1 is unlimited)" +msgstr "(-1表示无限)" + +#: glade/dgtkpref.glade:390 glade/dgtkpref.glade:511 +msgid "KB/s" +msgstr "" + +#: glade/dgtkpref.glade:436 +msgid "Maximum Download Rate:" +msgstr "最高下载速度:" + +#: glade/dgtkpref.glade:447 +msgid "Upload Slots" +msgstr "" + +#: glade/dgtkpref.glade:458 +msgid "Maximum Connections" +msgstr "最大连接数" + +#: glade/dgtkpref.glade:485 +msgid "Maximum Upload Rate:" +msgstr "最高上传速度:" + +#: glade/dgtkpref.glade:548 +msgid "Bandwidth " +msgstr "带宽 " + +#: glade/dgtkpref.glade:576 +msgid "Network" +msgstr "网络" + +#: glade/dgtkpref.glade:598 +msgid "gtk-cancel" +msgstr "取消" + +#: glade/dgtkpref.glade:605 +msgid "gtk-ok" +msgstr "好" + +#: glade/dgtkpref.glade:625 +msgid "Plugin Manager" +msgstr "插件管理器" + +#: glade/dgtkpref.glade:718 +msgid "gtk-close" +msgstr "" + +#: src/dcommon.py:41 src/delugegtk.py:330 +msgid "Infinity" +msgstr "无穷" + +#: src/dcommon.py:105 +msgid "Error: no webbrowser found" +msgstr "错误: 没有找到Web浏览器" + +#: src/deluge.py:332 +msgid "File was not found" +msgstr "找不到文件" + +#: src/delugegtk.py:173 +msgid "Show / Hide Window" +msgstr "显示 / 隐藏 窗口" + +#: src/delugegtk.py:250 +msgid "Plugin" +msgstr "插件" + +#: src/delugegtk.py:252 +msgid "Enabled" +msgstr "" + +#: src/delugegtk.py:340 +msgid "Unknown" +msgstr "未知的" + +#: src/delugegtk.py:348 +msgid "Name" +msgstr "文件名" + +#: src/delugegtk.py:356 +msgid "Ratio" +msgstr "" + +#: src/delugegtk.py:465 +msgid "IP Address" +msgstr "IP地址" + +#: src/delugegtk.py:466 +msgid "Client" +msgstr "客户端" + +#: src/delugegtk.py:467 +msgid "Percent Complete" +msgstr "完成百分比" + +#: src/delugegtk.py:468 +msgid "Download Rate" +msgstr "下载速度" + +#: src/delugegtk.py:469 +msgid "Upload Rate" +msgstr "上传速度" + +#: src/delugegtk.py:478 +msgid "Filename" +msgstr "" + +#: src/delugegtk.py:480 +msgid "Offset" +msgstr "" + +#: src/delugegtk.py:668 +msgid "" +"For some reason, the previous state could not be loaded, so a blank state " +"has been loaded for you." +msgstr "" + +#: src/delugegtk.py:670 +msgid "Would you like to attempt to reload the previous session's downloads?" +msgstr "" + +#: src/delugegtk.py:679 +msgid "Choose the download directory for" +msgstr "选择下载目录" + +#: src/delugegtk.py:692 src/delugegtk.py:704 +msgid "Connections" +msgstr "连接数" + +#: src/delugegtk.py:703 +msgid "Deluge Bittorrent Client" +msgstr "Deluge BT下载客户端" + +#: src/delugegtk.py:891 +msgid "Add torrent from URL" +msgstr "从URL添加种子" + +#: src/delugegtk.py:895 +msgid "Enter the URL of the .torrent to download" +msgstr "输入种子的URL下载" + +#: src/delugegtk.py:959 +msgid "Warning - all downloaded files for this torrent will be deleted!" +msgstr "警告 - 该种子所有下载的文件将会被删除" + +#: src/dgtk.py:71 +msgid "Choose a .torrent file" +msgstr "选择种子文件" + +#: src/dgtk.py:76 +msgid "Torrent files" +msgstr "Torrent 文件" + +#: src/dgtk.py:80 +msgid "All files" +msgstr "所有文件" + +#: src/dgtk.py:97 +#, fuzzy +msgid "Choose a download directory" +msgstr "请选择下载存储目录" \ No newline at end of file diff --git a/encryption/po/zh_TW.po b/encryption/po/zh_TW.po new file mode 100644 index 000000000..aa73e5889 --- /dev/null +++ b/encryption/po/zh_TW.po @@ -0,0 +1,923 @@ +# Chinese (Taiwan) translation for deluge +# Copyright (c) 2006 Rosetta Contributors and Canonical Ltd 2006 +# This file is distributed under the same license as the deluge package. +# FIRST AUTHOR , 2006. +# +msgid "" +msgstr "" +"Project-Id-Version: Deluge VERSION\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2007-03-06 16:19-0500\n" +"PO-Revision-Date: 2007-03-10 07:36+0000\n" +"Last-Translator: cwchien \n" +"Language-Team: Cheng-Wei Chien \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Rosetta-Export-Date: 2007-03-18 15:58+0000\n" +"X-Generator: Pootle 0.10.1\n" +"X-Poedit-Language: Chinese\n" +"X-Poedit-Country: TAIWAN\n" +"X-Poedit-SourceCharset: utf-8\n" + +#: glade/delugegtk.glade:93 +msgid "Estimated Time Remaining:" +msgstr "預計剩餘時間:" + +#: glade/delugegtk.glade:131 +msgid "Peers:" +msgstr "用戶數:" + +#: glade/delugegtk.glade:145 +msgid "Seeders:" +msgstr "種子數:" + +#: glade/delugegtk.glade:157 +msgid "Total Size:" +msgstr "總大小:" + +#: glade/delugegtk.glade:169 +msgid "Total Downloaded:" +msgstr "總共下載:" + +#: glade/delugegtk.glade:181 +msgid "Downloaded this session:" +msgstr "這次作業階段共下載:" + +#: glade/delugegtk.glade:229 +msgid "Tracker:" +msgstr "伺服器:" + +#: glade/delugegtk.glade:241 +msgid "Tracker Response:" +msgstr "伺服器回應:" + +#: glade/delugegtk.glade:253 +msgid "Tracker Status:" +msgstr "伺服器狀態:" + +#: glade/delugegtk.glade:277 +msgid "Next Announce:" +msgstr "下次發佈:" + +#: glade/delugegtk.glade:303 +msgid "Pieces:" +msgstr "區塊:" + +#: glade/delugegtk.glade:317 +msgid "Total Uploaded:" +msgstr "總共上傳:" + +#: glade/delugegtk.glade:355 +msgid "Share Ratio:" +msgstr "分享率:" + +#: glade/delugegtk.glade:369 +msgid "Uploaded This Session:" +msgstr "這次作業階段共上傳:" + +#: glade/delugegtk.glade:431 +msgid "Use compact storage allocation:" +msgstr "使用壓縮儲存:" + +#: glade/delugegtk.glade:455 +msgid "Download Rate:" +msgstr "下載頻寬:" + +#: glade/delugegtk.glade:479 +msgid "Upload Rate:" +msgstr "上傳頻寬:" + +#: glade/delugegtk.glade:522 glade/delugegtk.glade:746 +msgid "Details" +msgstr "細節" + +#: glade/delugegtk.glade:549 glade/delugegtk.glade:790 +#: glade/dgtkpopups.glade:37 src/delugegtk.py:352 +msgid "Peers" +msgstr "用戶數" + +#: glade/delugegtk.glade:578 +msgid "Files" +msgstr "檔案" + +#: glade/delugegtk.glade:613 +msgid "_File" +msgstr "檔案 (_F)" + +#: glade/delugegtk.glade:620 glade/delugegtk.glade:901 src/delugegtk.py:174 +msgid "Add Torrent" +msgstr "新增種子" + +#: glade/delugegtk.glade:636 +msgid "Add Torrent from URL" +msgstr "從超連結新增種子" + +#: glade/delugegtk.glade:644 glade/delugegtk.glade:914 +#: glade/dgtkpopups.glade:81 +msgid "Remove Torrent" +msgstr "移除種子" + +#: glade/delugegtk.glade:658 +msgid "Clear Completed" +msgstr "清除已完成的下載" + +#: glade/delugegtk.glade:692 +msgid "_Edit" +msgstr "編輯 (_E)" + +#: glade/delugegtk.glade:709 glade/dgtkpopups.glade:221 +#: glade/dgtkpref.glade:697 src/delugegtk.py:177 +msgid "Plugins" +msgstr "插件" + +#: glade/delugegtk.glade:729 +msgid "_View" +msgstr "檢視 (_V)" + +#: glade/delugegtk.glade:737 +msgid "Toolbar" +msgstr "工具列" + +#: glade/delugegtk.glade:755 +msgid "Columns" +msgstr "行" + +#: glade/delugegtk.glade:763 glade/dgtkpopups.glade:10 src/delugegtk.py:349 +#: src/delugegtk.py:479 +msgid "Size" +msgstr "大小" + +#: glade/delugegtk.glade:772 glade/dgtkpopups.glade:19 src/delugegtk.py:350 +msgid "Status" +msgstr "狀態" + +#: glade/delugegtk.glade:781 glade/dgtkpopups.glade:28 src/delugegtk.py:351 +msgid "Seeders" +msgstr "種子數" + +#: glade/delugegtk.glade:799 src/delugegtk.py:353 src/delugegtk.py:477 +#: src/delugegtk.py:692 src/delugegtk.py:704 +msgid "Download" +msgstr "下載" + +#: glade/delugegtk.glade:808 src/delugegtk.py:354 src/delugegtk.py:693 +#: src/delugegtk.py:705 +msgid "Upload" +msgstr "上傳" + +#: glade/delugegtk.glade:817 glade/dgtkpopups.glade:64 src/delugegtk.py:355 +msgid "Time Remaining" +msgstr "剩餘時間" + +#: glade/delugegtk.glade:826 glade/dgtkpopups.glade:73 +msgid "Share Ratio" +msgstr "分享率" + +#: glade/delugegtk.glade:843 +msgid "_Help" +msgstr "說明 (_H)" + +#: glade/delugegtk.glade:927 +msgid "Clear Finished Torrents" +msgstr "清除已完成的種子" + +#: glade/delugegtk.glade:949 +msgid "Start / Pause" +msgstr "開始 / 暫停" + +#: glade/delugegtk.glade:962 +msgid "Queue Torrent Up" +msgstr "佇列中種子上移" + +#: glade/delugegtk.glade:975 +msgid "Queue Torrent Down" +msgstr "佇列中種子下移" + +#: glade/dgtkpopups.glade:46 +msgid "Download Speed" +msgstr "下載速度" + +#: glade/dgtkpopups.glade:55 +msgid "Upload Speed" +msgstr "上傳速度" + +#: glade/dgtkpopups.glade:97 +msgid "" +"Are you sure you want to remove the selected torrent(s) from Deluge?" +msgstr "您確定要從 Deluge 中移除所選的種子(們)?" + +#: glade/dgtkpopups.glade:107 +msgid "Delete downloaded files" +msgstr "刪除已下載的檔案" + +#: glade/dgtkpopups.glade:167 +msgid "Show/Hide" +msgstr "顯示 / 隱藏" + +#: glade/dgtkpopups.glade:175 +msgid "Add a Torrent..." +msgstr "新增種子..." + +#: glade/dgtkpopups.glade:191 src/delugegtk.py:175 +msgid "Clear Finished" +msgstr "清除已完成的下載" + +#: glade/dgtkpopups.glade:212 glade/dgtkpref.glade:672 +msgid "gtk-preferences" +msgstr "偏好設定" + +#: glade/dgtkpopups.glade:242 +msgid "gtk-quit" +msgstr "離開" + +#: glade/dgtkpref.glade:8 +msgid "Preferences Dialog" +msgstr "偏好設定對話框" + +#: glade/dgtkpref.glade:53 +msgid "Enable system tray icon" +msgstr "啟用系統列圖示" + +#: glade/dgtkpref.glade:65 +msgid "Minimize to tray on close" +msgstr "關閉時最小化到系統列" + +#: glade/dgtkpref.glade:80 +msgid "Options" +msgstr "選項" + +#: glade/dgtkpref.glade:115 +msgid "Save all downloads to:" +msgstr "儲存所有下載至:" + +#: glade/dgtkpref.glade:127 +msgid "Ask me where to save each download" +msgstr "每次都問我下載到何處" + +#: glade/dgtkpref.glade:139 +msgid "Save Location" +msgstr "儲存位置" + +#: glade/dgtkpref.glade:180 +msgid "" +"Stop seeding torrents when\n" +"their share ratio reaches:" +msgstr "" +"當分享率達到下列值\n" +"就停止作種:" + +#: glade/dgtkpref.glade:190 +msgid "Seeding" +msgstr "正在作種" + +#: glade/dgtkpref.glade:209 +msgid "Use compact storage allocation" +msgstr "使用壓縮儲存" + +#: glade/dgtkpref.glade:216 +msgid "Storage" +msgstr "儲存" + +#: glade/dgtkpref.glade:246 +msgid "General" +msgstr "一般" + +#: glade/dgtkpref.glade:280 +msgid "Try from:" +msgstr "測試從:" + +#: glade/dgtkpref.glade:286 +msgid "to:" +msgstr "到:" + +#: glade/dgtkpref.glade:320 +msgid "Active port:" +msgstr "啟用的埠:" + +#: glade/dgtkpref.glade:332 +msgid "0000" +msgstr "0000" + +#: glade/dgtkpref.glade:343 +msgid "Test Port" +msgstr "測試連接埠" + +#: glade/dgtkpref.glade:357 +msgid "TCP Port" +msgstr "TCP 連接埠" + +#: glade/dgtkpref.glade:380 +msgid "(-1 is unlimited)" +msgstr "(-1 表示無限制)" + +#: glade/dgtkpref.glade:390 glade/dgtkpref.glade:511 +msgid "KB/s" +msgstr "KB/秒" + +#: glade/dgtkpref.glade:436 +msgid "Maximum Download Rate:" +msgstr "最大下載頻寬:" + +#: glade/dgtkpref.glade:447 +msgid "Upload Slots" +msgstr "上傳線程" + +#: glade/dgtkpref.glade:458 +msgid "Maximum Connections" +msgstr "" + +#: glade/dgtkpref.glade:485 +msgid "Maximum Upload Rate:" +msgstr "最大上傳頻寬:" + +#: glade/dgtkpref.glade:548 +msgid "Bandwidth " +msgstr "頻寬 " + +#: glade/dgtkpref.glade:576 +msgid "Network" +msgstr "網路" + +#: glade/dgtkpref.glade:598 +msgid "gtk-cancel" +msgstr "取消" + +#: glade/dgtkpref.glade:605 +msgid "gtk-ok" +msgstr "確定" + +#: glade/dgtkpref.glade:625 +msgid "Plugin Manager" +msgstr "插件管理員" + +#: glade/dgtkpref.glade:718 +msgid "gtk-close" +msgstr "關閉" + +#: src/dcommon.py:41 src/delugegtk.py:330 +msgid "Infinity" +msgstr "無限制" + +#: src/dcommon.py:105 +msgid "Error: no webbrowser found" +msgstr "錯誤:找不到網頁瀏覽器" + +#: src/deluge.py:332 +msgid "File was not found" +msgstr "" + +#: src/delugegtk.py:173 +msgid "Show / Hide Window" +msgstr "顯示 / 隱藏視窗" + +#: src/delugegtk.py:250 +msgid "Plugin" +msgstr "插件" + +#: src/delugegtk.py:252 +msgid "Enabled" +msgstr "已啟用" + +#: src/delugegtk.py:340 +msgid "Unknown" +msgstr "未知" + +#: src/delugegtk.py:348 +msgid "Name" +msgstr "名稱" + +#: src/delugegtk.py:356 +msgid "Ratio" +msgstr "分享率" + +#: src/delugegtk.py:465 +msgid "IP Address" +msgstr "IP 位址" + +#: src/delugegtk.py:466 +msgid "Client" +msgstr "用戶端" + +#: src/delugegtk.py:467 +msgid "Percent Complete" +msgstr "已完成百分率" + +#: src/delugegtk.py:468 +msgid "Download Rate" +msgstr "下載頻寬" + +#: src/delugegtk.py:469 +msgid "Upload Rate" +msgstr "上傳頻寬" + +#: src/delugegtk.py:478 +msgid "Filename" +msgstr "檔案名稱" + +#: src/delugegtk.py:480 +msgid "Offset" +msgstr "偏移量" + +#: src/delugegtk.py:668 +msgid "" +"For some reason, the previous state could not be loaded, so a blank state " +"has been loaded for you." +msgstr "" + +#: src/delugegtk.py:670 +msgid "Would you like to attempt to reload the previous session's downloads?" +msgstr "" + +#: src/delugegtk.py:679 +msgid "Choose the download directory for" +msgstr "" + +#: src/delugegtk.py:692 src/delugegtk.py:704 +msgid "Connections" +msgstr "連接數" + +#: src/delugegtk.py:703 +msgid "Deluge Bittorrent Client" +msgstr "Deluge BT 用戶端" + +#: src/delugegtk.py:891 +msgid "Add torrent from URL" +msgstr "從超連結新增種子" + +#: src/delugegtk.py:895 +msgid "Enter the URL of the .torrent to download" +msgstr "請輸入超連結以便下載種子檔" + +#: src/delugegtk.py:959 +msgid "Warning - all downloaded files for this torrent will be deleted!" +msgstr "警告 - 此種子下載的所有檔案將被刪除!" + +#: src/dgtk.py:71 +msgid "Choose a .torrent file" +msgstr "請選取一個種子檔" + +#: src/dgtk.py:76 +msgid "Torrent files" +msgstr "種子檔" + +#: src/dgtk.py:80 +msgid "All files" +msgstr "所有檔案" + +#: src/dgtk.py:97 +msgid "Choose a download directory" +msgstr "請選取一個下載目錄" + +#~ msgid "D/L" +#~ msgstr "下載" + +#~ msgid "U/L" +#~ msgstr "上傳" + +#~ msgid "ETA" +#~ msgstr "預計完成時間" + +#~ msgid "Share %" +#~ msgstr "分享率" + +#~ msgid "path" +#~ msgstr "路徑" + +#~ msgid "size" +#~ msgstr "大小" + +#~ msgid "offset" +#~ msgstr "前置量" + +#~ msgid "download?" +#~ msgstr "確定要下載?" + +#~ msgid "IP" +#~ msgstr "IP 位址" + +#~ msgid "Are you sure you want to quit Deluge?" +#~ msgstr "您確定要結束 Deluge?" + +#~ msgid "You have " +#~ msgstr "您有 " + +#~ msgid " active torrent" +#~ msgstr " 下載中的種子" + +#~ msgid "s" +#~ msgstr "們" + +#~ msgid ". Pressing 'Yes' will close Deluge and stop all torrent activity." +#~ msgstr "。點選 '確定' 將結束 Deluge 並停止所有下載。" + +#~ msgid "Loaded torrents from previous session" +#~ msgstr "已從上個作業階段載入種子" + +#~ msgid "No torrent is selected" +#~ msgstr "尚未選擇種子" + +#~ msgid "Connections:" +#~ msgstr "連接數:" + +#~ msgid "Download:" +#~ msgstr "下載:" + +#~ msgid "Upload:" +#~ msgstr "上傳:" + +#~ msgid "Enter the URL of the .torrent file to open:" +#~ msgstr "請輸入要開啟的種子檔網址:" + +#~ msgid "Select Torrent" +#~ msgstr "請選擇種子" + +#~ msgid "infinity" +#~ msgstr "無限" + +#~ msgid "1 second" +#~ msgstr "1 秒鐘" + +#~ msgid "seconds" +#~ msgstr "秒" + +#~ msgid "1 minute" +#~ msgstr "1 分鐘" + +#~ msgid "minutes" +#~ msgstr "分" + +#~ msgid "1 hour" +#~ msgstr "1 小時" + +#~ msgid "hours" +#~ msgstr "時" + +#~ msgid "1 day" +#~ msgstr "1 日" + +#~ msgid "days" +#~ msgstr "日" + +#~ msgid "1 week" +#~ msgstr "1 週" + +#~ msgid "weeks" +#~ msgstr "週" + +#~ msgid "No plugin is selected" +#~ msgstr "尚未選擇插件" + +#~ msgid "You must activate a plugin before configuring it" +#~ msgstr "您必須先啟用該插件才能設定它" + +#~ msgid "no information is listed for this plugin" +#~ msgstr "沒有此插件的資訊" + +#~ msgid "The torrent file does not contain valid information." +#~ msgstr "種子檔資訊是無效的。" + +#~ msgid "The torrent file does not contain all necessary valid information." +#~ msgstr "種子檔資訊部份是無效的。" + +#~ msgid "A filesystem error occurred when trying to open the torrent file." +#~ msgstr "試著開啟此種子檔時發生檔案系統錯誤。" + +#~ msgid "The selected torrent is already present in the system." +#~ msgstr "所選的種子已在系統中。" + +#~ msgid "N-A" +#~ msgstr "N-A" + +#~ msgid "Yes" +#~ msgstr "確定" + +#~ msgid "No" +#~ msgstr "取消" + +#~ msgid " is updating its tracker" +#~ msgstr " 正在更新它的伺服器" + +#~ msgid "Paused" +#~ msgstr "已暫停" + +#~ msgid "Queued" +#~ msgstr "已排入佇列" + +#~ msgid "Waiting to check" +#~ msgstr "等待測試" + +#~ msgid "Checking" +#~ msgstr "正在測試" + +#~ msgid "Downloading (T?)" +#~ msgstr "正在連線 (T?)" + +#~ msgid "Downloading Meta" +#~ msgstr "下載資訊" + +#~ msgid "Downloading" +#~ msgstr "正在下載" + +#~ msgid "Finished" +#~ msgstr "已完成" + +#~ msgid "Allocating" +#~ msgstr "正在分配空間" + +#~ msgid "N/A" +#~ msgstr "N/A" + +#~ msgid "INF" +#~ msgstr "INF" + +#~ msgid "Invalid request" +#~ msgstr "無效的要求" + +#~ msgid "File error" +#~ msgstr "檔案錯誤" + +#~ msgid "Hash failed" +#~ msgstr "雜湊失敗" + +#~ msgid "Peer banned" +#~ msgstr "已列入黑名單的端點" + +#~ msgid "Fastresume rejected" +#~ msgstr "快速恢復遭駁回" + +#~ msgid "Event" +#~ msgstr "事件" + +#~ msgid "translator-credits" +#~ msgstr "如對翻譯有任何意見,請送一封電子郵件給以下地址,會盡快回覆您:Cheng-Wei Chien " + +#~ msgid "Mark 'Do Not Download'" +#~ msgstr "標記為 '不下載'" + +#~ msgid "Mark 'Download'" +#~ msgstr "標記為 '下載'" + +#~ msgid "Name:" +#~ msgstr "名稱:" + +#~ msgid "Percentage Done:" +#~ msgstr "完成率:" + +#~ msgid "Add a Torrent from File..." +#~ msgstr "從檔案新增種子 ..." + +#~ msgid "Add a Torrent from URL..." +#~ msgstr "從網址新增種子 ..." + +#~ msgid "Deluge Support _Forums" +#~ msgstr "Deluge 支援論壇 (_F)" + +#~ msgid "Force Pause/Resume Torrent" +#~ msgstr "強制 暫停/恢復 下載" + +#~ msgid "New Torrent..." +#~ msgstr "新增種子 ..." + +#~ msgid "Open the online support forums in your web browser" +#~ msgstr "在瀏覽器內開啟線上支援論壇" + +#~ msgid "Queue Torrent to Bottom" +#~ msgstr "將種子排入佇列末端" + +#~ msgid "Remove selected Torrent" +#~ msgstr "移除所選的種子" + +#~ msgid "Show/Hide the Details Pane" +#~ msgstr "顯示/隱藏詳細訊息面板" + +#~ msgid "Translate This Application..." +#~ msgstr "翻譯這個應用程式..." + +#~ msgid "Update Tracker" +#~ msgstr "更新伺服器" + +#~ msgid "_Manage Plugins" +#~ msgstr "管理插件 (_M)" + +#~ msgid "_Update Tracker" +#~ msgstr "更新伺服器 (_U)" + +#~ msgid "gtk-about" +#~ msgstr "關於" + +#~ msgid "Advanced Network Options:" +#~ msgstr "進階網路選項:" + +#~ msgid "Bandwidth (KB/s):" +#~ msgstr "頻寬 (KB/s):" + +#~ msgid "Save Location:" +#~ msgstr "儲存位置:" + +#~ msgid "Sharing:" +#~ msgstr "分享:" + +#~ msgid "Storage:" +#~ msgstr "大小:" + +#~ msgid "TCP Port:" +#~ msgstr "TCP 埠:" + +#~ msgid "Automatically stop seeding torrents when their share ratio reaches:" +#~ msgstr "當分享率達到這數值時自動停止作種:" + +#~ msgid "Bandwidth" +#~ msgstr "頻寬" + +#~ msgid "Deluge Preferences" +#~ msgstr "Deluge 偏好設定" + +#~ msgid "Downloads" +#~ msgstr "下載" + +#~ msgid "Mainline DHT" +#~ msgstr "連接 DHT (分散式雜湊表) 網絡" + +#~ msgid "Maximum number of Downloads:" +#~ msgstr "最大同時下載數:" + +#~ msgid "Maximum number of Uploads:" +#~ msgstr "最大同時上傳數:" + +#~ msgid "Maximum number of connections:" +#~ msgstr "最大容許連接數:" + +#~ msgid "Other" +#~ msgstr "其他" + +#~ msgid "Select a save location for each download" +#~ msgstr "為每個下載選擇儲存位置" + +#~ msgid "To:" +#~ msgstr "到:" + +#~ msgid "Use compact storage allocation (save space)" +#~ msgstr "使用壓縮儲存 (節省空間)" + +#~ msgid "" +#~ "Warning - any change to these settings will only come into effect after a " +#~ "restart!" +#~ msgstr "警告 - 對這些設定做的任何改變將在重新啟動後生效!" + +#~ msgid "Force Pause" +#~ msgstr "強制暫停" + +#~ msgid "Remove" +#~ msgstr "移除" + +#~ msgid "Resume" +#~ msgstr "恢復" + +#~ msgid "Add a Torrent" +#~ msgstr "新增種子" + +#~ msgid "Update Trackers" +#~ msgstr "更新伺服器" + +#~ msgid "There is suspicious activity in" +#~ msgstr "發現可疑的活動於" + +#~ msgid "The torrent may be poisoned by the RIAA/MPAA. Found" +#~ msgstr "此種子檔可能被植入 RIAA/MPAA。發現" + +#~ msgid "at prevalence" +#~ msgstr "於向外連線時" + +#~ msgid "Shutting down Deluge immediately." +#~ msgstr "正在緊急關閉 Deluge。" + +#~ msgid "Shutting down Deluge immediately (CPU overload)." +#~ msgstr "正在緊急關閉 Deluge (CPU 負載過高)。" + +#~ msgid "CPU usage warning: more than" +#~ msgstr "CPU 使用率警告:超過" + +#~ msgid "CPU usage for" +#~ msgstr "CPU 使用率於" + +#, no-c-format +#~ msgid "" +#~ "CPU % must persist for following time (seconds) before either of the above:" +#~ msgstr "上述事件啟動前,CPU 使用率必須持續顯示多少時間 (秒) :" + +#~ msgid "CPU Monitor Configuration" +#~ msgstr "CPU 監控設定" + +#, no-c-format +#~ msgid "Fatal CPU % (Deluge will be shut down):" +#~ msgstr "危險的 CPU 使用率 (Deluge 即將關閉):" + +#, no-c-format +#~ msgid "Warning CPU % (notification only):" +#~ msgstr "警告的 CPU 使用率 (僅顯示通知):" + +#~ msgid "No incoming connections" +#~ msgstr "沒有可下載的連線" + +#~ msgid "" +#~ "No incoming connections: you may be behind a firewall or router. Perhaps you " +#~ "need to forward the relevant ports." +#~ msgstr "沒有可下載的連線:您的主機可能在防火牆或路由器後面。或許您必須延伸相關連接埠。" + +#~ msgid "Health: OK" +#~ msgstr "健康度:良好" + +#~ msgid "" +#~ "PopupNotifier: not all necessary dependencies are installed. To install " +#~ "them, on Ubuntu run: apt-get python-notify notification-daemon" +#~ msgstr "" +#~ "彈出式通知:必須的相依套件未全部安裝。若要安裝,於 Ubuntu 上執行:sudo apt-get install python-notify " +#~ "notification-daemon" + +#~ msgid "" +#~ "PopupNotifier: Cannot initialize pynotify, no notifications will be shown." +#~ msgstr "彈出式通知:無法初始化 pynotify,將不會顯示任何通知。" + +#~ msgid "Informative - can be easily ignored" +#~ msgstr "通知訊息 - 忽略無妨" + +#~ msgid "Warning - may be of interest" +#~ msgstr "警告訊息 - 需要注意" + +#~ msgid "Critical - should never be ignored" +#~ msgstr "緊急訊息 - 不可忽略" + +#~ msgid "Fatal - normal operation will probably not continue" +#~ msgstr "危險訊息 - 可能無法繼續正常運作" + +#~ msgid "" +#~ "Notifications of the following Severity Level and above will be displayed:" +#~ msgstr "危急程度高於此級的通知將會顯示:" + +#~ msgid "Popup Notifier Configuration" +#~ msgstr "彈出式通知設定" + +#~ msgid "Popup Notification" +#~ msgstr "彈出式通知" + +#~ msgid "Retrieving blocklist from server" +#~ msgstr "從伺服器取得黑名單" + +#~ msgid "Applying SafeDeluge blocklist" +#~ msgstr "套用安全模式 Deluge 的黑名單" + +#~ msgid "SafeDeluge Configuration" +#~ msgstr "安全模式 Deluge 設定" + +#~ msgid "Use custom blocklist URL:" +#~ msgstr "使用自訂的黑名單網址:" + +#~ msgid "Use default blocklist URL" +#~ msgstr "使用預設的黑名單網址" + +#~ msgid "Search string" +#~ msgstr "搜尋字串" + +#, no-c-format +#~ msgid "" +#~ "Add a new search engine by entering a Name and a URL. For Name, enter the " +#~ "name of the search engine to be used. For URL, enter the url of the seach " +#~ "page. The user's search query will be added to the end of the url, or " +#~ "replace any instances of %%query%% in the URL.\n" +#~ "For example, a Google search would be:\n" +#~ "Name: Google\n" +#~ "URL: http://www.google.com/search?hl=en" +#~ msgstr "" +#~ "輸入一組名稱和網址即可新增搜尋引擎。前者為搜尋引擎的名稱。後者則是搜尋頁面的網址。使用者的搜尋字串會被加到網址的末端,或是取代其中的 %%query%% " +#~ "字樣。\n" +#~ "例如:新增 Google 搜尋將會是:\n" +#~ "名稱:Google\n" +#~ "網址:http://www.google.com/search?hl=en" + +#~ msgid "Help" +#~ msgstr "說明" + +#~ msgid "Manage Search Plugins" +#~ msgstr "管理搜尋插件" + +#~ msgid "Name:" +#~ msgstr "名稱:" + +#~ msgid "URL:" +#~ msgstr "網址:" + +#~ msgid "gtk-add" +#~ msgstr "新增" + +#~ msgid "gtk-remove" +#~ msgstr "移除" + +#~ msgid "Connections: " +#~ msgstr "連接數: " + +#~ msgid "Download Speed:" +#~ msgstr "下載速度:" + +#~ msgid "Upload Speed:" +#~ msgstr "上傳速度:" \ No newline at end of file diff --git a/encryption/scripts/deluge b/encryption/scripts/deluge new file mode 100755 index 000000000..3e54196c5 --- /dev/null +++ b/encryption/scripts/deluge @@ -0,0 +1,94 @@ +#!/usr/bin/env python +# +# deluge +# Copyright (C) Zach Tibbitts 2006 +# Copyright (C) Alon Zakai 2006 +# +# +# +# Deluge is free software. +# +# You may 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. +# +# deluge 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 deluge. If not, write to: +# The Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor +# Boston, MA 02110-1301, USA. + +from optparse import OptionParser +import sys +import os, os.path +import gettext, locale +import deluge, deluge.common, deluge.interface +try: + import dbus + dbus_version = getattr(dbus, 'version', (0,0,0)) + if dbus_version >= (0,41,0) and dbus_version < (0,80,0): + import dbus.glib + elif dbus_version >= (0,80,0): + from dbus.mainloop.glib import DBusGMainLoop + DBusGMainLoop(set_as_default=True) + else: + pass +except: dbus_imported = False +else: dbus_imported = True + + +parser = OptionParser(usage="%prog [options] [actions]", version=deluge.common.PROGRAM_VERSION) +parser.add_option("--tray", dest="tray", help="start Deluge hidden in system tray", + metavar="TRAY", action="store_true") + +(options, args) = parser.parse_args() + + +if dbus_imported: + bus = dbus.SessionBus() + + dbus_objects = dbus.Interface(bus.get_object('org.freedesktop.DBus', '/org/freedesktop/DBus'), 'org.freedesktop.DBus').ListNames() + + interface = None + + if not "org.deluge_torrent.Deluge" in dbus_objects: + print "no existing Deluge session" + + interface = deluge.interface.DelugeGTK() + for arg in args: + apath = os.path.abspath(arg) + if apath.endswith(".torrent"): + interface.external_add_torrent(apath) + else: + print "Error,", arg, " does not seem to be a .torrent file" + interface.start(hidden=options.tray) + else: + ## This connects to the deluge interface + print "create proxy object" + proxy = bus.get_object('org.deluge_torrent.Deluge', '/org/deluge_torrent/DelugeObject') + print "create iface" + deluge_iface = dbus.Interface(proxy, 'org.deluge_torrent.Deluge') + print "send to iface" + for arg in args: + apath = os.path.abspath(arg) + if apath.endswith(".torrent"): + deluge_iface.external_add_torrent(apath) + else: + print "Error,", arg, " does not seem to be a .torrent file" +else: + print "no existing Deluge session" + + interface = deluge.interface.DelugeGTK() + for arg in args: + apath = os.path.abspath(arg) + if apath.endswith(".torrent"): + interface.external_add_torrent(apath) + else: + print "Error,", arg, " does not seem to be a .torrent file" + interface.start(hidden=options.tray) diff --git a/encryption/setup.py b/encryption/setup.py new file mode 100644 index 000000000..bdcdfcb63 --- /dev/null +++ b/encryption/setup.py @@ -0,0 +1,311 @@ +# Copyright (c) 2006 Zach Tibbitts ('zachtib') +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, write to: +# The Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor +# Boston, MA 02110-1301, USA. + +import os, platform +print "Attempting to detect your system information" +if platform.machine() == "i386" or platform.machine() == "i686": + print "32bit x86 system detected" + ARCH = "x86" +elif platform.machine() == "x86_64" or platform.machine() == "amd64": + print "64bit x86_64 system detected" + ARCH = "x64" +elif platform.processor() == "powerpc": + print "PowerPC system detected" + ARCH = "ppc" +else: + print "Couldn't detect CPU architecture" + ARCH = "" +if platform.system() == "Linux": + print "Linux operating system detected" + OS = "linux" +elif platform.system() == "Darwin" : + print "Darwin / OS X system detected" + OS = "osx" +elif platform.system() == "Windows": + print "Windows system detected" + OS = "win" +elif os.name == "posix": + print "Unix system detected" + OS = "nix" +else: + print "Couldn't detect operating system" + OS = "" +import os.path, glob +from distutils.core import setup, Extension +from distutils import sysconfig +import shutil +from distutils import cmd +from distutils.command.install import install as _install +from distutils.command.install_data import install_data as _install_data +from distutils.command.build import build as _build +import msgfmt + +python_version = platform.python_version()[0:3] + +NAME = "deluge" +FULLNAME = "Deluge BitTorrent Client" +VERSION = "0.5.0.90.1" +AUTHOR = "Zach Tibbitts, Alon Zakai, Marcos Pinto" +EMAIL = "zach@collegegeek.org, kripkensteiner@gmail.com, marcospinto@dipconsultants.com" +DESCRIPTION = "A bittorrent client written in PyGTK" +URL = "http://deluge-torrent.org" +LICENSE = "GPLv2" + +EXTRA_COMPILE_ARGS = ["-Wno-missing-braces"] +if ARCH == "x64": + EXTRA_COMPILE_ARGS.append("-DAMD64") + + +# NOTE: The following "hack" removes the -g and -Wstrict-prototypes +# build options from the command that will compile the C++ module, +# deluge_core. While we understand that you aren't generally +# encouraged to do this, we have done so for the following reasons: +# 1) The -g compiler option produces debugging information about +# the compiled module. However, this option increases the +# size of deluge_core.so from ~1.9MB to 13.6MB and slows down +# the program's execution without offering any benefits +# whatsoever. +# 2) -Wstrict-prototypes is not a valid C++ build option, and the +# compiler will throw a number of warnings at compile time. +# While this does not really impact anything, it makes it +# seem as if something is going wrong with the compile, and +# it has been removed to prevent confusion. + +if not OS == "win": + + if os.WEXITSTATUS(os.system('grep -q "Debian GNU/Linux 4.0\|Ubuntu 7.04\|Ubuntu 6.06\|Fedora Core release 6" /etc/issue')) == 0: + boosttype = 'nomt' + else: + boosttype = 'mt' + removals = ['-g', '-DNDEBUG', '-O2', '-Wstrict-prototypes'] + additions = ['-DNDEBUG', '-O2'] + + if python_version == '2.5': + cv_opt = sysconfig.get_config_vars()["CFLAGS"] + for removal in removals: + cv_opt = cv_opt.replace(removal, " ") + for addition in additions: + cv_opt = cv_opt + " " + addition + sysconfig.get_config_vars()["CFLAGS"] = ' '.join(cv_opt.split()) + else: + cv_opt = sysconfig.get_config_vars()["OPT"] + for removal in removals: + cv_opt = cv_opt.replace(removal, " ") + for addition in additions: + cv_opt = cv_opt + " " + addition + sysconfig.get_config_vars()["OPT"] = ' '.join(cv_opt.split()) +else: + librariestype = 'mt' + +# NOTE: The Rasterbar Libtorrent source code is in the libtorrent/ directory +# inside of Deluge's source tarball. On several occasions, it has been +# pointed out to us that we should build against the system's installed +# libtorrent rather than our internal copy, and a few people even submitted +# patches to do just that. However, as of now, this version +# of libtorrent is not available in Debian, and as a result, Ubuntu. Once +# libtorrent-rasterbar is available in the repositories of these distributions, +# we will probably begin to build against a system libtorrent, but at the +# moment, we are including the source code to make packaging on Debian and +# Ubuntu possible. +if boosttype == "nomt": + librariestype = ['boost_filesystem', 'boost_date_time', + 'boost_thread', 'z', 'pthread'] + print 'Libraries nomt' +elif boosttype == "mt": + librariestype = ['boost_filesystem-mt', 'boost_date_time-mt', + 'boost_thread-mt', 'z', 'pthread'] + print 'Libraries mt' + +deluge_core = Extension('deluge_core', + include_dirs = ['./libtorrent', './libtorrent/include', + './libtorrent/include/libtorrent', + '/usr/include/python' + python_version], + libraries = librariestype, + extra_compile_args = EXTRA_COMPILE_ARGS, + sources = ['src/deluge_core.cpp', + 'libtorrent/src/alert.cpp', + 'libtorrent/src/allocate_resources.cpp', + 'libtorrent/src/bandwidth_manager.cpp', + 'libtorrent/src/bt_peer_connection.cpp', + 'libtorrent/src/connection_queue.cpp', + 'libtorrent/src/entry.cpp', + 'libtorrent/src/escape_string.cpp', + 'libtorrent/src/file.cpp', + 'libtorrent/src/file_pool.cpp', + 'libtorrent/src/http_connection.cpp', + 'libtorrent/src/http_stream.cpp', + 'libtorrent/src/http_tracker_connection.cpp', + 'libtorrent/src/identify_client.cpp', + 'libtorrent/src/instantiate_connection.cpp', + 'libtorrent/src/ip_filter.cpp', + 'libtorrent/src/logger.cpp', + 'libtorrent/src/lsd.cpp', + 'libtorrent/src/metadata_transfer.cpp', + 'libtorrent/src/natpmp.cpp', + 'libtorrent/src/pe_crypto.cpp', + 'libtorrent/src/peer_connection.cpp', + 'libtorrent/src/piece_picker.cpp', + 'libtorrent/src/policy.cpp', + 'libtorrent/src/session.cpp', + 'libtorrent/src/session_impl.cpp', + 'libtorrent/src/sha1.cpp', + 'libtorrent/src/socks5_stream.cpp', + 'libtorrent/src/stat.cpp', + 'libtorrent/src/storage.cpp', + 'libtorrent/src/torrent.cpp', + 'libtorrent/src/torrent_handle.cpp', + 'libtorrent/src/torrent_info.cpp', + 'libtorrent/src/tracker_manager.cpp', + 'libtorrent/src/udp_tracker_connection.cpp', + 'libtorrent/src/upnp.cpp', + 'libtorrent/src/ut_pex.cpp', + 'libtorrent/src/web_peer_connection.cpp', + 'libtorrent/src/kademlia/closest_nodes.cpp', + 'libtorrent/src/kademlia/dht_tracker.cpp', + 'libtorrent/src/kademlia/find_data.cpp', + 'libtorrent/src/kademlia/node.cpp', + 'libtorrent/src/kademlia/node_id.cpp', + 'libtorrent/src/kademlia/refresh.cpp', + 'libtorrent/src/kademlia/routing_table.cpp', + 'libtorrent/src/kademlia/rpc_manager.cpp', + 'libtorrent/src/kademlia/traversal_algorithm.cpp']) + +# Thanks to Iain Nicol for code to save the location for installed prefix +# At runtime, we need to know where we installed the data to. + +class write_data_install_path(cmd.Command): + description = 'saves the data installation path for access at runtime' + + def initialize_options(self): + self.prefix = None + self.lib_build_dir = None + + def finalize_options(self): + self.set_undefined_options('install', + ('prefix', 'prefix') + ) + self.set_undefined_options('build', + ('build_lib', 'lib_build_dir') + ) + + def run(self): + conf_filename = os.path.join(self.lib_build_dir, + 'deluge', 'common.py') + + conf_file = open(conf_filename, 'r') + data = conf_file.read() + conf_file.close() + data = data.replace('@datadir@', self.prefix) + + conf_file = open(conf_filename, 'w') + conf_file.write(data) + conf_file.close() + +class unwrite_data_install_path(cmd.Command): + description = 'undoes write_data_install_path' + + def initialize_options(self): + self.lib_build_dir = None + + def finalize_options(self): + self.set_undefined_options('build', + ('build_lib', 'lib_build_dir') + ) + + def run(self): + dest = os.path.join(self.lib_build_dir, + 'deluge', 'common.py') + shutil.copyfile('src/common.py', dest) + +class build_trans(cmd.Command): + description = 'Compile .po files into .mo files' + + def initialize_options(self): + pass + + def finalize_options(self): + pass + + def run(self): + po_dir = os.path.join(os.path.dirname(__file__), 'po') + for path, names, filenames in os.walk(po_dir): + for f in filenames: + if f.endswith('.po'): + lang = f[:len(f) - 3] + src = os.path.join(path, f) + dest_path = os.path.join('build', 'locale', lang, 'LC_MESSAGES') + dest = os.path.join(dest_path, 'deluge.mo') + if not os.path.exists(dest_path): + os.makedirs(dest_path) + if not os.path.exists(dest): + print 'Compiling %s' % src + msgfmt.make(src, dest) + else: + src_mtime = os.stat(src)[8] + dest_mtime = os.stat(dest)[8] + if src_mtime > dest_mtime: + print 'Compiling %s' % src + msgfmt.make(src, dest) + +class build(_build): + sub_commands = _build.sub_commands + [('build_trans', None)] + def run(self): + _build.run(self) + +class install(_install): + sub_commands = [('write_data_install_path', None)] + \ + _install.sub_commands + [('unwrite_data_install_path', None)] + def run(self): + _install.run(self) + +class install_data(_install_data): + def run(self): + for lang in os.listdir('build/locale/'): + lang_dir = os.path.join('share', 'locale', lang, 'LC_MESSAGES') + lang_file = os.path.join('build', 'locale', lang, 'LC_MESSAGES', 'deluge.mo') + self.data_files.append( (lang_dir, [lang_file]) ) + _install_data.run(self) + +cmdclass = { + 'build': build, + 'install': install, + 'build_trans': build_trans, + 'install_data': install_data, + 'write_data_install_path': write_data_install_path, + 'unwrite_data_install_path': unwrite_data_install_path, +} + +data = [('share/deluge/glade', glob.glob('glade/*.glade')), + ('share/deluge/pixmaps', glob.glob('pixmaps/*.png')), + ('share/applications' , ['deluge.desktop']), + ('share/pixmaps' , ['deluge.xpm'])] + +for plugin in glob.glob('plugins/*'): + data.append( ('share/deluge/' + plugin, glob.glob(plugin + '/*')) ) + +setup(name=NAME, fullname=FULLNAME, version=VERSION, + author=AUTHOR, author_email=EMAIL, description=DESCRIPTION, + url=URL, license=LICENSE, + scripts=["scripts/deluge"], + packages=['deluge'], + package_dir = {'deluge': 'src'}, + data_files=data, + ext_package='deluge', + ext_modules=[deluge_core], + cmdclass=cmdclass +) diff --git a/encryption/src/__init__.py b/encryption/src/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/encryption/src/common.py b/encryption/src/common.py new file mode 100644 index 000000000..70833cd7e --- /dev/null +++ b/encryption/src/common.py @@ -0,0 +1,108 @@ +# dcommon.py +# +# Copyright (C) Zach Tibbitts 2006 +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, write to: +# The Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor +# Boston, MA 02110-1301, USA. + +import sys, os, os.path, webbrowser +import xdg, xdg.BaseDirectory + +import gettext + +PROGRAM_NAME = "Deluge" +PROGRAM_VERSION = "0.5.1" + +CLIENT_CODE = "DE" +CLIENT_VERSION = "0510" + +CONFIG_DIR = xdg.BaseDirectory.save_config_path('deluge') + +# the necessary substitutions are made at installation time +INSTALL_PREFIX = '@datadir@' +GLADE_DIR = os.path.join(INSTALL_PREFIX, 'share', 'deluge', 'glade') +PIXMAP_DIR = os.path.join(INSTALL_PREFIX, 'share', 'deluge', 'pixmaps') +PLUGIN_DIR = os.path.join(INSTALL_PREFIX, 'share', 'deluge', 'plugins') + +def estimate_eta(state): + try: + return ftime(get_eta(state["total_size"], state["total_done"], state["download_rate"])) + except ZeroDivisionError: + return _("Infinity") + +def get_eta(size, done, rate): + return long( (size - done) / rate ) + +# Returns formatted string describing filesize +# fsize_b should be in bytes +# Returned value will be in either KB, MB, or GB +def fsize(fsize_b): + fsize_kb = float (fsize_b / 1024.0) + if fsize_kb < 1000: + return '%.1f KB'%fsize_kb + fsize_mb = float (fsize_kb / 1024.0) + if fsize_mb < 1000: + return '%.1f MB'%fsize_mb + fsize_gb = float (fsize_mb / 1024.0) + return '%.1f GB'%fsize_gb + +# Returns a formatted string representing a percentage +def fpcnt(dec): + return '%.2f%%'%(dec * 100) + +# Returns a formatted string representing transfer rate +def frate(bps): + return '%s/s'%(fsize(bps)) + +def fseed(state): + return str(str(state['num_seeds']) + " (" + str(state['total_seeds']) + ")") + +def fpeer(state): + return str(str(state['num_peers']) + " (" + str(state['total_peers']) + ")") + +def ftime(seconds): + if seconds < 60: + return '%ds'%(seconds) + minutes = int(seconds/60) + seconds = seconds % 60 + if minutes < 60: + return '%dm %ds'%(minutes, seconds) + hours = int(minutes/60) + minutes = minutes % 60 + if hours < 24: + return '%dh %dm'%(hours, minutes) + days = int(hours/24) + hours = hours % 24 + if days < 7: + return '%dd %dh'%(days, hours) + weeks = int(days/7) + days = days % 7 + if weeks < 10: + return '%dw %dd'%(weeks, days) + return 'unknown' + + +def get_glade_file(fname): + return os.path.join(GLADE_DIR, fname) + +def get_pixmap(fname): + return os.path.join(PIXMAP_DIR, fname) + +def open_url_in_browser(dialog, link): + try: + webbrowser.open(link) + except webbrowser.Error: + print _("Error: no webbrowser found") diff --git a/encryption/src/core.py b/encryption/src/core.py new file mode 100644 index 000000000..abc3a170f --- /dev/null +++ b/encryption/src/core.py @@ -0,0 +1,755 @@ +# +# Copyright (C) 2006 Alon Zakai ('Kripken') +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, write to: +# The Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor +# Boston, MA 02110-1301, USA. + +# Deluge Library, previously known as python-libtorrent: +# +# Deluge is a Python library for torrenting, that includes +# Deluge, which is Python code, and Deluge_core, which is also a Python +# module, but written in C++, and includes the libtorrent torrent library. Only +# Deluge should be visible, and only it should be imported, in the client. +# Deluge_core contains mainly libtorrent-interfacing code, and a few other things +# that make most sense to write at that level. Deluge contains all other +# torrent-system management: queueing, configuration management, persistent +# list of torrents, etc. +# + +# Documentation: +# Torrents have 3 structures: +# 1. torrent_info - persistent data, like name, upload speed cap, etc. +# 2. core_torrent_state - transient state data from the core. This may take +# time to calculate, so we do if efficiently +# 3. supp_torrent_state - supplementary torrent data, from Deluge + +import deluge_core +import os, os.path, shutil, statvfs +import pickle +import time +import gettext + +# Constants + +TORRENTS_SUBDIR = "torrentfiles" + +STATE_FILENAME = "persistent.state" +PREFS_FILENAME = "prefs.state" +DHT_FILENAME = "dht.state" + +CACHED_DATA_EXPIRATION = 1 # seconds, like the output of time.time() + +# "max_half_open" : -1, +DEFAULT_PREFS = { + "max_uploads" : 2, # a.k.a. upload slots + "listen_on" : [6881,9999], + "max_connections" : 80, + "use_DHT" : True, + "max_active_torrents" : -1, + "auto_seed_ratio" : -1, + "max_download_rate" : -1, + "max_upload_rate" : -1 + } + +PREF_FUNCTIONS = { + "max_uploads" : deluge_core.set_max_uploads, + "listen_on" : deluge_core.set_listen_on, + "max_connections" : deluge_core.set_max_connections, + "use_DHT" : None, # not a normal pref in that is is applied only on start + "max_active_torrents" : None, # no need for a function, applied constantly + "auto_seed_ratio" : None, # no need for a function, applied constantly + "max_download_rate" : deluge_core.set_download_rate_limit, + "max_upload_rate" : deluge_core.set_upload_rate_limit + } +STATE_MESSAGES = ( "Queued", + "Checking", + "Connecting", + "Downloading Metadata", + "Downloading", + "Finished", + "Seeding", + "Allocating" + ) + + +# Exceptions + +class DelugeError(Exception): + def __init__(self, value): + self.value = value + def __str__(self): + return repr(self.value) + +class InvalidEncodingError(DelugeError): + pass + +class FilesystemError(DelugeError): + pass + +# Note: this may be raised both from deluge-core.cpp and deluge.py, for +# different reasons, both related to duplicate torrents +class DuplicateTorrentError(DelugeError): + pass + +class InvalidTorrentError(DelugeError): + pass + +class InvalidUniqueIDError(DelugeError): + pass + +class InsufficientFreeSpaceError(DelugeError): + def __init__(self, free_space, needed_space): + self.free_space = free_space + self.needed_space = needed_space + def __str__(self): + return "%d %d bytes needed"%(self.free_space, self.needed_space) + +# A cached data item + +class cached_data: + def __init__(self, get_method, key): + self.get_method = get_method + self.key = key + + self.timestamp = -1 + + def get(self, efficiently=True): + if self.timestamp == -1 or time.time() > self.timestamp + CACHED_DATA_EXPIRATION or \ + not efficiently: + self.data = self.get_method(self.key) + self.timestamp = time.time() + + return self.data + + +# Persistent information for a single torrent + +class torrent_info: + def __init__(self, filename, save_dir, compact): + self.filename = filename + self.save_dir = save_dir + self.compact = compact + + self.user_paused = False # start out unpaused + self.uploaded_memory = 0 + + self.delete_me = False # set this to true, to delete it on next sync + + +# The persistent state of the torrent system. Everything in this will be pickled + +class persistent_state: + def __init__(self): + # Torrents + self.torrents = [] + + # Prepare queue (queue is pickled, just like everything else) + self.queue = [] # queue[x] is the unique_ID of the x-th queue position. Simple. + + +# The manager for the torrent system + +class Manager: + # blank_slate mode ignores the two pickle files and DHT state file, i.e. you start + # completely fresh. When quitting, the old files will be overwritten + def __init__(self, client_ID, version, user_agent, base_dir, blank_slate=False): + self.base_dir = base_dir + + # Ensure directories exist + if not TORRENTS_SUBDIR in os.listdir(self.base_dir): + os.mkdir(os.path.join(self.base_dir, TORRENTS_SUBDIR)) + + # Pre-initialize the core's data structures + deluge_core.pre_init(DelugeError, + InvalidEncodingError, + FilesystemError, + DuplicateTorrentError, + InvalidTorrentError) + + # Start up the core + assert(len(version) == 4) + deluge_core.init(client_ID, + int(version[0]), + int(version[1]), + int(version[2]), + int(version[3]), + user_agent) + + self.constants = deluge_core.constants() + + # Unique IDs are NOT in the state, since they are temporary for each session + self.unique_IDs = {} # unique_ID -> a torrent_info object, i.e. persistent data + + # Saved torrent core_states. We do not poll the core in a costly manner, necessarily + self.saved_core_torrent_states = {} # unique_ID -> torrent_state + + # supplementary torrent states + self.supp_torrent_states = {} # unique_ID->dict of data + + # Saved torrent core_states. We do not poll the core in a costly manner, necessarily + self.saved_core_torrent_peer_infos = {} # unique_ID -> torrent_state + + # Saved torrent core_states. We do not poll the core in a costly manner, necessarily + self.saved_core_torrent_file_infos = {} # unique_ID -> torrent_state + + + # Unpickle the preferences, or create a new one + self.prefs = DEFAULT_PREFS + if not blank_slate: + try: + pkl_file = open(os.path.join(self.base_dir, PREFS_FILENAME), 'rb') + self.prefs = pickle.load(pkl_file) + pkl_file.close() + except IOError: + pass + + # Apply preferences. Note that this is before any torrents are added + self.apply_prefs() + + # Apply DHT, if needed. Note that this is before any torrents are added + if self.get_pref('use_DHT'): + if not blank_slate: + deluge_core.start_DHT(os.path.join(self.base_dir, DHT_FILENAME)) + else: + deluge_core.start_DHT("") + + # Unpickle the state, or create a new one + if not blank_slate: + try: + pkl_file = open(os.path.join(self.base_dir, STATE_FILENAME), 'rb') + self.state = pickle.load(pkl_file) + pkl_file.close() + + # Sync with the core: tell core about torrents, and get unique_IDs + self.sync() + + # Apply all the file filters, right after adding the torrents + self.apply_all_file_filters() + + # Apply the queue at this time, after all is loaded and ready + self.apply_queue() + except IOError: + self.state = persistent_state() + else: + self.state = persistent_state() + + def quit(self): + # Analyze data needed for pickling, etc. + self.pre_quitting() + + # Pickle the prefs + print "Pickling prefs..." + output = open(os.path.join(self.base_dir, PREFS_FILENAME), 'wb') + pickle.dump(self.prefs, output) + output.close() + + # Pickle the state + print "Pickling state..." + output = open(os.path.join(self.base_dir, STATE_FILENAME), 'wb') + pickle.dump(self.state, output) + output.close() + + # Save fastresume data + print "Saving fastresume data..." + self.save_fastresume_data() + + # Stop DHT, if needed + if self.get_pref('use_DHT'): + print "Stopping DHT..." + deluge_core.stop_DHT(os.path.join(self.base_dir, DHT_FILENAME)) + + # Shutdown torrent core + print "Quitting the core..." + deluge_core.quit() + + def pre_quitting(self): + # Save the uploaded data from this session to the existing upload memory + for unique_ID in self.unique_IDs.keys(): + self.unique_IDs[unique_ID].uploaded_memory = \ + self.unique_IDs[unique_ID].uploaded_memory + \ + self.get_core_torrent_state(unique_ID, False)['total_upload'] # Purposefully ineffi. + + # Preference management functions + + def get_pref(self, key): + # If we have a value, return, else fallback on default_prefs, else raise an error + # the fallback is useful if the source has newer prefs than the existing pref state, + # which was created by an old version of the source + if key in self.prefs.keys(): + return self.prefs[key] + elif key in DEFAULT_PREFS: + self.prefs[key] = DEFAULT_PREFS[key] + return self.prefs[key] + else: + raise DelugeError("Asked for a pref that doesn't exist: " + key) + + def set_pref(self, key, value): + # Make sure this is a valid key + if key not in DEFAULT_PREFS.keys(): + raise DelugeError("Asked to change a pref that isn't valid: " + key) + + self.prefs[key] = value + + # Apply the pref, if applicable + if PREF_FUNCTIONS[key] is not None: + PREF_FUNCTIONS[key](value) + + # Torrent addition and removal functions + + def add_torrent(self, filename, save_dir, compact): + self.add_torrent_ns(filename, save_dir, compact) + return self.sync() # Syncing will create a new torrent in the core, and return it's ID + + def remove_torrent(self, unique_ID, data_also): + # Save some data before we remove the torrent, needed later in this func + temp = self.unique_IDs[unique_ID] + temp_fileinfo = deluge_core.get_file_info(unique_ID) + + self.remove_torrent_ns(unique_ID) + self.sync() + + # Remove data, if asked to do so + if data_also: + # Must be done AFTER the torrent is removed + # Note: can this be to the trash? + for filedata in temp_fileinfo: + filename = filedata['path'] + try: + os.remove(os.path.join(temp.save_dir, filename)) + except OSError: + pass # No file just means it wasn't downloaded, we can continue + + # A function to try and reload a torrent from a previous session. This is + # used in the event that Deluge crashes and a blank state is loaded. + def add_old_torrent(self, filename, save_dir, compact): + if not filename in os.listdir(os.path.join(self.base_dir, TORRENTS_SUBDIR)): + raise InvalidTorrentError(_("File was not found") + ": " + filename) + + full_new_name = os.path.join(self.base_dir, TORRENTS_SUBDIR, filename) + + # Create torrent object + new_torrent = torrent_info(full_new_name, save_dir, compact) + self.state.torrents.append(new_torrent) + + return self.sync() + + # A separate function, because people may want to call it from time to time + def save_fastresume_data(self): + for unique_ID in self.unique_IDs: + deluge_core.save_fastresume(unique_ID, self.unique_IDs[unique_ID].filename) + + # Load all NEW torrents in a directory. The GUI can call this every minute or so, + # if one wants a directory to be 'watched' (personally, I think it should only be + # done on user command).os.path.join( + def autoload_directory(self, directory, save_dir, compact): + for filename in os.listdir(directory): + if filename[-len(".torrent"):].lower() == ".torrent": + try: + self.add_torrent_ns(self, filename, save_dir, compact) + except DuplicateTorrentError: + pass + + self.sync() + + # State retrieval functions + + def get_state(self): + ret = deluge_core.get_session_info() + + # Get additional data from our level + ret['is_listening'] = deluge_core.is_listening() + ret['port'] = deluge_core.listening_port() + if self.get_pref('use_DHT'): + ret['DHT_nodes'] = deluge_core.get_DHT_info() + + return ret + + # This is the EXTERNAL function, for the GUI. It returns the core_state + supp_state + def get_torrent_state(self, unique_ID): + # Check to see if unique_ID exists: + if self.state.queue.count(unique_ID) == 0: + raise InvalidUniqueIDError("Asked for a torrent that doesn't exist") + + ret = self.get_core_torrent_state(unique_ID, True).copy() + + # Add the deluge-level things to the deluge_core data + if self.get_supp_torrent_state(unique_ID) is not None: + ret.update(self.get_supp_torrent_state(unique_ID)) + + # Get queue position + ret['queue_pos'] = self.state.queue.index(unique_ID) + + return ret + + def get_torrent_peer_info(self, unique_ID): + # Perhaps at some time we may add info here + return self.get_core_torrent_peer_info(unique_ID) + + def get_torrent_file_info(self, unique_ID): + return self.get_core_torrent_file_info(unique_ID) + + # Queueing functions + + def queue_up(self, unique_ID): + curr_index = self.get_queue_index(unique_ID) + if curr_index > 0: + temp = self.state.queue[curr_index - 1] + self.state.queue[curr_index - 1] = unique_ID + self.state.queue[curr_index] = temp + + def queue_down(self, unique_ID): + curr_index = self.get_queue_index(unique_ID) + if curr_index < (len(self.state.queue) - 1): + temp = self.state.queue[curr_index + 1] + self.state.queue[curr_index + 1] = unique_ID + self.state.queue[curr_index] = temp + + def queue_bottom(self, unique_ID): + curr_index = self.get_queue_index(unique_ID) + if curr_index < (len(self.state.queue) - 1): + self.state.queue.remove(curr_index) + self.state.queue.append(unique_ID) + + def clear_completed(self): + for unique_ID in self.unique_IDs: + torrent_state = self.get_core_torrent_state(unique_ID) + if torrent_state['progress'] == 1.0: + self.remove_torrent_ns(unique_ID) + + self.sync() + + # Enforce the queue: pause/unpause as needed, based on queue and user_pausing + # This should be called after changes to relevant parameters (user_pausing, or + # altering max_active_torrents), or just from time to time + # ___ALL queuing code should be in this function, and ONLY here___ + def apply_queue(self, efficient = True): + # Handle autoseeding - downqueue as needed + if self.get_pref('auto_seed_ratio') != -1: + for unique_ID in self.unique_IDs: + if self.get_core_torrent_state(unique_ID, efficient)['is_seed']: + torrent_state = self.get_core_torrent_state(unique_ID, efficient) + ratio = self.calc_ratio(unique_ID, torrent_state) + if ratio >= self.get_pref('auto_seed_ratio'): + self.queue_bottom(unique_ID) + + + # Pause and resume torrents + for index in range(len(self.state.queue)): + unique_ID = self.state.queue[index] + if (index < self.get_pref('max_active_torrents') or self.get_pref('max_active_torrents') == -1) \ + and self.get_core_torrent_state(unique_ID, efficient)['is_paused'] \ + and not self.is_user_paused(unique_ID): + deluge_core.resume(unique_ID) + elif (not self.get_core_torrent_state(unique_ID, efficient)['is_paused']) and \ + ( (index >= self.get_pref('max_active_torrents') and \ + self.get_pref('max_active_torrents') != -1 ) or \ + self.is_user_paused(unique_ID)): + deluge_core.pause(unique_ID) + + # Event handling + + def handle_events(self): + # Handle them for the backend's purposes, but still send them up in case the client + # wants to do something - show messages, for example + ret = [] + + event = deluge_core.pop_event() + + while event is not None: +# print "EVENT: ", event + + ret.append(event) + + if event['event_type'] is self.constants['EVENT_FINISHED']: + # If we are autoseeding, then we need to apply the queue + if self.get_pref('auto_seed_ratio') == -1: + self.apply_queue(efficient = False) # To work on current data + elif event['event_type'] is self.constants['EVENT_TRACKER']: + unique_ID = event['unique_ID'] + status = event['tracker_status'] + message = event['message'] + tracker = message[message.find('"')+1:message.rfind('"')] + + self.set_supp_torrent_state_val(unique_ID, + "tracker_status", + (tracker, status)) + + old_state = self.get_supp_torrent_state(unique_ID) + try: + new = old_state['tracker_messages'] + except KeyError: + new = {} + + new[tracker] = message + + self.set_supp_torrent_state_val(unique_ID, + "tracker_messages", + new) + + event = deluge_core.pop_event() + + return ret + + # Filtering functions + + def set_file_filter(self, unique_ID, file_filter): + assert(len(file_filter) == self.get_core_torrent_state(unique_ID, True)['num_files']) + + self.unique_IDs[unique_ID].file_filter = file_filter[:] + + deluge_core.set_filter_out(unique_ID, file_filter) + + def get_file_filter(self, unique_ID): + try: + return self.unique_IDs[unique_ID].file_filter[:] + except AttributeError: + return None + + # Called when a session starts, to apply existing filters + def apply_all_file_filters(self): + for unique_ID in self.unique_IDs.keys(): + try: + self.set_file_filter(unique_ID, self.unique_IDs[unique_ID].file_filter) + except AttributeError: + pass + + # Advanced statistics - these may be SLOW. The client should call these only + # when needed, and perhaps only once in a long while (they are mostly just + # approximations anyhow + + def calc_availability(self, unique_ID): + return deluge_stats.calc_availability(self.get_core_torrent_peer_info(unique_ID)) + + def calc_swarm_speed(self, unique_ID): + pieces_per_sec = deluge_stats.calc_swarm_speed(self.get_core_torrent_peer_info(unique_ID)) + piece_length = self.get_core_torrent_state(unique_ID, efficiently=True) + + return pieces_per_sec * piece_length + + # Miscellaneous minor functions + + def set_user_pause(self, unique_ID, new_value): + self.unique_IDs[unique_ID].user_paused = new_value + self.apply_queue() + + def is_user_paused(self, unique_ID): + return self.unique_IDs[unique_ID].user_paused + + def get_num_torrents(self): + return deluge_core.get_num_torrents() + + def get_unique_IDs(self): + return self.unique_IDs.keys() + + def update_tracker(self, unique_ID): + deluge_core.reannounce(unique_ID) + + + #################### + # Internal functions + #################### + + # Efficient: use a saved state, if it hasn't expired yet + def get_core_torrent_state(self, unique_ID, efficiently=True): + if unique_ID not in self.saved_core_torrent_states.keys(): + self.saved_core_torrent_states[unique_ID] = cached_data(deluge_core.get_torrent_state, + unique_ID) + + return self.saved_core_torrent_states[unique_ID].get(efficiently) + + def get_supp_torrent_state(self, unique_ID): + try: + return self.supp_torrent_states[unique_ID] + except KeyError: + return None + + def set_supp_torrent_state_val(self, unique_ID, key, val): + try: + if self.supp_torrent_states[unique_ID] is None: + self.supp_torrent_states[unique_ID] = {} + except KeyError: + self.supp_torrent_states[unique_ID] = {} + + self.supp_torrent_states[unique_ID][key] = val + + def get_core_torrent_peer_info(self, unique_ID, efficiently=True): + if unique_ID not in self.saved_core_torrent_peer_infos.keys(): + self.saved_core_torrent_peer_infos[unique_ID] = cached_data(deluge_core.get_peer_info, unique_ID) + + return self.saved_core_torrent_peer_infos[unique_ID].get(efficiently) + + def get_core_torrent_file_info(self, unique_ID, efficiently=True): + if unique_ID not in self.saved_core_torrent_file_infos.keys(): + self.saved_core_torrent_file_infos[unique_ID] = cached_data(deluge_core.get_file_info, unique_ID) + + return self.saved_core_torrent_file_infos[unique_ID].get(efficiently) + + # Functions for checking if enough space is available + + def calc_free_space(self, directory): + dir_stats = os.statvfs(directory) + block_size = dir_stats[statvfs.F_BSIZE] + avail_blocks = dir_stats[statvfs.F_BAVAIL] + return long(block_size * avail_blocks) + + # Non-syncing functions. Used when we loop over such events, and sync manually at the end + + def add_torrent_ns(self, filename, save_dir, compact): + # Cache torrent file + (temp, filename_short) = os.path.split(filename) + + # if filename_short in os.listdir(self.base_dir + "/" + TORRENTS_SUBDIR): + # raise DuplicateTorrentError("Duplicate Torrent, it appears: " + filename_short) + + full_new_name = os.path.join(self.base_dir, TORRENTS_SUBDIR, filename_short) + + shutil.copy(filename, full_new_name) + + # Create torrent object + new_torrent = torrent_info(full_new_name, save_dir, compact) + self.state.torrents.append(new_torrent) + + def remove_torrent_ns(self, unique_ID): + self.unique_IDs[unique_ID].delete_me = True + + + # Sync the state.torrents and unique_IDs lists with the core + # ___ALL syncing code with the core is here, and ONLY here___ + # Also all self-syncing is done here (various lists) + + ## + ## I had to make some changes here to get things to work properly + ## Some of these changes may be hack-ish, so look at them and make + ## sure nothing is wrong. + ## + def sync(self): + ret = None # We return new added unique ID(s), or None + no_space = False + + # Add torrents to core and unique_IDs + torrents_with_unique_ID = self.unique_IDs.values() + + for torrent in self.state.torrents: + if not os.path.exists(torrent.filename): + print "Missing file: %s" % torrent.filename + self.state.torrents.remove(torrent) + continue + if torrent not in torrents_with_unique_ID: +# print "Adding torrent to core:", torrent.filename, torrent.save_dir, torrent.compact + try: + unique_ID = deluge_core.add_torrent(torrent.filename, + torrent.save_dir, + torrent.compact) + except DelugeError, e: + print "Error:", e + self.state.torrents.remove(torrent) + raise e +# print "Got unique ID:", unique_ID + # Now to check and see if there is enough free space for the download + size = deluge_core.get_torrent_state(unique_ID)["total_size"] + avail = self.calc_free_space(torrent.save_dir) + print "Torrent Size", size +# print "Available Space", avail +# size = avail + 1 #debug! + if size > avail: # Not enough free space + torrent.user_paused = True + no_space = True + deluge_core.remove_torrent(unique_ID) #Remove the torrent + self.state.torrents.remove(torrent) + os.remove(torrent.filename) + raise InsufficientFreeSpaceError(avail, size) + ret = unique_ID + self.unique_IDs[unique_ID] = torrent + + +# print torrents_with_unique_ID + # Remove torrents from core, unique_IDs and queue + to_delete = [] + for unique_ID in self.unique_IDs.keys(): +# print torrent + if self.unique_IDs[unique_ID].delete_me: + deluge_core.remove_torrent(unique_ID) + to_delete.append(unique_ID) + + for unique_ID in to_delete: + self.state.torrents.remove(self.unique_IDs[unique_ID]) + self.state.queue.remove(unique_ID) + # Remove .torrent and .fastresume + os.remove(self.unique_IDs[unique_ID].filename) + try: + # Must be after removal of the torrent, because that saves a new .fastresume + os.remove(self.unique_IDs[unique_ID].filename + ".fastresume") + except OSError: + pass # Perhaps there never was one to begin with + del self.unique_IDs[unique_ID] + + # Add torrents to queue - at the end, of course + for unique_ID in self.unique_IDs.keys(): + if unique_ID not in self.state.queue: + self.state.queue.append(unique_ID) + # run through queue, remove those that no longer exists + to_delete = [] + for queue_item in self.state.queue: + print "queue_item", queue_item + if queue_item not in self.unique_IDs.keys(): + to_delete.append(queue_item) + for del_item in to_delete: + self.state.queue.remove(del_item) + + assert(len(self.unique_IDs) == len(self.state.torrents)) + + print "Debug 1", self.unique_IDs, self.state.queue + print "Debug 2", len(self.unique_IDs), len(self.state.queue) + + assert(len(self.unique_IDs) == len(self.state.queue)) + assert(len(self.unique_IDs) == deluge_core.get_num_torrents()) + + if no_space: + self.apply_queue() + + return ret + + def get_queue_index(self, unique_ID): + return self.state.queue.index(unique_ID) + + def apply_prefs(self): + print "Applying preferences" + assert(len(PREF_FUNCTIONS) == len(DEFAULT_PREFS)) + + for pref in PREF_FUNCTIONS.keys(): + if PREF_FUNCTIONS[pref] is not None: + PREF_FUNCTIONS[pref](self.get_pref(pref)) + + # Calculations + + def calc_ratio(self, unique_ID, torrent_state): + up = float(torrent_state['total_upload'] + self.unique_IDs[unique_ID].uploaded_memory) + down = float(torrent_state["total_done"]) + + try: + ret = float(up/down) + except: + ret = -1 + + return ret + + + def create_torrent(self, filename, source_directory, trackers, comments=None, + pieces=256, author="Deluge"): + return deluge_core.create_torrent(filename, source_directory, trackers, comments, pieces, author) + + def pe_settings(self, out_enc_policy, in_enc_policy, allowed_enc_level, prefer_rc4): + return deluge_core.pe_settings(out_enc_policy, in_enc_policy, allowed_enc_level, prefer_rc4) diff --git a/encryption/src/deluge_core.cpp b/encryption/src/deluge_core.cpp new file mode 100644 index 000000000..ff1cd7390 --- /dev/null +++ b/encryption/src/deluge_core.cpp @@ -0,0 +1,1281 @@ +/* + * Copyright 2006 Alon Zakai ('Kripken') + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Thank You: Some code portions were derived from BSD-licensed work by + * Arvid Norberg, and GPL-licensed work by Christophe Dumez + */ + +//------------------ +// TODO: +// +// The DHT capability requires UDP. We need to check that this port is in fact +// open, just like the normal TCP port for bittorrent. +// +//----------------- +#include + +#include +#include +#include + +#include "libtorrent/entry.hpp" +#include "libtorrent/bencode.hpp" +#include "libtorrent/session.hpp" +#include "libtorrent/identify_client.hpp" +#include "libtorrent/alert_types.hpp" +#include "libtorrent/storage.hpp" +#include "libtorrent/hasher.hpp" +#include "libtorrent/ip_filter.hpp" +#include "libtorrent/upnp.hpp" +#include "libtorrent/file_pool.hpp" +#include "libtorrent/natpmp.hpp" +#include "libtorrent/extensions/metadata_transfer.hpp" +#include "libtorrent/extensions/ut_pex.hpp" + +extern "C" +{ +#include "openssl/sha.h" +#include "openssl/bn.h" +} +using namespace boost::filesystem; +using namespace libtorrent; + +//---------------- +// CONSTANTS +//---------------- + +#ifdef AMD64 +#define python_long int +#else +#define python_long long +#endif + +#define EVENT_NULL 0 +#define EVENT_FINISHED 1 +#define EVENT_PEER_ERROR 2 +#define EVENT_INVALID_REQUEST 3 +#define EVENT_FILE_ERROR 4 +#define EVENT_HASH_FAILED_ERROR 5 +#define EVENT_PEER_BAN_ERROR 6 +#define EVENT_FASTRESUME_REJECTED_ERROR 8 +#define EVENT_TRACKER 9 +#define EVENT_OTHER 10 + +#define STATE_QUEUED 0 +#define STATE_CHECKING 1 +#define STATE_CONNECTING 2 +#define STATE_DOWNLOADING_META 3 +#define STATE_DOWNLOADING 4 +#define STATE_FINISHED 5 +#define STATE_SEEDING 6 +#define STATE_ALLOCATING 7 + +#define DHT_ROUTER_PORT 6881 + +//----------------- +// TYPES +//----------------- + +typedef long unique_ID_t; +typedef std::vector filter_out_t; +typedef std::string torrent_name_t; + +struct torrent_t +{ + torrent_handle handle; + unique_ID_t unique_ID; +}; + +typedef std::vector torrents_t; +typedef torrents_t::iterator torrents_t_iterator; + +//--------------------------- +// MODULE-GLOBAL VARIABLES +//--------------------------- + +long M_unique_counter = 0; +session_settings *M_settings = NULL; +session *M_ses = NULL; +PyObject *M_constants = NULL; +ip_filter *M_the_filter = NULL; +torrents_t *M_torrents = NULL; + +//------------------------ +// Exception types & macro +//------------------------ + +static PyObject *DelugeError = NULL; +static PyObject *InvalidEncodingError = NULL; +static PyObject *FilesystemError = NULL; +static PyObject *DuplicateTorrentError = NULL; +static PyObject *InvalidTorrentError = NULL; + +#define RAISE_PTR(e,s) { printf("Raising error: %s\r\n", s); PyErr_SetString(e, s); return NULL; } +#define RAISE_INT(e,s) { printf("Raising error: %s\r\n", s); PyErr_SetString(e, s); return -1; } + +//--------------------- +// Internal functions +//--------------------- + +bool empty_name_check(const std::string & name) +{ + return 1; +} + + +long handle_exists(torrent_handle &handle) +{ + for (unsigned long i = 0; i < M_torrents->size(); i++) + if ((*M_torrents)[i].handle == handle) + return 1; + + return 0; +} + + +long get_torrent_index(torrent_handle &handle) +{ + for (unsigned long i = 0; i < M_torrents->size(); i++) + if ((*M_torrents)[i].handle == handle) + { + // printf("Found: %li\r\n", i); + return i; + } + + RAISE_INT(DelugeError, "Handle not found."); +} + + +long get_index_from_unique_ID(long unique_ID) +{ + assert(M_handles->size() == M_unique_IDs->size()); + + for (unsigned long i = 0; i < M_torrents->size(); i++) + if ((*M_torrents)[i].unique_ID == unique_ID) + return i; + + RAISE_INT(DelugeError, "No such unique_ID."); +} + + +long internal_add_torrent(std::string const& torrent_name, +float preferred_ratio, +bool compact_mode, +boost::filesystem::path const& save_path) +{ + + std::ifstream in(torrent_name.c_str(), std::ios_base::binary); + in.unsetf(std::ios_base::skipws); + entry e; + e = bdecode(std::istream_iterator(in), std::istream_iterator()); + + torrent_info t(e); + + entry resume_data; + try + { + std::stringstream s; + s << torrent_name << ".fastresume"; + boost::filesystem::ifstream resumeFile(s.str(), std::ios_base::binary); + resumeFile.unsetf(std::ios_base::skipws); + resume_data = bdecode(std::istream_iterator(resumeFile), + std::istream_iterator()); + } + catch (invalid_encoding&) + { + } + catch (boost::filesystem::filesystem_error&) {} + + // Create new torrent object + + torrent_t new_torrent; + + torrent_handle h = M_ses->add_torrent(t, save_path, resume_data, compact_mode, 16 * 1024); + + // h.set_max_connections(60); // at some point we should use this + h.set_max_uploads(-1); + h.set_ratio(preferred_ratio); + new_torrent.handle = h; + + new_torrent.unique_ID = M_unique_counter; + M_unique_counter++; + + M_torrents->push_back(new_torrent); + + return (new_torrent.unique_ID); +} + + +void internal_remove_torrent(long index) +{ + assert(index < M_torrents->size()); + + torrent_handle& h = M_torrents->at(index).handle; + + M_ses->remove_torrent(h); + + torrents_t_iterator it = M_torrents->begin() + index; + M_torrents->erase(it); +} + + +long get_peer_index(tcp::endpoint addr, std::vector const& peers) +{ + long index = -1; + + for (unsigned long i = 0; i < peers.size(); i++) + if (peers[i].ip == addr) + index = i; + + return index; +} + + +// The following function contains code by Christophe Dumez and Arvid Norberg +void internal_add_files(torrent_info& t, +boost::filesystem::path const& p, +boost::filesystem::path const& l) +{ + // change default checker, perhaps? + boost::filesystem::path f(p / l); + if (is_directory(f)) + { + for (boost::filesystem::directory_iterator i(f), end; i != end; ++i) + internal_add_files(t, p, l / i->leaf()); + } else + t.add_file(l, file_size(f)); +} + + +long count_DHT_peers(entry &state) +{ + long num_peers = 0; + entry *nodes = state.find_key("nodes"); + if (nodes) + { + entry::list_type &peers = nodes->list(); + entry::list_type::const_iterator i; + i = peers.begin(); + + while (i != peers.end()) + { + num_peers++; + i++; + } + } + + return num_peers; +} + + +//===================== +// External functions +//===================== + +static PyObject *torrent_pre_init(PyObject *self, PyObject *args) +{ + if (!PyArg_ParseTuple(args, "OOOOO", &DelugeError, + &InvalidEncodingError, + &FilesystemError, + &DuplicateTorrentError, + &InvalidTorrentError)) + return NULL; + + Py_INCREF(Py_None); return Py_None; +} + + +static PyObject *torrent_init(PyObject *self, PyObject *args) +{ + printf("deluge_core; using libtorrent %s. Compiled with NDEBUG value: %d\r\n", + LIBTORRENT_VERSION, + NDEBUG); + + // Tell Boost that we are on *NIX, so bloody '.'s are ok inside a directory name! + boost::filesystem::path::default_name_check(empty_name_check); + + char *client_ID, *user_agent; + python_long v1,v2,v3,v4; + + if (!PyArg_ParseTuple(args, "siiiis", &client_ID, &v1, &v2, &v3, &v4, &user_agent)) + return NULL; + + M_settings = new session_settings; + M_ses = new session(fingerprint(client_ID, v1, v2, v3, v4)); + + M_torrents = new torrents_t; + M_torrents->reserve(10); // pretty cheap, just 10 + + // Init values + + M_settings->user_agent = std::string(user_agent); + + M_ses->set_max_half_open_connections(-1); + M_ses->set_download_rate_limit(-1); + M_ses->set_upload_rate_limit(-1); + + M_ses->set_settings(*M_settings); + M_ses->set_severity_level(alert::debug); + + M_ses->add_extension(&libtorrent::create_metadata_plugin); + M_ses->add_extension(&libtorrent::create_ut_pex_plugin); + + M_constants = Py_BuildValue("{s:i,s:i,s:i,s:i,s:i,s:i,s:i,s:i,s:i,s:i,s:i,s:i,s:i,s:i,s:i,s:i,s:i,s:i}", + "EVENT_NULL", EVENT_NULL, + "EVENT_FINISHED", EVENT_FINISHED, + "EVENT_PEER_ERROR", EVENT_PEER_ERROR, + "EVENT_INVALID_REQUEST", EVENT_INVALID_REQUEST, + "EVENT_FILE_ERROR", EVENT_FILE_ERROR, + "EVENT_HASH_FAILED_ERROR", EVENT_HASH_FAILED_ERROR, + "EVENT_PEER_BAN_ERROR", EVENT_PEER_BAN_ERROR, + "EVENT_FASTRESUME_REJECTED_ERROR", EVENT_FASTRESUME_REJECTED_ERROR, + "EVENT_TRACKER", EVENT_TRACKER, + "EVENT_OTHER", EVENT_OTHER, + "STATE_QUEUED", STATE_QUEUED, + "STATE_CHECKING", STATE_CHECKING, + "STATE_CONNECTING", STATE_CONNECTING, + "STATE_DOWNLOADING_META", STATE_DOWNLOADING_META, + "STATE_DOWNLOADING", STATE_DOWNLOADING, + "STATE_FINISHED", STATE_FINISHED, + "STATE_SEEDING", STATE_SEEDING, + "STATE_ALLOCATING", STATE_ALLOCATING); + + Py_INCREF(Py_None); return Py_None; +}; + +static PyObject *torrent_quit(PyObject *self, PyObject *args) +{ + printf("core: shutting down session...\r\n"); + M_settings->stop_tracker_timeout = 5; + M_ses->set_settings(*M_settings); + delete M_ses; // 100% CPU... + printf("core: removing settings...\r\n"); + delete M_settings; + printf("core: removing torrents...\r\n"); + delete M_torrents; + + Py_DECREF(M_constants); + + printf("core shut down.\r\n"); + + Py_INCREF(Py_None); return Py_None; +}; + +static PyObject *torrent_save_fastresume(PyObject *self, PyObject *args) +{ + python_long unique_ID; + const char *torrent_name; + if (!PyArg_ParseTuple(args, "is", &unique_ID, &torrent_name)) + return NULL; + + long index = get_index_from_unique_ID(unique_ID); + if (PyErr_Occurred()) + return NULL; + + torrent_handle& h = M_torrents->at(index).handle; + // For valid torrents, save fastresume data + if (h.is_valid() && h.has_metadata()) + { + h.pause(); + + entry data = h.write_resume_data(); + + std::stringstream s; + s << torrent_name << ".fastresume"; + + boost::filesystem::ofstream out(s.str(), std::ios_base::binary); + + out.unsetf(std::ios_base::skipws); + + bencode(std::ostream_iterator(out), data); + + h.resume(); + + Py_INCREF(Py_None); return Py_None; + } else + RAISE_PTR(DelugeError, "Invalid handle or no metadata for fastresume."); +} + + +static PyObject *torrent_set_max_half_open(PyObject *self, PyObject *args) +{ + python_long arg; + if (!PyArg_ParseTuple(args, "i", &arg)) + return NULL; + + M_ses->set_max_half_open_connections(arg); + + Py_INCREF(Py_None); return Py_None; +} + + +static PyObject *torrent_set_download_rate_limit(PyObject *self, PyObject *args) +{ + python_long arg; + if (!PyArg_ParseTuple(args, "i", &arg)) + return NULL; + + // printf("Capping download to %d bytes per second\r\n", (int)arg); + M_ses->set_download_rate_limit(arg); + + Py_INCREF(Py_None); return Py_None; +} + + +static PyObject *torrent_set_upload_rate_limit(PyObject *self, PyObject *args) +{ + python_long arg; + if (!PyArg_ParseTuple(args, "i", &arg)) + return NULL; + + // printf("Capping upload to %d bytes per second\r\n", (int)arg); + M_ses->set_upload_rate_limit(arg); + + Py_INCREF(Py_None); return Py_None; +} + + +static PyObject *torrent_set_listen_on(PyObject *self, PyObject *args) +{ + PyObject *port_vec; + if (!PyArg_ParseTuple(args, "O", &port_vec)) + return NULL; + + M_ses->listen_on(std::make_pair( PyInt_AsLong(PyList_GetItem(port_vec, 0)), + PyInt_AsLong(PyList_GetItem(port_vec, 1))), ""); + + Py_INCREF(Py_None); return Py_None; +} + + +static PyObject *torrent_is_listening(PyObject *self, PyObject *args) +{ + long ret = (M_ses->is_listening() != 0); + + return Py_BuildValue("i", ret); +} + + +static PyObject *torrent_listening_port(PyObject *self, PyObject *args) +{ + return Py_BuildValue("i", (python_long)M_ses->listen_port()); +} + + +static PyObject *torrent_set_max_uploads(PyObject *self, PyObject *args) +{ + python_long max_up; + if (!PyArg_ParseTuple(args, "i", &max_up)) + return NULL; + + M_ses->set_max_uploads(max_up); + + Py_INCREF(Py_None); return Py_None; +} + + +static PyObject *torrent_set_max_connections(PyObject *self, PyObject *args) +{ + python_long max_conn; + if (!PyArg_ParseTuple(args, "i", &max_conn)) + return NULL; + + // printf("Setting max connections: %d\r\n", max_conn); + M_ses->set_max_connections(max_conn); + + Py_INCREF(Py_None); return Py_None; +} + + +static PyObject *torrent_add_torrent(PyObject *self, PyObject *args) +{ + const char *name, *save_dir; + python_long compact; + if (!PyArg_ParseTuple(args, "ssi", &name, &save_dir, &compact)) + return NULL; + + boost::filesystem::path save_dir_2 (save_dir, empty_name_check); + + try + { + long ret = internal_add_torrent(name, 0, compact, save_dir_2); + if (PyErr_Occurred()) + return NULL; + else + return Py_BuildValue("i", ret); + } + catch (invalid_encoding&) + { RAISE_PTR(InvalidEncodingError, ""); } + catch (invalid_torrent_file&) + { RAISE_PTR(InvalidTorrentError, ""); } + catch (boost::filesystem::filesystem_error&) + { RAISE_PTR(FilesystemError, ""); } + catch (duplicate_torrent&) + { RAISE_PTR(DuplicateTorrentError, "libtorrent reports this is a duplicate torrent"); } +} + + +static PyObject *torrent_remove_torrent(PyObject *self, PyObject *args) +{ + python_long unique_ID; + if (!PyArg_ParseTuple(args, "i", &unique_ID)) + return NULL; + + long index = get_index_from_unique_ID(unique_ID); + if (PyErr_Occurred()) + return NULL; + + internal_remove_torrent(index); + + Py_INCREF(Py_None); return Py_None; +} + + +static PyObject *torrent_get_num_torrents(PyObject *self, PyObject *args) +{ + return Py_BuildValue("i", M_torrents->size()); +} + + +static PyObject *torrent_reannounce(PyObject *self, PyObject *args) +{ + python_long unique_ID; + if (!PyArg_ParseTuple(args, "i", &unique_ID)) + return NULL; + + long index = get_index_from_unique_ID(unique_ID); + if (PyErr_Occurred()) + return NULL; + + M_torrents->at(index).handle.force_reannounce(); + + Py_INCREF(Py_None); return Py_None; +} + + +static PyObject *torrent_pause(PyObject *self, PyObject *args) +{ + python_long unique_ID; + if (!PyArg_ParseTuple(args, "i", &unique_ID)) + return NULL; + + long index = get_index_from_unique_ID(unique_ID); + if (PyErr_Occurred()) + return NULL; + + M_torrents->at(index).handle.pause(); + + Py_INCREF(Py_None); return Py_None; +} + + +static PyObject *torrent_resume(PyObject *self, PyObject *args) +{ + python_long unique_ID; + if (!PyArg_ParseTuple(args, "i", &unique_ID)) + return NULL; + + long index = get_index_from_unique_ID(unique_ID); + if (PyErr_Occurred()) + return NULL; + + M_torrents->at(index).handle.resume(); + + Py_INCREF(Py_None); return Py_None; +} + + +static PyObject *torrent_get_torrent_state(PyObject *self, PyObject *args) +{ + python_long unique_ID; + if (!PyArg_ParseTuple(args, "i", &unique_ID)) + return NULL; + + long index = get_index_from_unique_ID(unique_ID); + if (PyErr_Occurred()) + return NULL; + + torrent_t &t = M_torrents->at(index); + torrent_status s = t.handle.status(); + const torrent_info &i = t.handle.get_torrent_info(); + + std::vector peers; + t.handle.get_peer_info(peers); + + long total_seeds = 0; + long total_peers = 0; + + for (unsigned long i = 0; i < peers.size(); i++) + if (peers[i].seed) + total_seeds++; + else + total_peers++; + + return Py_BuildValue("{s:s,s:l,s:l,s:l,s:l,s:f,s:f,s:d,s:f,s:l,s:l,s:s,s:s,s:f,s:d,s:l,s:l,s:l,s:d,s:l,s:l,s:l,s:l,s:l,s:l,s:d,s:d,s:l,s:l}", + "name", t.handle.get_torrent_info().name().c_str(), + "num_files", t.handle.get_torrent_info().num_files(), + "state", s.state, + "num_peers", s.num_peers, + "num_seeds", s.num_seeds, + "distributed_copies", s.distributed_copies, + "download_rate", s.download_rate, + "total_download", double(s.total_download), + "upload_rate", s.upload_rate, + "total_upload", long(s.total_upload), + "tracker_ok", !s.current_tracker.empty(), + "next_announce", boost::posix_time::to_simple_string(s.next_announce).c_str(), + "tracker", s.current_tracker.c_str(), + "progress", float(s.progress), + "total_done", double(s.total_done), + "pieces", long(s.pieces), + "pieces_done", long(s.num_pieces), + "block_size", long(s.block_size), + "total_size", double(i.total_size()), + "piece_length", long(i.piece_length()), + "num_pieces", long(i.num_pieces()), + "total_seeds", total_seeds, + "total_peers", total_peers, + "is_paused", long(t.handle.is_paused()), + "is_seed", long(t.handle.is_seed()), + "total_wanted", double(s.total_wanted), + "total_wanted_done", double(s.total_wanted_done), + "num_complete", long(s.num_complete), + "num_incomplete", long(s.num_incomplete)); +}; + +static PyObject *torrent_pop_event(PyObject *self, PyObject *args) +{ + std::auto_ptr a; + + a = M_ses->pop_alert(); + + alert *popped_alert = a.get(); + + if (!popped_alert) + { + Py_INCREF(Py_None); return Py_None; + } else if (dynamic_cast(popped_alert)) + { + torrent_handle handle = (dynamic_cast(popped_alert))->handle; + + long index = get_torrent_index(handle); + if (PyErr_Occurred()) + return NULL; + + if (handle_exists(handle)) + return Py_BuildValue("{s:i,s:i}", "event_type", EVENT_FINISHED, + "unique_ID", + M_torrents->at(index).unique_ID); + else + { Py_INCREF(Py_None); return Py_None; } + } else if (dynamic_cast(popped_alert)) + { + peer_id peer_ID = (dynamic_cast(popped_alert))->pid; + std::string peer_IP = + (dynamic_cast(popped_alert))->ip.address().to_string(); + + return Py_BuildValue("{s:i,s:s,s:s,s:s}", "event_type", EVENT_PEER_ERROR, + "client_ID", identify_client(peer_ID).c_str(), + "ip", peer_IP.c_str(), + "message", a->msg().c_str()); + } else if (dynamic_cast(popped_alert)) + { + peer_id peer_ID = (dynamic_cast(popped_alert))->pid; + + return Py_BuildValue("{s:i,s:s,s:s}", + "event_type", EVENT_INVALID_REQUEST, + "client_ID", identify_client(peer_ID).c_str(), + "message", a->msg().c_str()); + } else if (dynamic_cast(popped_alert)) + { + torrent_handle handle = (dynamic_cast(popped_alert))->handle; + long index = get_torrent_index(handle); + if (PyErr_Occurred()) + return NULL; + + if (handle_exists(handle)) + return Py_BuildValue("{s:i,s:i,s:s}", + "event_type", EVENT_FILE_ERROR, + "unique_ID", M_torrents->at(index).unique_ID, + "message", a->msg().c_str()); + else + { Py_INCREF(Py_None); return Py_None; } + } else if (dynamic_cast(popped_alert)) + { + torrent_handle handle = (dynamic_cast(popped_alert))->handle; + long index = get_torrent_index(handle); + if (PyErr_Occurred()) + return NULL; + + if (handle_exists(handle)) + return Py_BuildValue("{s:i,s:i,s:i,s:s}", + "event_type", EVENT_HASH_FAILED_ERROR, + "unique_ID", M_torrents->at(index).unique_ID, + "piece_index", + long((dynamic_cast(popped_alert))->piece_index), + "message", a->msg().c_str()); + else + { Py_INCREF(Py_None); return Py_None; } + } else if (dynamic_cast(popped_alert)) + { + torrent_handle handle = (dynamic_cast(popped_alert))->handle; + long index = get_torrent_index(handle); + if (PyErr_Occurred()) + return NULL; + std::string peer_IP = (dynamic_cast(popped_alert))->ip.address().to_string(); + + if (handle_exists(handle)) + return Py_BuildValue("{s:i,s:i,s:s,s:s}", + "event_type", EVENT_PEER_BAN_ERROR, + "unique_ID", M_torrents->at(index).unique_ID, + "ip", peer_IP.c_str(), + "message", a->msg().c_str()); + else + { Py_INCREF(Py_None); return Py_None; } + } else if (dynamic_cast(popped_alert)) + { + torrent_handle handle = (dynamic_cast(popped_alert))->handle; + long index = get_torrent_index(handle); + if (PyErr_Occurred()) + return NULL; + + if (handle_exists(handle)) + return Py_BuildValue("{s:i,s:i,s:s}", + "event_type", EVENT_FASTRESUME_REJECTED_ERROR, + "unique_ID", M_torrents->at(index).unique_ID, + "message", a->msg().c_str()); + else + { Py_INCREF(Py_None); return Py_None; } + } else if (dynamic_cast(popped_alert)) + { + torrent_handle handle = (dynamic_cast(popped_alert))->handle; + long index = get_torrent_index(handle); + if (PyErr_Occurred()) + return NULL; + + if (handle_exists(handle)) + return Py_BuildValue("{s:i,s:i,s:s,s:s}", + "event_type", EVENT_TRACKER, + "unique_ID", + M_torrents->at(index).unique_ID, + "tracker_status", "Announce sent", + "message", a->msg().c_str()); + else + { Py_INCREF(Py_None); return Py_None; } + } else if (dynamic_cast(popped_alert)) + { + torrent_handle handle = (dynamic_cast(popped_alert))->handle; + long index = get_torrent_index(handle); + if (PyErr_Occurred()) + return NULL; + + if (handle_exists(handle)) + return Py_BuildValue("{s:i,s:i,s:s,s:s}", + "event_type", EVENT_TRACKER, + "unique_ID", + M_torrents->at(index).unique_ID, + "tracker_status", "Bad response (status code=?)", + "message", a->msg().c_str()); + else + { Py_INCREF(Py_None); return Py_None; } + } else if (dynamic_cast(popped_alert)) + { + torrent_handle handle = (dynamic_cast(popped_alert))->handle; + long index = get_torrent_index(handle); + if (PyErr_Occurred()) + return NULL; + + if (handle_exists(handle)) + return Py_BuildValue("{s:i,s:i,s:s,s:s}", + "event_type", EVENT_TRACKER, + "unique_ID", + M_torrents->at(index).unique_ID, + "tracker_status", "Announce succeeded", + "message", a->msg().c_str()); + else + { Py_INCREF(Py_None); return Py_None; } + } else if (dynamic_cast(popped_alert)) + { + torrent_handle handle = (dynamic_cast(popped_alert))->handle; + long index = get_torrent_index(handle); + if (PyErr_Occurred()) + return NULL; + + if (handle_exists(handle)) + return Py_BuildValue("{s:i,s:i,s:s,s:s}", + "event_type", EVENT_TRACKER, + "unique_ID", + M_torrents->at(index).unique_ID, + "tracker_status", "Warning in response", + "message", a->msg().c_str()); + else + { Py_INCREF(Py_None); return Py_None; } + } + + return Py_BuildValue("{s:i,s:s}", "event_type", EVENT_OTHER, + "message", a->msg().c_str() ); +} + + +static PyObject *torrent_get_session_info(PyObject *self, PyObject *args) +{ + session_status s = M_ses->status(); + + return Py_BuildValue("{s:l,s:f,s:f,s:f,s:f,s:l}", + "has_incoming_connections", long(s.has_incoming_connections), + "upload_rate", float(s.upload_rate), + "download_rate", float(s.download_rate), + "payload_upload_rate", float(s.payload_upload_rate), + "payload_download_rate", float(s.payload_download_rate), + "num_peers", long(s.num_peers)); +} + + +static PyObject *torrent_get_peer_info(PyObject *self, PyObject *args) +{ + python_long unique_ID; + if (!PyArg_ParseTuple(args, "i", &unique_ID)) + return NULL; + + long index = get_index_from_unique_ID(unique_ID); + if (PyErr_Occurred()) + return NULL; + + std::vector peers; + M_torrents->at(index).handle.get_peer_info(peers); + + PyObject *peer_info; + PyObject *ret = PyTuple_New(peers.size()); + PyObject *curr_piece, *py_pieces; + + for (unsigned long i = 0; i < peers.size(); i++) + { + std::vector &pieces = peers[i].pieces; + unsigned long pieces_had = 0; + + py_pieces = PyTuple_New(pieces.size()); + + for (unsigned long piece = 0; piece < pieces.size(); piece++) + { + if (pieces[piece]) + pieces_had++; + + curr_piece = Py_BuildValue("i", long(pieces[piece])); + PyTuple_SetItem(py_pieces, piece, curr_piece); + } + + peer_info = Py_BuildValue( + "{s:f,s:d,s:f,s:d,s:i,s:i,s:i,s:i,s:i,s:i,s:i,s:i,s:i,s:i,s:i,s:s,s:i,s:s,s:f,s:O}", + "download_speed", float(peers[i].down_speed), + "total_download", double(peers[i].total_download), + "upload_speed", float(peers[i].up_speed), + "total_upload", double(peers[i].total_upload), + "download_queue_length", long(peers[i].download_queue_length), + "upload_queue_length", long(peers[i].upload_queue_length), + "is_interesting", long((peers[i].flags & peer_info::interesting) != 0), + "is_choked", long((peers[i].flags & peer_info::choked) != 0), + "is_remote_interested", long((peers[i].flags & peer_info::remote_interested) != 0), + "is_remote_choked", long((peers[i].flags & peer_info::remote_choked) != 0), + "supports_extensions", long((peers[i].flags & peer_info::supports_extensions)!= 0), + "is_local_connection", long((peers[i].flags & peer_info::local_connection) != 0), + "is_awaiting_handshake", long((peers[i].flags & peer_info::handshake) != 0), + "is_connecting", long((peers[i].flags & peer_info::connecting) != 0), + "is_queued", long((peers[i].flags & peer_info::queued) != 0), + "client", peers[i].client.c_str(), + "is_seed", long(peers[i].seed), + "ip", peers[i].ip.address().to_string().c_str(), + "peer_has", float(float(pieces_had)*100.0/pieces.size()), + "pieces", py_pieces + ); + + Py_DECREF(py_pieces); // Assuming the previous line does NOT steal the ref, then this is + // needed! + + PyTuple_SetItem(ret, i, peer_info); + }; + + return ret; +}; + +static PyObject *torrent_get_file_info(PyObject *self, PyObject *args) +{ + python_long unique_ID; + if (!PyArg_ParseTuple(args, "i", &unique_ID)) + return NULL; + + long index = get_index_from_unique_ID(unique_ID); + if (PyErr_Occurred()) + return NULL; + + std::vector temp_files; + + PyObject *file_info; + + std::vector progresses; + + torrent_t &t = M_torrents->at(index); + t.handle.file_progress(progresses); + + torrent_info::file_iterator start = + t.handle.get_torrent_info().begin_files(); + torrent_info::file_iterator end = + t.handle.get_torrent_info().end_files(); + + long fileIndex = 0; + + for(torrent_info::file_iterator i = start; i != end; ++i) + { + file_entry const &currFile = (*i); + + file_info = Py_BuildValue( + "{s:s,s:d,s:d,s:f}", + "path", currFile.path.string().c_str(), + "offset", double(currFile.offset), + "size", double(currFile.size), + "progress", progresses[i - start]*100.0 + ); + + fileIndex++; + + temp_files.push_back(file_info); + }; + + PyObject *ret = PyTuple_New(temp_files.size()); + + for (unsigned long i = 0; i < temp_files.size(); i++) + PyTuple_SetItem(ret, i, temp_files[i]); + + return ret; +}; + +static PyObject *torrent_set_filter_out(PyObject *self, PyObject *args) +{ + python_long unique_ID; + PyObject *filter_out_object; + if (!PyArg_ParseTuple(args, "iO", &unique_ID, &filter_out_object)) + return NULL; + + long index = get_index_from_unique_ID(unique_ID); + if (PyErr_Occurred()) + return NULL; + + torrent_t &t = M_torrents->at(index); + long num_files = t.handle.get_torrent_info().num_files(); + assert(PyList_Size(filter_out_object) == num_files); + + filter_out_t filter_out(num_files); + + for (long i = 0; i < num_files; i++) + { + filter_out.at(i) = + PyInt_AsLong(PyList_GetItem(filter_out_object, i)); + }; + + t.handle.filter_files(filter_out); + + Py_INCREF(Py_None); return Py_None; +} + + +/*static PyObject *torrent_get_unique_IDs(PyObject *self, PyObject *args) +{ + PyObject *ret = PyTuple_New(M_torrents.size()); + PyObject *temp; + + for (unsigned long i = 0; i < M_torrents.size(); i++) + { + temp = Py_BuildValue("i", M_torrents->at(i).unique_ID) + + PyTuple_SetItem(ret, i, temp); + }; + + return ret; +};*/ + +static PyObject *torrent_constants(PyObject *self, PyObject *args) +{ + Py_INCREF(M_constants); return M_constants; +} + + +static PyObject *torrent_start_DHT(PyObject *self, PyObject *args) +{ + const char *DHT_path; + if (!PyArg_ParseTuple(args, "s", &DHT_path)) + return NULL; + + // printf("Loading DHT state from %s\r\n", DHT_path); + + boost::filesystem::path tempPath(DHT_path, empty_name_check); + boost::filesystem::ifstream DHT_state_file(tempPath, std::ios_base::binary); + DHT_state_file.unsetf(std::ios_base::skipws); + + entry DHT_state; + try + { + DHT_state = bdecode(std::istream_iterator(DHT_state_file), + std::istream_iterator()); + M_ses->start_dht(DHT_state); + // printf("DHT state recovered.\r\n"); + + // // Print out the state data from the FILE (not the session!) + // printf("Number of DHT peers in recovered state: %ld\r\n", count_DHT_peers(DHT_state)); + + } + catch (std::exception&) + { + printf("No DHT file to resume\r\n"); + M_ses->start_dht(); + } + + M_ses->add_dht_router(std::make_pair(std::string("router.bittorrent.com"), + DHT_ROUTER_PORT)); + M_ses->add_dht_router(std::make_pair(std::string("router.utorrent.com"), + DHT_ROUTER_PORT)); + M_ses->add_dht_router(std::make_pair(std::string("router.bitcomet.com"), + DHT_ROUTER_PORT)); + + Py_INCREF(Py_None); return Py_None; +} + + +static PyObject *torrent_stop_DHT(PyObject *self, PyObject *args) +{ + const char *DHT_path; + if (!PyArg_ParseTuple(args, "s", &DHT_path)) + return NULL; + + // printf("Saving DHT state to %s\r\n", DHT_path); + + boost::filesystem::path tempPath = boost::filesystem::path(DHT_path, empty_name_check); + + try + { + entry DHT_state = M_ses->dht_state(); + + // printf("Number of DHT peers in state, saving: %ld\r\n", count_DHT_peers(DHT_state)); + + boost::filesystem::ofstream out(tempPath, std::ios_base::binary); + out.unsetf(std::ios_base::skipws); + bencode(std::ostream_iterator(out), DHT_state); + } + catch (std::exception& e) + { + printf("An error occured in saving DHT\r\n"); + std::cerr << e.what() << "\n"; + } + + Py_INCREF(Py_None); return Py_None; +} + + +static PyObject *torrent_get_DHT_info(PyObject *self, PyObject *args) +{ + entry DHT_state = M_ses->dht_state(); + + return Py_BuildValue("l", python_long(count_DHT_peers(DHT_state))); + + /* + // DHT_state.print(cout); + entry *nodes = DHT_state.find_key("nodes"); + if (!nodes) + return Py_BuildValue("l", -1); // No nodes - we are just starting up... + + entry::list_type &peers = nodes->list(); + entry::list_type::const_iterator i; + + python_long num_peers = 0; + + i = peers.begin(); + while (i != peers.end()) + { + num_peers++; + i++; + } + + return Py_BuildValue("l", num_peers); + */ +} + + +// Create Torrents: call with something like: +// create_torrent("mytorrent.torrent", "directory or file to make a torrent out of", +// "tracker1\ntracker2\ntracker3", "no comment", 256, "Deluge"); +// That makes a torrent with pieces of 256K, with "Deluge" as the creator string. +// +// The following function contains code by Christophe Dumez and Arvid Norberg +static PyObject *torrent_create_torrent(PyObject *self, PyObject *args) +{ + using namespace libtorrent; + using namespace boost::filesystem; + + path::default_name_check(no_check); + + char *destination, *comment, *creator_str, *input, *trackers; + python_long piece_size; + if (!PyArg_ParseTuple(args, "ssssis", + &destination, &input, &trackers, &comment, &piece_size, &creator_str)) + return NULL; + + piece_size = piece_size * 1024; + + try + { + torrent_info t; + boost::filesystem::path full_path = complete(boost::filesystem::path(input)); + boost::filesystem::ofstream out(complete(boost::filesystem::path(destination)), std::ios_base::binary); + + internal_add_files(t, full_path.branch_path(), full_path.leaf()); + t.set_piece_size(piece_size); + + file_pool fp; + boost::scoped_ptr st( + default_storage_constructor(t, full_path.branch_path(), fp)); + + std::string stdTrackers(trackers); + unsigned long index = 0, next = stdTrackers.find("\n"); + while (1 == 1) + { + t.add_tracker(stdTrackers.substr(index, next-index)); + index = next + 1; + if (next >= stdTrackers.length()) + break; + next = stdTrackers.find("\n", index); + if (next == std::string::npos) + break; + } + + int num = t.num_pieces(); + std::vector buf(piece_size); + for (int i = 0; i < num; ++i) + { + st->read(&buf[0], i, 0, t.piece_size(i)); + hasher h(&buf[0], t.piece_size(i)); + t.set_hash(i, h.final()); + } + + t.set_creator(creator_str); + t.set_comment(comment); + + entry e = t.create_torrent(); + bencode(std::ostream_iterator(out), e); + return Py_BuildValue("l", 1); + } catch (std::exception& e) + { + // std::cerr << e.what() << "\n"; + // return Py_BuildValue("l", 0); + RAISE_PTR(DelugeError, e.what()); + } +} + + +static PyObject *torrent_apply_IP_filter(PyObject *self, PyObject *args) +{ + PyObject *ranges; + if (!PyArg_ParseTuple(args, "O", &ranges)) + return NULL; + + long num_ranges = PyList_Size(ranges); + + // printf("Number of ranges: %ld\r\n", num_ranges); + // Py_INCREF(Py_None); return Py_None; + + // Remove existing filter, if there is one + if (M_the_filter != NULL) + delete M_the_filter; + + M_the_filter = new ip_filter(); + + address_v4 from, to; + PyObject *curr; + + // printf("Can I 10.10.10.10? %d\r\n", the_filter->access(address_v4::from_string("10.10.10.10"))); + + for (long i = 0; i < num_ranges; i++) + { + curr = PyList_GetItem(ranges, i); + // PyObject_Print(curr, stdout, 0); + from = address_v4::from_string(PyString_AsString(PyList_GetItem(curr, 0))); + to = address_v4::from_string(PyString_AsString(PyList_GetItem(curr, 1))); + // printf("Filtering: %s - %s\r\n", from.to_string().c_str(), to.to_string().c_str()); + M_the_filter->add_rule(from, to, ip_filter::blocked); + }; + + // printf("Can I 10.10.10.10? %d\r\n", the_filter->access(address_v4::from_string("10.10.10.10"))); + + M_ses->set_ip_filter(*M_the_filter); + + // printf("Can I 10.10.10.10? %d\r\n", the_filter->access(address_v4::from_string("10.10.10.10"))); + + Py_INCREF(Py_None); return Py_None; +} + +static PyObject *torrent_pe_settings(PyObject *self, PyObject *args) +{ + pe_settings p = pe_settings(); + return Py_BuildValue("{p:l,p:l,p:l,p:l}", + "out_enc_policy", long(p.out_enc_policy), + "in_enc_policy", long(p.in_enc_policy), + "allowed_enc_level", long(p.allowed_enc_level), + "prefer_rc4", long(p.prefer_rc4)); +} + +//==================== +// Python Module data +//==================== + +static PyMethodDef deluge_core_methods[] = +{ + {"pe_settings", torrent_pe_settings, METH_VARARGS, "."}, + {"pre_init", torrent_pre_init, METH_VARARGS, "."}, + {"init", torrent_init, METH_VARARGS, "."}, + {"quit", torrent_quit, METH_VARARGS, "."}, + {"save_fastresume", torrent_save_fastresume, METH_VARARGS, "."}, + {"set_max_half_open", torrent_set_max_half_open, METH_VARARGS, "."}, + {"set_download_rate_limit", torrent_set_download_rate_limit, METH_VARARGS, "."}, + {"set_upload_rate_limit", torrent_set_upload_rate_limit, METH_VARARGS, "."}, + {"set_listen_on", torrent_set_listen_on, METH_VARARGS, "."}, + {"is_listening", torrent_is_listening, METH_VARARGS, "."}, + {"listening_port", torrent_listening_port, METH_VARARGS, "."}, + {"set_max_uploads", torrent_set_max_uploads, METH_VARARGS, "."}, + {"set_max_connections", torrent_set_max_connections, METH_VARARGS, "."}, + {"add_torrent", torrent_add_torrent, METH_VARARGS, "."}, + {"remove_torrent", torrent_remove_torrent, METH_VARARGS, "."}, + {"get_num_torrents", torrent_get_num_torrents, METH_VARARGS, "."}, + {"reannounce", torrent_reannounce, METH_VARARGS, "."}, + {"pause", torrent_pause, METH_VARARGS, "."}, + {"resume", torrent_resume, METH_VARARGS, "."}, + {"get_torrent_state", torrent_get_torrent_state, METH_VARARGS, "."}, + {"pop_event", torrent_pop_event, METH_VARARGS, "."}, + {"get_session_info", torrent_get_session_info, METH_VARARGS, "."}, + {"get_peer_info", torrent_get_peer_info, METH_VARARGS, "."}, + {"get_file_info", torrent_get_file_info, METH_VARARGS, "."}, + {"set_filter_out", torrent_set_filter_out, METH_VARARGS, "."}, + {"constants", torrent_constants, METH_VARARGS, "."}, + {"start_DHT", torrent_start_DHT, METH_VARARGS, "."}, + {"stop_DHT", torrent_stop_DHT, METH_VARARGS, "."}, + {"get_DHT_info", torrent_get_DHT_info, METH_VARARGS, "."}, + {"create_torrent", torrent_create_torrent, METH_VARARGS, "."}, + {"apply_IP_filter", torrent_apply_IP_filter, METH_VARARGS, "."}, + {NULL} +}; + +PyMODINIT_FUNC +initdeluge_core(void) +{ + Py_InitModule("deluge_core", deluge_core_methods); +}; diff --git a/encryption/src/deluge_stats.py b/encryption/src/deluge_stats.py new file mode 100644 index 000000000..41caf3a9a --- /dev/null +++ b/encryption/src/deluge_stats.py @@ -0,0 +1,79 @@ +# +# Copyright (C) 2006 Alon Zakai ('Kripken') +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, write to: +# The Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor +# Boston, MA 02110-1301, USA. + +import time + +# Global variables. Caching of saved data, mostly + +old_peer_info = None +old_peer_info_timestamp = None + +# Availability - how many complete copies are among our peers +def calc_availability(peer_info): + if len(peer_info) == 0: + return 0 + + num_pieces = len(peer_info[0].pieces) + + freqs = [0]*num_pieces + + for peer in peer_info: + for piece in num_pieces: + freqs[piece] = freqs[piece] + peer['pieces'][piece] + + minimum = min(freqs) +# frac = freqs.count(minimum + 1) # Does this mean something? + + return minimum + +# Swarm speed - try to guess the speed of the entire swarm +# We return #pieces / second. The calling function should convert pieces to KB, if it wants +# Note that we return the delta from the last call. If the client calls too soon, this may +# be too unreliable. But the client can smooth things out, if desired +def calc_swarm_speed(peer_info): + if old_peer_info is not None: + new_pieces = 0 + peers_known = 0 + + # List new peers + new_peer_IPs = {} # ip->peerinfo dict (from the core) + for peer in peer_info: + new_peer_IPs[peer['ip']] = peer + + for new_IP in new_peer_IPs.keys(): + if new_IP in old_peer_IPs.keys(): + # We know this peer from before, see what changed + peers_known = peers_known + 1 + delta = sum(new_peer_IPs[new_IP].pieces) - sum(old_peer_IPs[new_IP].pieces) + + if delta >= 0: + new_pieces = new_pieces + delta + else: + print "Deluge.stat.calc_swarm_speed: Bad Delta: ", delta, old_peer_IPs[new_IP].pieces, new_peer_IPs[new_IP].pieces + + # Calculate final value + time_delta = time.time() - old_peer_info_timestamp + ret = float(new_pieces)/( float(peers_known) * time_delta ) + + # Save info + old_peer_info = peer_info + old_peer_info_timestamp = time.time() + old_peer_IPs = new_peer_IPs + + return ret diff --git a/encryption/src/dgtk.py b/encryption/src/dgtk.py new file mode 100644 index 000000000..083ff3f6a --- /dev/null +++ b/encryption/src/dgtk.py @@ -0,0 +1,94 @@ +# dgtk.py +# +# Copyright (C) Zach Tibbitts 2006 +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, write to: +# The Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor +# Boston, MA 02110-1301, USA. + +# Similar to common, this contains any common functions +# related to gtk that are needed by the client + +import common +import gettext +import pygtk +pygtk.require('2.0') +import gtk +import gtk.glade + +# This is a dummy tray object to allow Deluge to run on PyGTK < 2.9 +class StupidTray: + def __init__(self): + pass + def set_visible(self, value): + pass + def set_tooltip(self, value): + pass + +## Functions to create columns + +def add_func_column(view, header, func, data, sortid=None): + column = gtk.TreeViewColumn(header) + render = gtk.CellRendererText() + column.pack_start(render, True) + column.set_cell_data_func(render, func, data) + if sortid is not None: + column.set_clickable(True) + column.set_sort_column_id(sortid) + else: + try: + if len(data) == 1: + column.set_clickable(True) + column.set_sort_column_id(data[0]) + except TypeError: + column.set_clickable(True) + column.set_sort_column_id(data) + column.set_resizable(True) + column.set_expand(False) + view.append_column(column) + return column + + +def add_text_column(view, header, cid): + render = gtk.CellRendererText() + column = gtk.TreeViewColumn(header, render, text=cid) + column.set_clickable(True) + column.set_sort_column_id(cid) + column.set_resizable(True) + column.set_expand(False) + view.append_column(column) + return column + +def add_progress_column(view, header, pid, mid): + render = gtk.CellRendererProgress() + column = gtk.TreeViewColumn(header, render, value=pid, text=mid) + column.set_clickable(True) + column.set_sort_column_id(pid) + column.set_resizable(True) + column.set_expand(False) + view.append_column(column) + return column + +def add_toggle_column(view, header, cid, toggled_signal=None): + render = gtk.CellRendererToggle() + render.set_property('activatable', True) + column = gtk.TreeViewColumn(header, render, active=cid) + column.set_clickable(True) + column.set_resizable(True) + column.set_expand(False) + view.append_column(column) + if toggled_signal is not None: + render.connect("toggled", toggled_signal) + return column diff --git a/encryption/src/dialogs.py b/encryption/src/dialogs.py new file mode 100644 index 000000000..a76403fe5 --- /dev/null +++ b/encryption/src/dialogs.py @@ -0,0 +1,285 @@ +# dialogs.py +# +# Copyright (C) Zach Tibbitts 2006 +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, write to: +# The Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor +# Boston, MA 02110-1301, USA. + +import common, dgtk +import gtk, gtk.glade +import os, os.path + + + +class PreferencesDlg: + def __init__(self, parent, preferences): + self.glade = gtk.glade.XML(common.get_glade_file("preferences_dialog.glade"), domain='deluge') + self.dialog = self.glade.get_widget("pref_dialog") + self.dialog.set_icon_from_file(common.get_pixmap("deluge32.png")) + self.glade.signal_autoconnect({ + 'tray_toggle': self.tray_toggle, + }) + self.parent = parent + self.preferences = preferences + + def show(self): + # Load settings into dialog + try: + if(self.preferences.get("chk_encin_disabled", bool, True)): + self.glade.get_widget("chk_encin_enabled").set_active(False) + self.glade.get_widget("chk_encin_enabled").set_active(False) + elif!(self.glade.get_widget("chk_encin_enabled", bool, False)): + self.glade.get_widget("chk_encin_enabled").set_active(False) + self.glade.get_widget("chk_encin_enabled").set_active(False) + elif!(self.glade.get_widget("chk_encin_forced", bool, False)): + self.glade.get_widget("chk_encin_enabled").set_active(False) + self.glade.get_widget("chk_encin_enabled").set_active(False) + if(self.preferences.get("chk_encout_disabled", bool, True)): + self.glade.get_widget("chk_encout_enabled").set_active(False) + self.glade.get_widget("chk_encout_enabled").set_active(False) + elif!(self.glade.get_widget("chk_encout_enabled", bool, False)): + self.glade.get_widget("chk_encout_enabled").set_active(False) + self.glade.get_widget("chk_encout_enabled").set_active(False) + elif!(self.glade.get_widget("chk_encout_forced", bool, False)): + self.glade.get_widget("chk_encout_enabled").set_active(False) + self.glade.get_widget("chk_encout_enabled").set_active(False) + + + + + + + + + self.glade.get_widget("chk_prefer_rc4").set_active(self.preferences.get("prefer_rc4", bool, default=True)) + self.glade.get_widget("chk_use_tray").set_active(self.preferences.get("enable_system_tray", bool, default=True)) + self.glade.get_widget("chk_min_on_close").set_active(self.preferences.get("close_to_tray", bool, default=False)) + self.glade.get_widget("chk_lock_tray").set_active(self.preferences.get("lock_tray", bool, default=False)) + self.glade.get_widget("txt_tray_passwd").set_text(self.preferences.get("tray_passwd", default="")) + if(self.preferences.get("use_default_dir", bool, False)): + self.glade.get_widget("radio_save_all_to").set_active(True) + else: + self.glade.get_widget("radio_ask_save").set_active(True) + self.glade.get_widget("download_path_button").set_filename(self.preferences.get("default_download_path", str, default=os.path.expandvars('$HOME'))) + self.glade.get_widget("chk_compact").set_active(self.preferences.get("use_compact_storage", bool, default=False)) + self.glade.get_widget("active_port_label").set_text(str(self.parent.manager.get_state()['port'])) + self.glade.get_widget("spin_port_min").set_value(self.preferences.get("tcp_port_range_lower", int, default=6881)) + self.glade.get_widget("spin_port_max").set_value(self.preferences.get("tcp_port_range_upper", int, default=6889)) + self.glade.get_widget("spin_max_upload").set_value(self.preferences.get("max_upload_rate", int, default=-1)) + self.glade.get_widget("spin_num_upload").set_value(self.preferences.get("max_number_uploads", int, default=-1)) + self.glade.get_widget("spin_max_download").set_value(self.preferences.get("max_download_rate", int, default=-1)) + self.glade.get_widget("spin_num_download").set_value(self.preferences.get("max_number_downloads", int, default=-1)) + self.glade.get_widget("spin_torrents").set_value(self.preferences.get("max_number_torrents", int, default=-1)) + self.glade.get_widget("chk_seedbottom").set_active(self.preferences.get("queue_seeds_to_bottom", bool, default=False)) + self.glade.get_widget("chk_dht").set_active(self.preferences.get("enable_dht", bool, default=True)) + self.glade.get_widget("spin_dht").set_value(self.preferences.get("dht_connections", int, default=80)) + self.glade.get_widget("spin_gui").set_value(self.preferences.get("gui_update_interval", float, default=1.0)) + except KeyError: + pass + # Now, show the dialog + self.dialog.show() + r = self.dialog.run() + self.dialog.hide() + # Now, get the settings from the dialog + if r == 1: + self.preferences.set("enable_system_tray", self.glade.get_widget("chk_use_tray").get_active()) + self.preferences.set("close_to_tray", self.glade.get_widget("chk_min_on_close").get_active()) + self.preferences.set("lock_tray", self.glade.get_widget("chk_lock_tray").get_active()) + self.preferences.set("tray_passwd", self.glade.get_widget("txt_tray_passwd").get_text()) + self.preferences.set("use_default_dir", self.glade.get_widget("radio_save_all_to").get_active()) + self.preferences.set("default_download_path", self.glade.get_widget("download_path_button").get_filename()) + self.preferences.set("auto_end_seeding", self.glade.get_widget("chk_autoseed").get_active()) + self.preferences.set("end_seed_ratio", self.glade.get_widget("ratio_spinner").get_value()) + self.preferences.set("use_compact_storage", self.glade.get_widget("chk_compact").get_active()) + self.preferences.set("tcp_port_range_lower", self.glade.get_widget("spin_port_min").get_value()) + self.preferences.set("tcp_port_range_upper", self.glade.get_widget("spin_port_max").get_value()) + self.preferences.set("max_upload_rate", self.glade.get_widget("spin_max_upload").get_value()) + self.preferences.set("max_number_uploads", self.glade.get_widget("spin_num_upload").get_value()) + self.preferences.set("max_download_rate", self.glade.get_widget("spin_max_download").get_value()) + self.preferences.set("max_number_downloads", self.glade.get_widget("spin_num_download").get_value()) + self.preferences.set("max_number_torrents", self.glade.get_widget("spin_torrents").get_value()) + self.preferences.set("queue_seeds_to_bottom", self.glade.get_widget("chk_seedbottom").get_active()) + self.preferences.set("enable_dht", self.glade.get_widget("chk_dht").get_active()) + self.preferences.set("dht_connections", self.glade.get_widget("spin_dht").get_value()) + self.preferences.set("gui_update_interval", self.glade.get_widget("spin_gui").get_value()) + + def tray_toggle(self, obj): + if obj.get_active(): + self.glade.get_widget("chk_min_on_close").set_sensitive(True) + else: + self.glade.get_widget("chk_min_on_close").set_sensitive(False) + + + + +class PluginDlg: + def __init__(self, parent, plugins): + self.glade = gtk.glade.XML(common.get_glade_file("plugin_dialog.glade"), domain='deluge') + self.dialog = self.glade.get_widget("plugin_dialog") + self.dialog.set_icon_from_file(common.get_pixmap("deluge32.png")) + self.view = self.glade.get_widget("plugin_view") + self.store = gtk.ListStore(str, bool) + self.view.set_model(self.store) + try: + self.view.get_selection().set_select_function(self.plugin_clicked, full=True) + except TypeError: + self.view.get_selection().set_select_function(self.old_clicked) + name_col = dgtk.add_text_column(self.view, _("Plugin"), 0) + name_col.set_expand(True) + dgtk.add_toggle_column(self.view, _("Enabled"), 1, toggled_signal=self.plugin_toggled) + self.glade.signal_autoconnect({'plugin_pref': self.plugin_pref}) + self.parent = parent + self.plugins = plugins + + + def show(self): + self.store.clear() + for plugin in self.plugins.get_available_plugins(): + print plugin + if plugin in self.plugins.get_enabled_plugins(): + self.store.append( (plugin, True) ) + else: + self.store.append( (plugin, False) ) + self.glade.get_widget("plugin_text").get_buffer().set_text("") + self.glade.get_widget("plugin_conf").set_sensitive(False) + self.dialog.show() + self.dialog.run() + self.dialog.hide() + + def old_clicked(self, path): + return self.plugin_clicked(self.view.get_selection(), self.store, path, False) + + def plugin_clicked(self, selection, model, path, is_selected): + if is_selected: + return True + name = model.get_value(model.get_iter(path), 0) + plugin = self.plugins.get_plugin(name) + author = plugin['author'] + version = plugin['version'] + config = plugin['config'] + description = plugin['description'] + if name in self.plugins.get_enabled_plugins(): + self.glade.get_widget("plugin_conf").set_sensitive(config) + else: + self.glade.get_widget("plugin_conf").set_sensitive(False) + self.glade.get_widget("plugin_text").get_buffer( + ).set_text("%s\nBy: %s\nVersion: %s\n\n%s"% + (name, author, version, description)) + return True + + def plugin_toggled(self, renderer, path): + plugin_iter = self.store.get_iter_from_string(path) + plugin_name = self.store.get_value(plugin_iter, 0) + plugin_value = not self.store.get_value(plugin_iter, 1) + self.store.set_value(plugin_iter, 1, plugin_value) + if plugin_value: + self.plugins.enable_plugin(plugin_name) + self.glade.get_widget("plugin_conf").set_sensitive( + self.plugins.get_plugin(plugin_name)['config']) + else: + self.plugins.disable_plugin(plugin_name) + self.glade.get_widget("plugin_conf").set_sensitive(False) + + def plugin_pref(self, widget=None): + (model, plugin_iter) = self.view.get_selection().get_selected() + plugin_name = self.store.get_value(plugin_iter, 0) + self.plugins.configure_plugin(plugin_name) + + +def show_about_dialog(parent=None): + gtk.about_dialog_set_url_hook(common.open_url_in_browser) + abt = gtk.glade.XML(common.get_glade_file("aboutdialog.glade")).get_widget("aboutdialog") + abt.set_name(common.PROGRAM_NAME) + abt.set_version(common.PROGRAM_VERSION) + abt.set_authors(["Zach Tibbits", "Alon Zakai", "Marcos Pinto"]) + abt.set_artists(["Andrew Wedderburn"]) + abt.set_website("http://deluge-torrent.org") + abt.set_website_label("http://deluge-torrent.org") + abt.set_icon_from_file(common.get_pixmap("deluge32.png")) + abt.set_logo(gtk.gdk.pixbuf_new_from_file( + common.get_pixmap("deluge-about.png"))) + abt.show_all() + abt.run() + abt.hide_all() + +def show_popup_warning(window, message): + warner = gtk.MessageDialog(parent = window, + flags = gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, + buttons= gtk.BUTTONS_OK, + message_format=message, + type = gtk.MESSAGE_WARNING) + warner.run() + warner.destroy() + +def show_popup_question(window, message): + asker = gtk.MessageDialog(parent = window, + flags = gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, + buttons = gtk.BUTTONS_YES_NO, + message_format=message, + type=gtk.MESSAGE_QUESTION) + result = asker.run() + asker.destroy() + if result == gtk.RESPONSE_YES: + return True + elif result == gtk.RESPONSE_NO: + return False + elif result == gtk.RESPONSE_DELETE_EVENT: + return False + else: + return False + + +## Browse for .torrent files +def show_file_open_dialog(parent=None, title=None): + if title is None: + title = _("Choose a .torrent file") + chooser = gtk.FileChooserDialog(title, parent, gtk.FILE_CHOOSER_ACTION_OPEN, + buttons=(gtk.STOCK_CANCEL,gtk.RESPONSE_CANCEL,gtk.STOCK_OPEN,gtk.RESPONSE_OK)) + + f0 = gtk.FileFilter() + f0.set_name(_("Torrent files")) + f0.add_pattern("*." + "torrent") + chooser.add_filter(f0) + f1 = gtk.FileFilter() + f1.set_name(_("All files")) + f1.add_pattern("*") + chooser.add_filter(f1) + + chooser.set_icon_from_file(common.get_pixmap("deluge32.png")) + chooser.set_property("skip-taskbar-hint", True) + + response = chooser.run() + if response == gtk.RESPONSE_OK: + result = chooser.get_filename() + else: + result = None + chooser.destroy() + return result + +def show_directory_chooser_dialog(parent=None, title=None): + if title is None: + title = _("Choose a download directory") + chooser = gtk.FileChooserDialog(title, parent, gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER, + buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OK, gtk.RESPONSE_OK)) + chooser.set_icon_from_file(common.get_pixmap("deluge32.png")) + chooser.set_property("skip-taskbar-hint", True) + if chooser.run() == gtk.RESPONSE_OK: + result = chooser.get_filename() + else: + result = None + chooser.destroy() + return result + diff --git a/encryption/src/interface.py b/encryption/src/interface.py new file mode 100644 index 000000000..9df853985 --- /dev/null +++ b/encryption/src/interface.py @@ -0,0 +1,1070 @@ +#!/usr/bin/env python +# +# interface.py +# +# Copyright (C) Zach Tibbitts 2006 +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, write to: +# The Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor +# Boston, MA 02110-1301, USA. + +import sys, os, os.path, urllib +import core, common, dgtk, ipc_manager, dialogs +import plugins, pref +import pygtk +pygtk.require('2.0') +import gtk, gtk.glade, gobject +import xdg, xdg.BaseDirectory +import gettext, locale + +DEFAULT_PREFS = { + "auto_end_seeding" : False, + "close_to_tray" : False, + "lock_tray" : False, + "tray_passwd" : "", + "default_download_path" : "", + "dht_connections" : 80, + "enable_dht" : True, + "enable_system_tray" : True, + "enabled_plugins" : "", + "end_seed_ratio" : 0.0, + "gui_update_interval" : 1.0, + "max_download_rate" : -1.0, + "max_number_downloads" : -1.0, + "max_number_torrents" : -1.0, + "max_number_uploads" : -1.0, + "max_upload_rate" : -1.0, + "queue_seeds_to_bottom" : False, + "show_dl" : True, + "show_eta" : True, + "show_infopane" : True, + "show_peers" : True, + "show_seeders" : True, + "show_share" : True, + "show_size" : True, + "show_status" : True, + "show_toolbar" : True, + "show_ul" : True, + "tcp_port_range_lower" : 6881, + "tcp_port_range_upper" : 6889, + "use_compact_storage" : False, + "use_default_dir" : False, + "window_height" : 480, + "window_width" : 640, + "window_x_pos" : 0, + "window_y_pos" : 0, + } + +class DelugeGTK: + def __init__(self): + APP = 'deluge' + DIR = os.path.join(common.INSTALL_PREFIX, 'share', 'locale') + locale.setlocale(locale.LC_ALL, '') + locale.bindtextdomain(APP, DIR) + locale.textdomain(APP) + gettext.bindtextdomain(APP, DIR) + gettext.textdomain(APP) + gettext.install(APP, DIR) + + self.is_running = False + self.ipc_manager = ipc_manager.Manager(self) + self.torrent_file_queue = [] + #Load up a config file: + self.conf_file = os.path.join(common.CONFIG_DIR, 'deluge.conf') + if os.path.isdir(self.conf_file): + print 'Weird, the file I was trying to write to, %s, is an existing directory'%(self.conf_file) + sys.exit(0) + if not os.path.isfile(self.conf_file): + f = open(self.conf_file, mode='w') + f.flush() + f.close() + #Start the Deluge Manager: + self.manager = core.Manager(common.CLIENT_CODE, common.CLIENT_VERSION, + '%s %s'%(common.PROGRAM_NAME, common.PROGRAM_VERSION), common.CONFIG_DIR) + self.something_screwed_up = False + self.plugins = plugins.PluginManager(self.manager, self) + self.plugins.add_plugin_dir(common.PLUGIN_DIR) + if os.path.isdir(os.path.join(common.CONFIG_DIR , 'plugins')): + self.plugins.add_plugin_dir(os.path.join(common.CONFIG_DIR, 'plugins')) + self.plugins.scan_for_plugins() + self.config = pref.Preferences(self.conf_file, DEFAULT_PREFS) + #Set up the interface: + self.wtree = gtk.glade.XML(common.get_glade_file("delugegtk.glade"), domain=APP) + self.window = self.wtree.get_widget("main_window") + self.toolbar = self.wtree.get_widget("tb_middle") + self.window.drag_dest_set(gtk.DEST_DEFAULT_ALL,[('text/uri-list', 0, 80)], gtk.gdk.ACTION_COPY) + self.window.connect("delete_event", self.close) + self.window.connect("drag_data_received", self.on_drag_data) + self.window.set_title(common.PROGRAM_NAME) + self.window.set_icon_from_file(common.get_pixmap("deluge32.png")) + self.notebook = self.wtree.get_widget("torrent_info") + self.statusbar = self.wtree.get_widget("statusbar") + + + + + ## Construct the Interface + try: + self.build_tray_icon() + except AttributeError: + #python-pygtk is < 2.9 + self.tray_icon = dgtk.StupidTray() + self.has_tray = False + else: + self.has_tray = True + + self.preferences_dialog = dialogs.PreferencesDlg(self, self.config) + self.plugin_dialog = dialogs.PluginDlg(self, self.plugins) + self.build_torrent_table() + self.build_summary_tab() + self.build_file_tab() + self.build_peer_tab() + + self.connect_signals() + + try: + self.load_window_settings() + except KeyError: + pass + + enable_plugins = self.config.get('enabled_plugins', str, default="").split(':') + print enable_plugins + for plugin in enable_plugins: + try: + self.plugins.enable_plugin(plugin) + except KeyError: + pass + self.apply_prefs() + self.load_window_geometry() + + def external_add_torrent(self, torrent_file): + print "Ding!" + print "Got torrent externally:", os.path.basename(torrent_file) + print "Here's the raw data:", torrent_file + print "\tNow, what to do with it?" + if self.is_running: + print "\t\tthe client seems to already be running, i'll try and add the torrent" + uid = self.interactive_add_torrent(torrent_file) + else: + print "\t\tthe client hasn't started yet, I'll queue the torrent" + self.torrent_file_queue.append(torrent_file) + + def connect_signals(self): + self.wtree.signal_autoconnect({ + ## File Menu + "add_torrent": self.add_torrent_clicked, + "add_torrent_url": self.add_torrent_url_clicked, + "remove_torrent" : self.remove_torrent_clicked, + "menu_quit": self.quit, + ## Edit Menu + "pref_clicked": self.show_pref_dialog, + "plugins_clicked": self.show_plugin_dialog, + ## View Menu + "toolbar_toggle": self.toolbar_toggle, + "infopane_toggle": self.infopane_toggle, + "size_toggle": self.size_toggle, + "status_toggle": self.status_toggle, + "seeders_toggle": self.seeders_toggle, + "peers_toggle": self.peers_toggle, + "dl_toggle": self.dl_toggle, + "ul_toggle": self.ul_toggle, + "eta_toggle": self.eta_toggle, + "share_toggle": self.share_toggle, + ## Help Menu + "show_about_dialog": self.show_about_dialog, + ## Toolbar + "start_pause": self.start_pause, + "update_tracker": self.update_tracker, + "clear_finished": self.clear_finished, + "queue_up": self.q_torrent_up, + "queue_down": self.q_torrent_down, + "queue_bottom": self.q_to_bottom, + }) + + def build_tray_icon(self): + self.tray_icon = gtk.status_icon_new_from_file(common.get_pixmap("deluge32.png")) + self.tray_menu = gtk.Menu() + item_show = gtk.MenuItem(_("Show / Hide Window")) + item_add = gtk.ImageMenuItem(_("Add Torrent")) + item_clear = gtk.ImageMenuItem(_("Clear Finished")) + item_pref = gtk.ImageMenuItem(gtk.STOCK_PREFERENCES) + item_plug = gtk.ImageMenuItem(_("Plugins")) + item_quit = gtk.ImageMenuItem(gtk.STOCK_QUIT) + + item_add.set_image(gtk.image_new_from_stock(gtk.STOCK_ADD, gtk.ICON_SIZE_MENU)) + item_clear.set_image(gtk.image_new_from_stock(gtk.STOCK_CLEAR, gtk.ICON_SIZE_MENU)) + item_plug.set_image(gtk.image_new_from_stock(gtk.STOCK_DISCONNECT, gtk.ICON_SIZE_MENU)) + + item_show.connect("activate", self.force_show_hide) + item_add.connect("activate", self.add_torrent_clicked) + item_clear.connect("activate", self.clear_finished) + item_pref.connect("activate", self.show_pref_dialog) + item_plug.connect("activate", self.show_plugin_dialog) + item_quit.connect("activate", self.quit) + + self.tray_menu.append(item_show) + self.tray_menu.append(item_add) + self.tray_menu.append(item_clear) + self.tray_menu.append(gtk.SeparatorMenuItem()) + self.tray_menu.append(item_pref) + self.tray_menu.append(item_plug) + self.tray_menu.append(gtk.SeparatorMenuItem()) + self.tray_menu.append(item_quit) + + self.tray_menu.show_all() + + self.tray_icon.connect("activate", self.tray_clicked) + self.tray_icon.connect("popup-menu", self.tray_popup) + + def tray_popup(self, status_icon, button, activate_time): + self.tray_menu.popup(None, None, gtk.status_icon_position_menu, + button, activate_time, status_icon) + + def unlock_tray(self,comingnext): + entered_pass = gtk.Entry(25) + entered_pass.set_activates_default(True) + entered_pass.set_width_chars(25) + entered_pass.set_visibility(False) + entered_pass.show() + tray_lock = gtk.Dialog(title=_("Deluge is locked"), parent=self.window, + buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT, gtk.STOCK_OK, gtk.RESPONSE_ACCEPT)) + label = gtk.Label(_("Deluge is password protected.\nTo show the Deluge window, please enter your password")) + label.set_line_wrap(True) + label.set_justify(gtk.JUSTIFY_CENTER) + tray_lock.set_position(gtk.WIN_POS_CENTER_ALWAYS) + tray_lock.set_size_request(400, 200) + tray_lock.set_default_response(gtk.RESPONSE_ACCEPT) + tray_lock.vbox.pack_start(label) + tray_lock.vbox.pack_start(entered_pass) + tray_lock.show_all() + if tray_lock.run() == gtk.RESPONSE_ACCEPT: + if self.config.get("tray_passwd", default="") == entered_pass.get_text(): + if comingnext == "mainwinshow": + self.window.show() + elif comingnext == "prefwinshow": + self.preferences_dialog.show() + self.apply_prefs() + self.config.save_to_file() + elif comingnext == "quitus": + self.save_window_geometry() + self.window.hide() + self.shutdown() + + tray_lock.destroy() + return True + + + def tray_clicked(self, status_icon): + if self.window.get_property("visible"): + if self.window.is_active(): + self.window.hide() + else: + self.window.present() + else: + if self.config.get("lock_tray", bool, default=False) == True: + self.unlock_tray("mainwinshow") + else: + self.window.show() + + def force_show_hide(self, arg=None): + if self.window.get_property("visible"): + self.window.hide() + else: + if self.config.get("lock_tray", bool, default=False) == True: + self.unlock_tray("mainwinshow") + else: + self.window.show() + + def build_torrent_table(self): + ## Create the torrent listview + self.torrent_view = self.wtree.get_widget("torrent_view") + self.torrent_glade = gtk.glade.XML(common.get_glade_file("torrent_menu.glade"), domain='deluge') + self.torrent_menu = self.torrent_glade.get_widget("torrent_menu") + self.torrent_glade.signal_autoconnect({ "start_pause": self.start_pause, + "update_tracker": self.update_tracker, + "clear_finished": self.clear_finished, + "queue_up": self.q_torrent_up, + "queue_down": self.q_torrent_down, + "queue_bottom": self.q_to_bottom, + }) + # UID, Q#, Name, Size, Progress, Message, Seeders, Peers, DL, UL, ETA, Share + self.torrent_model = gtk.ListStore(int, int, str, str, float, str, int, int, int, int, int, int, str, float) + self.torrent_view.set_model(self.torrent_model) + self.torrent_view.set_rules_hint(True) + self.torrent_view.set_reorderable(True) + + def size(column, cell, model, iter, data): + size = long(model.get_value(iter, data)) + size_str = common.fsize(size) + cell.set_property('text', size_str) + + def rate(column, cell, model, iter, data): + rate = int(model.get_value(iter, data)) + rate_str = common.frate(rate) + cell.set_property('text', rate_str) + + def peer(column, cell, model, iter, data): + c1, c2 = data + a = int(model.get_value(iter, c1)) + b = int(model.get_value(iter, c2)) + cell.set_property('text', '%d (%d)'%(a, b)) + + def time(column, cell, model, iter, data): + time = int(model.get_value(iter, data)) + if time < 0: + time_str = _("Infinity") + elif time == 0: + time_str = "-" + else: + time_str = common.ftime(time) + cell.set_property('text', time_str) + + def ratio(column, cell, model, iter, data): + ratio = float(model.get_value(iter, data)) + if ratio == -1: + ratio_str = _("Unknown") + else: + ratio_str = "%.2f"%ratio + cell.set_property('text', ratio_str) + + + ## Initializes the columns for the torrent_view + self.queue_column = dgtk.add_text_column(self.torrent_view, "#", 1) + self.name_column = dgtk.add_text_column(self.torrent_view, _("Name"), 2) + self.size_column = dgtk.add_func_column(self.torrent_view, _("Size"), size, 3) + self.status_column = dgtk.add_progress_column(self.torrent_view, _("Status"), 4, 5) + self.seed_column = dgtk.add_func_column(self.torrent_view, _("Seeders"), peer, (6, 7)) + self.peer_column = dgtk.add_func_column(self.torrent_view, _("Peers"), peer, (8, 9)) + self.dl_column = dgtk.add_func_column(self.torrent_view, _("Download"), rate, 10) + self.ul_column = dgtk.add_func_column(self.torrent_view, _("Upload"), rate, 11) + self.eta_column = dgtk.add_func_column(self.torrent_view, _("Time Remaining"), time, 12) + self.share_column = dgtk.add_func_column(self.torrent_view, _("Ratio"), ratio, 13) + + self.status_column.set_expand(True) + self.seed_column.set_sort_column_id(6) + self.peer_column.set_sort_column_id(8) + + def long_sort(model, iter1, iter2, data): + value1 = long(model.get_value(iter1, data)) + value2 = long(model.get_value(iter2, data)) + if value1 < value2: + return -1 + elif value1 > value2: + return 1 + else: + return 0 + + self.torrent_model.set_sort_func(3, long_sort, 3) + self.torrent_model.set_sort_func(12, long_sort, 12) + try: + self.torrent_view.get_selection().set_select_function(self.torrent_clicked, full=True) + except TypeError: + self.torrent_view.get_selection().set_select_function(self.old_t_click) + self.torrent_view.connect("button-press-event", self.torrent_view_clicked) + + def old_t_click(self, path): + return self.torrent_clicked(self.torrent_view.get_selection(), self.torrent_model, path, False) + + def torrent_clicked(self, selection, model, path, is_selected): + if is_selected: + # Torrent is already selected, we don't need to do anything + return True + unique_id = model.get_value(model.get_iter(path), 0) + state = self.manager.get_torrent_state(unique_id) + # A new torrent has been selected, need to update parts of interface + self.text_summary_total_size.set_text(common.fsize(state["total_size"])) + self.text_summary_pieces.set_text(str(state["pieces"])) + self.text_summary_tracker.set_text(str(state["tracker"])) + #self.text_summary_compact_allocation.set_text(str(state[""])) + # Now for the File tab + self.file_store.clear() + all_files = self.manager.get_torrent_file_info(unique_id) + file_filter = self.manager.get_file_filter(unique_id) + if file_filter is None: + file_filter = [False] * len(all_files) + assert(len(all_files) == len(file_filter)) + i=0 + for f in all_files: + self.file_store.append([not file_filter[i], f['path'], common.fsize(f['size']), + f['offset'], '%.2f%%'%f['progress']]) + i=i+1 + + return True + + def torrent_view_clicked(self, widget, event): + print widget + print event + if event.button == 3: + x = int(event.x) + y = int(event.y) + data = self.torrent_view.get_path_at_pos(x, y) + if data is None: + return True + path, col, cellx, celly = data + self.torrent_view.grab_focus() + self.torrent_view.set_cursor(path, col, 0) + unique_id = self.get_selected_torrent() + widget = self.torrent_glade.get_widget("menu_pause") + if(self.manager.is_user_paused(self.get_selected_torrent())): + widget.set_image(gtk.image_new_from_stock(gtk.STOCK_MEDIA_PLAY, gtk.ICON_SIZE_MENU)) + widget.get_children()[0].set_text(_("Start")) + else: + widget.set_image(gtk.image_new_from_stock(gtk.STOCK_MEDIA_PAUSE, gtk.ICON_SIZE_MENU)) + widget.get_children()[0].set_text(_("Pause")) + + self.torrent_menu.popup(None, None, None, event.button, event.time) + + return True + else: + return False + + def start_pause(self, widget): + print "Pause btn clicked" + unique_id = self.get_selected_torrent() + try: + self.manager.set_user_pause(unique_id, not self.manager.is_user_paused(unique_id)) + except KeyError: + pass + + def build_summary_tab(self): + #Torrent Summary tab + # Look into glade's widget prefix function + self.text_summary_title = self.wtree.get_widget("summary_title") + self.text_summary_total_size = self.wtree.get_widget("summary_total_size") + self.text_summary_pieces = self.wtree.get_widget("summary_pieces") + self.text_summary_total_downloaded = self.wtree.get_widget("summary_total_downloaded") + self.text_summary_total_uploaded = self.wtree.get_widget("summary_total_uploaded") + self.text_summary_download_rate = self.wtree.get_widget("summary_download_rate") + self.text_summary_upload_rate = self.wtree.get_widget("summary_upload_rate") + self.text_summary_seeders = self.wtree.get_widget("summary_seeders") + self.text_summary_peers = self.wtree.get_widget("summary_peers") + self.text_summary_percentage_done = self.wtree.get_widget("summary_percentage_done") + self.text_summary_share_ratio = self.wtree.get_widget("summary_share_ratio") + self.text_summary_downloaded_this_session = self.wtree.get_widget("summary_downloaded_this_session") + self.text_summary_uploaded_this_session = self.wtree.get_widget("summary_uploaded_this_session") + self.text_summary_tracker = self.wtree.get_widget("summary_tracker") + self.text_summary_tracker_response = self.wtree.get_widget("summary_tracker_response") + self.text_summary_tracker_status = self.wtree.get_widget("summary_tracker_status") + self.text_summary_next_announce = self.wtree.get_widget("summary_next_announce") + self.text_summary_compact_allocation = self.wtree.get_widget("summary_compact_allocation") + self.text_summary_eta = self.wtree.get_widget("summary_eta") + + def build_peer_tab(self): + self.peer_view = self.wtree.get_widget("peer_view") + self.peer_store = gtk.ListStore(str, str, str, str, str) + self.peer_view.set_model(self.peer_store) + + self.peer_ip_column = dgtk.add_text_column(self.peer_view, _("IP Address"), 0) + self.peer_client_column = dgtk.add_text_column(self.peer_view, _("Client"), 1) + self.peer_complete_column = dgtk.add_text_column(self.peer_view, _("Percent Complete"), 2) + self.peer_download_column = dgtk.add_text_column(self.peer_view, _("Download Rate"), 3) + self.peer_upload_column = dgtk.add_text_column(self.peer_view, _("Upload Rate"), 4) + + + def build_file_tab(self): + self.file_view = self.wtree.get_widget("file_view") + self.file_store = gtk.ListStore(bool, str, str, str, str) + self.file_view.set_model(self.file_store) + + dgtk.add_toggle_column(self.file_view, _("Download"), 0, toggled_signal=self.file_toggled) + dgtk.add_text_column(self.file_view, _("Filename"), 1).set_expand(True) + dgtk.add_text_column(self.file_view, _("Size"), 2) + dgtk.add_text_column(self.file_view, _("Offset"), 3) + dgtk.add_text_column(self.file_view, _("Progress"), 4) + + + def file_toggled(self, renderer, path): + file_iter = self.file_store.get_iter_from_string(path) + value = not renderer.get_active() + self.file_store.set_value(file_iter, 0, value) + file_filter = [] + itr = self.file_store.get_iter_first() + while itr is not None: + file_filter.append(not self.file_store.get_value(itr, 0)) + itr = self.file_store.iter_next(itr) + print file_filter + self.manager.set_file_filter(self.get_selected_torrent(), file_filter) + + def show_about_dialog(self, arg=None): + dialogs.show_about_dialog() + + def show_pref_dialog(self, arg=None): + if self.window.get_property("visible"): + self.preferences_dialog.show() + self.apply_prefs() + self.config.save_to_file() + + else: + if self.config.get("lock_tray", bool, default=False) == True: + self.unlock_tray("prefwinshow") + else: + self.preferences_dialog.show() + self.apply_prefs() + self.config.save_to_file() + + + def show_plugin_dialog(self, arg=None): + self.plugin_dialog.show() + + def apply_prefs(self): + ulrate = self.config.get("max_upload_rate", int, default=-1) + dlrate = self.config.get("max_download_rate", int, default=-1) + if not (ulrate == -1): + ulrate *= 1024 + if not (dlrate == -1): + dlrate *= 1024 + ports = [self.config.get("tcp_port_range_lower", int, default=6881), + self.config.get("tcp_port_range_upper", int, default=6889)] + if self.config.get("auto_end_seeding", bool, default=False): + auto_seed_ratio = self.config.get("end_seed_ratio", float, default=1.0) + else: + auto_seed_ratio = -1 + self.tray_icon.set_visible(self.config.get("enable_system_tray", bool, default=True)) + self.manager.set_pref("listen_on", ports) + self.manager.set_pref("max_upload_rate", ulrate) + self.manager.set_pref("max_download_rate", dlrate) + self.manager.set_pref("max_uploads", self.config.get("max_number_uploads", int, default=-1)) + self.manager.set_pref("max_connections", self.config.get("max_number_downloads", int, default=-1)) + self.manager.set_pref("auto_seed_ratio", auto_seed_ratio) + + # '%s %d%%'%(core.STATE_MESSAGES[state['state']], int(state['progress'] * 100)) + def get_message_from_state(self, torrent_state): + state = torrent_state['state'] + is_paused = torrent_state['is_paused'] + progress = torrent_state['progress'] + progress = '%d%%'%int(progress * 100) + if is_paused: + message = 'Paused %s'%progress + else: + try: + message = core.STATE_MESSAGES[state] + if state in (1, 3, 4, 7): + message = '%s %s'%(message, progress) + except IndexError: + message = '' + return message + + # UID, Q#, Name, Size, Progress, Message, Seeders, Peers, DL, UL, ETA, Share + def get_list_from_unique_id(self, unique_id): + state = self.manager.get_torrent_state(unique_id) + + queue = int(state['queue_pos']) + 1 + name = state['name'] + size = long(state['total_size']) + progress = float(state['progress'] * 100) + message = self.get_message_from_state(state) + seeds = int(state['num_seeds']) + seeds_t = int(state['total_seeds']) + peers = int(state['num_peers']) + peers_t = int(state['total_peers']) + dlrate = int(state['download_rate']) + ulrate = int(state['upload_rate']) + try: + eta = common.get_eta(state["total_size"], state["total_done"], state["download_rate"]) + except ZeroDivisionError: + eta = -1 + share = float(self.calc_share_ratio(unique_id, state)) + rlist = [int(unique_id), int(queue), str(name), long(size), float(progress), str(message), + int(seeds), int(seeds_t), int(peers), int(peers_t), int(dlrate), int(ulrate), int(eta), float(share)] + + return rlist + + + ## Start the timer that updates the interface + def start(self, hidden=False): + if not hidden: + self.window.show() + # go through torrent files to add + #dummy preferences values: + use_default_download_location = True + default_download_location = "." + for torrent_file in self.torrent_file_queue: + print "adding torrent", torrent_file + try: + self.interactive_add_torrent(torrent_file, append=False) + except core.DelugeError: + print "duplicate torrent found, ignoring", torrent_file + ## add torrents in manager to interface + # self.torrent_model.append([0, 1, "Hello, World", 2048, 50.0, "Hi", 1, 2, 1, 2, 2048, 2048, 120, 1.0]) + for uid in self.manager.get_unique_IDs(): + self.torrent_model.append(self.get_list_from_unique_id(uid)) + gobject.timeout_add(1000, self.update) + try: + self.is_running = True + gtk.main() + except KeyboardInterrupt: + self.manager.quit() + + ## Call via a timer to update the interface + def update(self): + # Make sure that the interface still exists + try: + tab = self.wtree.get_widget("torrent_info").get_current_page() + except AttributeError: + return False + + if self.something_screwed_up: + dialogs.show_popup_warning(self.window, + _("For some reason, the previous state could not be loaded, so a blank state has been loaded for you.")) + restore_torrents = dialogs.show_popup_question(self.window, + _("Would you like to attempt to reload the previous session's downloads?")) + if restore_torrents: + torrent_subdir = os.path.join(self.manager.base_dir, core.TORRENTS_SUBDIR) + for torrent in os.listdir(torrent_subdir): + if torrent.endswith('.torrent'): + self.interactive_add_torrent(torrent) + self.something_screwed_up = False + + # Update Statusbar and Tray Tips + core_state = self.manager.get_state() + connections = core_state['num_peers'] + dlrate = common.frate(core_state['download_rate']) + ulrate = common.frate(core_state['upload_rate']) + + self.statusbar_temp_msg = '%s: %s %s: %s %s: %s'%( + _('Connections'), connections, _('Download'), + dlrate, _('Upload'), ulrate) + + if 'DHT_nodes' in core_state.keys(): + dht_peers = core_state['DHT_nodes'] + if dht_peers == -1: + dht_peers = '?' + else: + dht_peers = str(dht_peers) + self.statusbar_temp_msg = self.statusbar_temp_msg + ' [DHT: %s]'%(dht_peers) + + msg = _("Deluge Bittorrent Client") + "\n" + \ + _("Connections") + ": " + str(connections) + "\n" + _("Download") + ": " + \ + dlrate + "\n" + _("Upload") + ": " + ulrate + + self.tray_icon.set_tooltip(msg) + + #Update any active plugins + self.plugins.update_active_plugins() + + # Put the generated message into the statusbar + # This gives plugins a chance to write to the + # statusbar if they want + self.statusbar.pop(1) + self.statusbar.push(1, self.statusbar_temp_msg) + + # If no torrent is selected, select the first torrent: + (temp, selection) = self.torrent_view.get_selection().get_selected() + if selection is None: + self.torrent_view.get_selection().select_path("0") + #Torrent List + itr = self.torrent_model.get_iter_first() + if itr is None: + return True + + while itr is not None: + uid = self.torrent_model.get_value(itr, 0) + try: + state = self.manager.get_torrent_state(uid) + tlist = self.get_list_from_unique_id(uid) + for i in range(len(tlist)): + try: + self.torrent_model.set_value(itr, i, tlist[i]) + except: + print "ERR", i, type(tlist[i]), tlist[i] + itr = self.torrent_model.iter_next(itr) + except core.InvalidUniqueIDError: + self.torrent_model.remove(itr) + if not self.torrent_model.iter_is_valid(itr): + itr = None + try: + if self.manager.is_user_paused(self.get_selected_torrent()): + self.wtree.get_widget("toolbutton_pause").set_stock_id(gtk.STOCK_MEDIA_PLAY) + self.wtree.get_widget("toolbutton_pause").set_label("Play") + else: + self.wtree.get_widget("toolbutton_pause").set_stock_id(gtk.STOCK_MEDIA_PAUSE) + self.wtree.get_widget("toolbutton_pause").set_label(_("Pause")) + + except KeyError: + pass + + try: + state = self.manager.get_torrent_state(self.get_selected_torrent()) + except core.InvalidUniqueIDError: + return True + + + if tab == 0: #Details Pane + self.wtree.get_widget("summary_name").set_text(state['name']) + self.text_summary_total_size.set_text(common.fsize(state["total_size"])) + self.text_summary_pieces.set_text(str(state["pieces"])) + self.text_summary_total_downloaded.set_text(common.fsize(state["total_done"])) + #self.text_summary_total_uploaded.set_text() + self.text_summary_download_rate.set_text(common.frate(state["download_rate"])) + self.text_summary_upload_rate.set_text(common.frate(state["upload_rate"])) + self.text_summary_seeders.set_text(common.fseed(state)) + self.text_summary_peers.set_text(common.fpeer(state)) + self.wtree.get_widget("progressbar").set_fraction(float(state['progress'])) + self.wtree.get_widget("progressbar").set_text(common.fpcnt(state["progress"])) + self.text_summary_share_ratio.set_text('%.3f'%(self.calc_share_ratio(self.get_selected_torrent(), state))) + self.text_summary_downloaded_this_session.set_text(common.fsize(state["total_download"])) + self.text_summary_uploaded_this_session.set_text(common.fsize(state["total_upload"])) + self.text_summary_tracker.set_text(str(state["tracker"])) + #self.text_summary_tracker_response.set_text(str(state[""])) + self.text_summary_tracker_status.set_text(str(state["tracker_ok"])) + self.text_summary_next_announce.set_text(str(state["next_announce"])) + #self.text_summary_compact_allocation.set_text(str(state[""])) + self.text_summary_eta.set_text(common.estimate_eta(state)) + elif tab == 1: #Peers List + def biographer(model, path, iter, dictionary): + assert(model.get_value(iter, 0) not in dictionary.keys()) + dictionary[model.get_value(iter, 0)] = model.get_string_from_iter(iter) + + class remover_data: + def __init__(self, new_ips): + self.new_ips = new_ips + self.removed = False + + def remover(model, path, iter, data): + if model.get_value(iter, 0) not in data.new_ips: + model.remove(iter) + data.removed = True + return True + else: + return False + + unique_id = self.get_selected_torrent() + + new_peer_info = self.manager.get_torrent_peer_info(unique_id) + + new_ips = {} + + for index in range(len(new_peer_info)): + if not new_peer_info[index]['client'] == "": + assert(new_peer_info[index]['ip'] not in new_ips.keys()) + new_ips[new_peer_info[index]['ip']] = index + + while True: + data = remover_data(new_ips.keys()) + self.peer_store.foreach(remover, data) + if not data.removed: + break + + curr_ips = {} + + self.peer_store.foreach(biographer, curr_ips) + + assert(self.peer_store.iter_n_children(None) == len(curr_ips.keys())) + + for peer in new_peer_info: + if peer['ip'] in curr_ips.keys(): + self.peer_store.set(self.peer_store.get_iter_from_string(curr_ips[peer['ip']]), + 1, unicode(peer['client'], 'Latin-1'), + 2, '%.2f%%'%peer["peer_has"], + 3, common.frate(peer["download_speed"]), + 4, common.frate(peer["upload_speed"])) + for peer in new_peer_info: + if peer['ip'] not in curr_ips.keys() and peer['client'] is not "": + self.peer_store.append([peer["ip"], + unicode(peer["client"], 'Latin-1'), + '%.2f%%'%peer["peer_has"], + common.frate(peer["download_speed"]), + common.frate(peer["upload_speed"])]) + #print new_ips + #print curr_ips + #print new_peer_info + del new_peer_info + del new_ips + del curr_ips + + + + elif tab == 2: #File List + pass + else: + pass + + return True + + def calc_share_ratio(self, unique_id, torrent_state): + r = float(self.manager.calc_ratio(unique_id, torrent_state)) + return r + + def get_selected_torrent(self): + try: + return self.torrent_model.get_value(self.torrent_view.get_selection().get_selected()[1], 0) + except TypeError: + return None + + def on_drag_data(self, widget, drag_context, x, y, selection_data, info, timestamp): + uri_split = selection_data.data.strip().split() + for uri in uri_split: + path = urllib.url2pathname(uri).strip('\r\n\x00') + if path.startswith('file:\\\\\\'): + path = path[8:] + elif path.startswith('file://'): + path = path[7:] + elif path.startswith('file:'): + path = path[5:] + if path.endswith('.torrent'): + self.interactive_add_torrent(path) + + + + def interactive_add_torrent(self, torrent, append=True): + if self.config.get('use_default_dir', bool, default=False): + path = self.config.get('default_download_path', default=os.path.expandvars('$HOME')) + else: + path = dialogs.show_directory_chooser_dialog(self.window) + if path is None: + return + try: + unique_id = self.manager.add_torrent(torrent, path, self.config.get('use_compact_storage', bool, default=False)) + + if append: + self.torrent_model.append(self.get_list_from_unique_id(unique_id)) + except core.InvalidEncodingError, e: + print "InvalidEncodingError", e + dialogs.show_popup_warning(self.window, _("An error occured while trying to add the torrent. It's possible your .torrent file is corrupted.")) + except core.DuplicateTorrentError, e: + dialogs.show_popup_warning(self.window, _("The torrent you've added seems to already be in Deluge.")) + except core.InsufficientFreeSpaceError, e: + nice_need = common.fsize(e.needed_space) + nice_free = common.fsize(e.free_space) + dialogs.show_popup_warning(self.window, _("There is not enough free disk space to complete your download.") + "\n" + \ + _("Space Needed:") + " " + nice_need + "\n" + \ + _("Available Space:") + " " + nice_free) + + + + + + + def add_torrent_clicked(self, obj=None): + torrent = dialogs.show_file_open_dialog() + if torrent is not None: + self.interactive_add_torrent(torrent) + + def add_torrent_url_clicked(self, obj=None): + dlg = gtk.Dialog(title=_("Add torrent from URL"), parent=self.window, + buttons=(gtk.STOCK_CANCEL, 0, gtk.STOCK_OK, 1)) + dlg.set_icon_from_file(common.get_pixmap("deluge32.png")) + + label = gtk.Label(_("Enter the URL of the .torrent to download")) + entry = gtk.Entry() + dlg.vbox.pack_start(label) + dlg.vbox.pack_start(entry) + dlg.show_all() + result = dlg.run() + url = entry.get_text() + dlg.destroy() + + if result == 1: + opener = urllib.URLopener() + filename, headers = opener.retrieve(url) + if filename.endswith(".torrent") or headers["content-type"]=="application/x-bittorrent": + self.interactive_add_torrent(filename) + + + def remove_torrent_clicked(self, obj=None): + torrent = self.get_selected_torrent() + if torrent is not None: + glade = gtk.glade.XML(common.get_glade_file("dgtkpopups.glade"), domain='deluge') + asker = glade.get_widget("remove_torrent_dlg") + + asker.set_icon_from_file(common.get_pixmap("deluge32.png")) + + warning = glade.get_widget("warning") + warning.set_text(" ") + + data_also = glade.get_widget("data_also") + data_also.connect("toggled", self.remove_toggle_warning, warning) + + response = asker.run() + asker.destroy() + if response == 1: + self.manager.remove_torrent(torrent, data_also.get_active()) + self.clear_details_pane() + + def clear_details_pane(self): + self.wtree.get_widget("progressbar").set_text("") + self.text_summary_total_size.set_text("") + self.text_summary_pieces.set_text("") + self.text_summary_total_downloaded.set_text("") + self.text_summary_total_uploaded.set_text("") + self.text_summary_download_rate.set_text("") + self.text_summary_upload_rate.set_text("") + self.text_summary_seeders.set_text("") + self.text_summary_peers.set_text("") + self.wtree.get_widget("progressbar").set_fraction(0.0) + self.text_summary_share_ratio.set_text("") + self.text_summary_downloaded_this_session.set_text("") + self.text_summary_uploaded_this_session.set_text("") + self.text_summary_tracker.set_text("") + self.text_summary_tracker_response.set_text("") + self.text_summary_tracker_status.set_text("") + self.text_summary_next_announce.set_text("") + self.text_summary_compact_allocation.set_text("") + self.text_summary_eta.set_text("") + self.peer_store.clear() + self.file_store.clear() + + + def remove_toggle_warning(self, args, warning): + if not args.get_active(): + warning.set_text(" ") + else: + warning.set_markup("" + _("Warning - all downloaded files for this torrent will be deleted!") + "") + return False + + def update_tracker(self, obj=None): + torrent = self.get_selected_torrent() + if torrent is not None: + self.manager.update_tracker(torrent) + + def clear_finished(self, obj=None): + print "Clearing Completed Torrents" + self.manager.clear_completed() + + def q_torrent_up(self, obj=None): + torrent = self.get_selected_torrent() + if torrent is not None: + self.manager.queue_up(torrent) + + def q_torrent_down(self, obj=None): + torrent = self.get_selected_torrent() + if torrent is not None: + self.manager.queue_down(torrent) + + def q_to_bottom(self, widget): + torrent = self.get_selected_torrent() + if torrent is not None: + self.manager.queue_bottom(torrent) + + def toolbar_toggle(self, widget): + if widget.get_active(): + self.wtree.get_widget("tb_left").show() + self.wtree.get_widget("tb_middle").show() + self.wtree.get_widget("tb_right").show() + else: + self.wtree.get_widget("tb_left").hide() + self.wtree.get_widget("tb_middle").hide() + self.wtree.get_widget("tb_right").hide() + + def infopane_toggle(self, widget): + if widget.get_active(): + self.wtree.get_widget("torrent_info").show() + else: + self.wtree.get_widget("torrent_info").hide() + + def size_toggle(self, obj): + self.size_column.set_visible(obj.get_active()) + + + def status_toggle(self, obj): + self.status_column.set_visible(obj.get_active()) + + def seeders_toggle(self, obj): + self.seed_column.set_visible(obj.get_active()) + + def peers_toggle(self, obj): + self.peer_column.set_visible(obj.get_active()) + + def dl_toggle(self, obj): + self.dl_column.set_visible(obj.get_active()) + + def ul_toggle(self, obj): + self.ul_column.set_visible(obj.get_active()) + + def eta_toggle(self, obj): + self.eta_column.set_visible(obj.get_active()) + + def share_toggle(self, obj): + self.share_column.set_visible(obj.get_active()) + + def load_window_settings(self): + self.wtree.get_widget("chk_infopane").set_active(self.config.get("show_infopane", bool, default=True)) + self.wtree.get_widget("chk_toolbar").set_active(self.config.get("show_toolbar", bool, default=True)) + self.wtree.get_widget("chk_size").set_active(self.config.get("show_size", bool, default=True)) + self.wtree.get_widget("chk_status").set_active(self.config.get("show_status", bool, default=True)) + self.wtree.get_widget("chk_seed").set_active(self.config.get("show_seeders", bool, default=True)) + self.wtree.get_widget("chk_peer").set_active(self.config.get("show_peers", bool, default=True)) + self.wtree.get_widget("chk_download").set_active(self.config.get("show_dl", bool, default=True)) + self.wtree.get_widget("chk_upload").set_active(self.config.get("show_ul", bool, default=True)) + self.wtree.get_widget("chk_eta").set_active(self.config.get("show_eta", bool, default=True)) + self.wtree.get_widget("chk_ratio").set_active(self.config.get("show_share", bool, default=True)) + + def save_window_settings(self): + self.config.set("show_infopane", self.wtree.get_widget("chk_infopane").get_active()) + self.config.set("show_toolbar", self.wtree.get_widget("chk_toolbar").get_active()) + self.config.set("show_size", self.size_column.get_visible()) + self.config.set("show_status", self.status_column.get_visible()) + self.config.set("show_seeders", self.seed_column.get_visible()) + self.config.set("show_peers", self.peer_column.get_visible()) + self.config.set("show_dl", self.dl_column.get_visible()) + self.config.set("show_ul", self.ul_column.get_visible()) + self.config.set("show_eta", self.eta_column.get_visible()) + self.config.set("show_share", self.share_column.get_visible()) + + def save_window_geometry(self): + x, y = self.window.get_position() + w, h = self.window.get_size() + self.config.set('window_x_pos', x) + self.config.set('window_y_pos', y) + self.config.set('window_width', w) + self.config.set('window_height',h) + + def load_window_geometry(self): + x = self.config.get('window_x_pos', int, default=0) + y = self.config.get('window_y_pos', int, default=0) + w = self.config.get('window_width', int, default=640) + h = self.config.get('window_height',int, default=480) + self.window.move(x, y) + self.window.resize(w, h) + + + def close(self, widget, event): + if self.config.get("close_to_tray", bool, default=False) and self.config.get("enable_system_tray", bool, default=True) and self.has_tray: + self.save_window_geometry() + self.window.hide() + return True + else: + self.quit() + + def quit(self, widget=None): + if self.window.get_property("visible"): + self.save_window_geometry() + self.window.hide() + self.shutdown() + + else: + if self.config.get("lock_tray", bool, default=False) == True: + self.unlock_tray("quitus") + else: + self.save_window_geometry() + self.window.hide() + self.shutdown() + + def shutdown(self): + enabled_plugins = ':'.join(self.plugins.get_enabled_plugins()) + self.config.set('enabled_plugins', enabled_plugins) + self.save_window_settings() + self.config.save_to_file(self.conf_file) + self.plugins.shutdown_all_plugins() + self.manager.quit() + gtk.main_quit() + + + +## For testing purposes, create a copy of the interface +if __name__ == "__main__": + interface = DelugeGTK() + interface.start() + diff --git a/encryption/src/ipc_manager.py b/encryption/src/ipc_manager.py new file mode 100644 index 000000000..3a5e9fb9f --- /dev/null +++ b/encryption/src/ipc_manager.py @@ -0,0 +1,60 @@ +# ipc_manager.py +# +# Copyright (C) Zach Tibbitts 2006 +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, write to: +# The Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor +# Boston, MA 02110-1301, USA. + + +# Code for dbus_importing borrowed from Listen (http://listen-project.org) +# I couldn't figure out how to use dbus without breaking on versions past +# 0.80.0. I finally found a solution by reading the source code from the +# Listen project. +try: + import dbus, dbus.service + dbus_version = getattr(dbus, 'version', (0,0,0)) + if dbus_version >= (0,41,0) and dbus_version < (0,80,0): + dbus.SessionBus() + import dbus.glib + elif dbus_version >= (0,80,0): + from dbus.mainloop.glib import DBusGMainLoop + DBusGMainLoop(set_as_default=True) + dbus.SessionBus() + else: + pass +except: dbus_imported = False +else: dbus_imported = True + +if dbus_imported: + class Manager(dbus.service.Object): + def __init__(self, interface, object_path='/org/deluge_torrent/DelugeObject'): + self.interface = interface + self.bus = dbus.SessionBus() + bus_name = dbus.service.BusName("org.deluge_torrent.Deluge", bus=self.bus) + dbus.service.Object.__init__(self, bus_name, object_path) + + ## external_add_torrent should only be called from outside the class + @dbus.service.method('org.deluge_torrent.Deluge') + def external_add_torrent(self, torrent_file): + self.interface.external_add_torrent(torrent_file) +else: + # This is a fallback class in case dbus is not available + class Manager: + def __init__(self, interface, object_path=None): + self.interface = interface + + def external_add_torrent(self, torrent_file): + print "I can't do anything with this." diff --git a/encryption/src/plugins.py b/encryption/src/plugins.py new file mode 100644 index 000000000..912f2cc8c --- /dev/null +++ b/encryption/src/plugins.py @@ -0,0 +1,90 @@ +#!/usr/bin/env python +# +# delugeplugins.py +# +# Copyright (C) Zach Tibbitts 2006 +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, write to: +# The Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor +# Boston, MA 02110-1301, USA. + +import os + +class PluginManager: + def __init__(self, deluge_core, deluge_interface): + self.plugin_dirs = [] + self.available_plugins = {} + self.enabled_plugins = {} + self.core = deluge_core + self.interface = deluge_interface + + def add_plugin_dir(self, directory): + self.plugin_dirs.append(directory) + + def scan_for_plugins(self): + register_plugin = self.register_plugin + for folder in self.plugin_dirs: + plugin_folders = os.listdir(folder) + for plugin in plugin_folders: + if os.path.isfile(os.path.join(folder, plugin, "plugin.py")): + self.path = os.path.join(folder, plugin) + execfile(os.path.join(folder, plugin, "plugin.py")) + + def get_available_plugins(self): + return self.available_plugins.keys() + + def get_plugin(self, name): + return self.available_plugins[name] + + def enable_plugin(self, name): + self.enabled_plugins[name] = self.available_plugins[name]['class']( + self.available_plugins[name]['path'], self.core, self.interface) + + def get_enabled_plugins(self): + return self.enabled_plugins.keys() + + def disable_plugin(self, name): + self.enabled_plugins[name].unload() + self.enabled_plugins.pop(name) + + def configure_plugin(self, name): + self.enabled_plugins[name].configure() + + def update_active_plugins(self): + for name in self.enabled_plugins.keys(): + self.enabled_plugins[name].update() + + def shutdown_all_plugins(self): + for name in self.enabled_plugins.keys(): + self.enabled_plugins[name].unload() + self.enabled_plugins.clear() + + def register_plugin(self, name, plugin_class, author, version, description, config=False): + self.available_plugins[name] = {'class': plugin_class, + 'author': author, + 'version': version, + 'description': description, + 'config': config, + 'path': self.path} + +## Few lines of code to test functionality +if __name__ == "__main__": + p = PluginManager() + p.add_plugin_dir("plugins/") + p.scan_for_plugins() + for x in p.plugins: + print x + for y in p.plugins[x]: + print "\t", y diff --git a/encryption/src/pref.py b/encryption/src/pref.py new file mode 100644 index 000000000..7366f8fa5 --- /dev/null +++ b/encryption/src/pref.py @@ -0,0 +1,129 @@ +# pref.py +# +# Copyright (C) Zach Tibbitts 2006 +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, write to: +# The Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor +# Boston, MA 02110-1301, USA. + + +# Preferences is basically a wrapper around a simple Python dictionary +# object. However, this class provides a few extra features on top of +# the built in class that Deluge can take advantage of. +class Preferences: + def __init__(self, filename=None, defaults=None): + self.mapping = {} + self.config_file = filename + if self.config_file is not None: + self.load_from_file(self.config_file) + if defaults is not None: + for key in defaults.keys(): + self.mapping.setdefault(key, defaults[key]) + + # Allows you to access an item in a Preferences objecy by calling + # instance[key] rather than instance.get(key). However, this will + # return the value as the type it is currently in memory, so it is + # advisable to use get() if you need the value converted. + def __getitem__(self, key): + return self.mapping[key] + + def __setitem__(self, key, value): + self.mapping[key] = value + + def __delitem__(self, key): + del self.mapping[key] + + def __len__(self): + return len(self.mapping) + + def has_key(self, key): return self.mapping.has_key(key) + def items(self): return self.mapping.items() + def keys(self): return self.mapping.keys() + def values(self): return self.mapping.values() + + def save_to_file(self, filename=None): + if filename is None: + filename = self.config_file + f = open(filename, mode='w') + keys = self.mapping.keys() + keys.sort() + for key in keys: + f.write(key) + f.write(' = ') + f.write(str(self.mapping[key])) + f.write('\n') + f.flush() + f.close() + self.config_file = filename + + def load_from_file(self, filename=None): + if filename is None: + filename = self.config_file + f = open(filename, mode='r') + for line in f: + try: + (key, value) = line.split('=', 1) + key = key.strip(' \n') + value = value.strip(' \n') + self.mapping[key] = value + except ValueError: + pass + f.close() + self.config_file = filename + + def set(self, key, value): + self.mapping[key] = value + + def get(self, key, kind=None, default=None): + if not key in self.mapping.keys(): + if default is not None: + self.mapping[key] = default + return default + else: + raise KeyError + result = self.mapping[key] + if kind == None: + pass + elif kind == bool: + if isinstance(result, str): + result = not (result.lower() == "false") + elif isinstance(result, int): + result = not (result == 0) + else: + result = False + elif kind == int: + try: + result = int(result) + except ValueError: + result = int(float(result)) + elif kind == float: + result = float(result) + elif kind == str: + result = str(result) + else: + pass + + return result + + def remove(self, key): + self.mapping.pop(key) + + def clear(self): + self.mapping.clear() + + def printout(self): + for key in self.mapping.keys(): + print key, ':', self.mapping[key] +