summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDmitrijs Ledkovs <dmitrij.ledkov@ubuntu.com>2013-07-05 23:23:01 +0100
committerDmitrijs Ledkovs <dmitrij.ledkov@ubuntu.com>2013-07-05 23:23:01 +0100
commitf589802f2992a2d0691caf8e3a4cc334f2289251 (patch)
treefd4d3688cc7a754284cc773b6dde364cc356a8e8
dc-qt (0.2.0.alpha-4.2) unstable; urgency=low
* Non-maintainer upload. * Update boost-linking patch, to link with boost1.53 (Closes: #709254) # imported from the archive
-rw-r--r--AUTHORS3
-rw-r--r--COPYING345
-rw-r--r--README51
-rw-r--r--SConstruct66
-rw-r--r--backend/BackendUtil.h27
-rw-r--r--backend/ClientNotifier.cpp497
-rw-r--r--backend/ClientNotifier.h88
-rw-r--r--backend/CommonTypes.h47
-rw-r--r--backend/IdAssigner.h58
-rw-r--r--backend/RpcArgumentFactory.h151
-rw-r--r--backend/SConscript10
-rw-r--r--backend/Selecter.cpp72
-rw-r--r--backend/Selecter.h25
-rw-r--r--backend/Session.cpp341
-rw-r--r--backend/Session.h91
-rw-r--r--backend/SessionManager.cpp190
-rw-r--r--backend/SessionManager.h100
-rw-r--r--backend/SettingsMapper.cpp118
-rw-r--r--backend/SettingsMapper.h26
-rw-r--r--backend/Stack.h52
-rw-r--r--backend/TransferManager.cpp282
-rw-r--r--backend/TransferManager.h75
-rw-r--r--backend/User.h21
-rw-r--r--backend/commandhandlers.cpp554
-rw-r--r--backend/commandhandlers.h375
-rw-r--r--backend/filelog.cpp65
-rw-r--r--backend/filelog.h36
-rw-r--r--backend/log.h35
-rw-r--r--backend/main.cpp250
-rw-r--r--dcpp/ADLSearch.cpp327
-rw-r--r--dcpp/ADLSearch.h313
-rw-r--r--dcpp/AdcCommand.cpp157
-rw-r--r--dcpp/AdcCommand.h210
-rw-r--r--dcpp/AdcHub.cpp432
-rw-r--r--dcpp/AdcHub.h106
-rw-r--r--dcpp/BZUtils.cpp106
-rw-r--r--dcpp/BZUtils.h71
-rw-r--r--dcpp/BitInputStream.h73
-rw-r--r--dcpp/BitOutputStream.h65
-rw-r--r--dcpp/BloomFilter.h98
-rw-r--r--dcpp/BufferedSocket.cpp441
-rw-r--r--dcpp/BufferedSocket.h218
-rw-r--r--dcpp/CID.h70
-rw-r--r--dcpp/Client.cpp104
-rw-r--r--dcpp/Client.h202
-rw-r--r--dcpp/ClientManager.cpp360
-rw-r--r--dcpp/ClientManager.h154
-rw-r--r--dcpp/ClientManagerListener.h48
-rw-r--r--dcpp/ConnectionManager.cpp695
-rw-r--r--dcpp/ConnectionManager.h163
-rw-r--r--dcpp/ConnectionManagerListener.h50
-rw-r--r--dcpp/CriticalSection.h185
-rw-r--r--dcpp/CryptoManager.cpp405
-rw-r--r--dcpp/CryptoManager.h131
-rw-r--r--dcpp/DCPlusPlus.cpp121
-rw-r--r--dcpp/DCPlusPlus.h132
-rw-r--r--dcpp/DirectoryListing.cpp336
-rw-r--r--dcpp/DirectoryListing.h177
-rw-r--r--dcpp/DownloadManager.cpp891
-rw-r--r--dcpp/DownloadManager.h281
-rw-r--r--dcpp/Encoder.cpp109
-rw-r--r--dcpp/Encoder.h45
-rw-r--r--dcpp/Exception.cpp28
-rw-r--r--dcpp/Exception.h61
-rw-r--r--dcpp/FastAlloc.h105
-rw-r--r--dcpp/FavoriteUser.h49
-rw-r--r--dcpp/File.h426
-rw-r--r--dcpp/FilteredFile.h191
-rw-r--r--dcpp/FinishedManager.cpp99
-rw-r--r--dcpp/FinishedManager.h112
-rw-r--r--dcpp/HashManager.cpp678
-rw-r--r--dcpp/HashManager.h283
-rw-r--r--dcpp/HashValue.h69
-rw-r--r--dcpp/HttpConnection.cpp163
-rw-r--r--dcpp/HttpConnection.h94
-rw-r--r--dcpp/HubManager.cpp604
-rw-r--r--dcpp/HubManager.h274
-rw-r--r--dcpp/LogManager.cpp27
-rw-r--r--dcpp/LogManager.h116
-rw-r--r--dcpp/MerkleCheckOutputStream.h111
-rw-r--r--dcpp/MerkleTree.h233
-rw-r--r--dcpp/NmdcHub.cpp723
-rw-r--r--dcpp/NmdcHub.h269
-rw-r--r--dcpp/Pointer.h171
-rw-r--r--dcpp/QueueItem.h288
-rw-r--r--dcpp/QueueManager.cpp1388
-rw-r--r--dcpp/QueueManager.h256
-rw-r--r--dcpp/QueueManagerListener.h56
-rw-r--r--dcpp/ResourceManager.cpp73
-rw-r--r--dcpp/ResourceManager.h82
-rw-r--r--dcpp/SConscript7
-rw-r--r--dcpp/SFVReader.cpp105
-rw-r--r--dcpp/SFVReader.h56
-rw-r--r--dcpp/SearchManager.cpp344
-rw-r--r--dcpp/SearchManager.h206
-rw-r--r--dcpp/SearchManagerListener.h41
-rw-r--r--dcpp/Semaphore.h103
-rw-r--r--dcpp/ServerSocket.cpp59
-rw-r--r--dcpp/ServerSocket.h73
-rw-r--r--dcpp/SettingsManager.cpp374
-rw-r--r--dcpp/SettingsManager.h201
-rw-r--r--dcpp/ShareManager.cpp1484
-rw-r--r--dcpp/ShareManager.h327
-rw-r--r--dcpp/SimpleXML.cpp338
-rw-r--r--dcpp/SimpleXML.h289
-rw-r--r--dcpp/Singleton.h64
-rw-r--r--dcpp/Socket.cpp590
-rw-r--r--dcpp/Socket.h230
-rw-r--r--dcpp/Speaker.h124
-rw-r--r--dcpp/Streams.h195
-rw-r--r--dcpp/StringDefs.cpp1121
-rw-r--r--dcpp/StringDefs.h566
-rw-r--r--dcpp/StringSearch.h117
-rw-r--r--dcpp/StringTokenizer.cpp28
-rw-r--r--dcpp/StringTokenizer.h66
-rw-r--r--dcpp/Text.cpp285
-rw-r--r--dcpp/Text.h127
-rw-r--r--dcpp/Thread.cpp46
-rw-r--r--dcpp/Thread.h159
-rw-r--r--dcpp/TigerHash.cpp717
-rw-r--r--dcpp/TigerHash.h80
-rw-r--r--dcpp/TimerManager.cpp52
-rw-r--r--dcpp/TimerManager.h95
-rw-r--r--dcpp/UploadManager.cpp447
-rw-r--r--dcpp/UploadManager.h202
-rw-r--r--dcpp/User.cpp218
-rw-r--r--dcpp/User.h150
-rw-r--r--dcpp/UserCommand.h83
-rw-r--r--dcpp/UserConnection.cpp188
-rw-r--r--dcpp/UserConnection.h405
-rw-r--r--dcpp/Util.cpp882
-rw-r--r--dcpp/Util.h592
-rw-r--r--dcpp/ZUtils.cpp111
-rw-r--r--dcpp/ZUtils.h87
-rw-r--r--dcpp/atomic-ppc.h94
-rw-r--r--dcpp/atomic-x86_64.h39
-rw-r--r--dcpp/atomic.h56
-rw-r--r--dcpp/config.h127
-rw-r--r--dcpp/stdinc.cpp36
-rw-r--r--dcpp/stdinc.h124
-rw-r--r--dcpp/version.h28
-rw-r--r--debian/changelog58
-rw-r--r--debian/compat1
-rw-r--r--debian/control24
-rw-r--r--debian/copyright31
-rw-r--r--debian/dc-qt.desktop8
-rw-r--r--debian/dc-qt.xpm48
-rw-r--r--debian/dirs1
-rw-r--r--debian/menu7
-rw-r--r--debian/patches/10-path.patch14
-rw-r--r--debian/patches/20-gcc-fix.patch29
-rw-r--r--debian/patches/30-libboost-linking.patch34
-rw-r--r--debian/patches/40_gcc43_compat.diff32
-rw-r--r--debian/patches/50_gcc44_compat.diff12
-rwxr-xr-xdebian/rules28
-rw-r--r--rpcdriver/SConscript10
-rw-r--r--rpcdriver/commanddispatcher.cpp266
-rw-r--r--rpcdriver/commanddispatcher.h130
-rw-r--r--rpcdriver/datainputstream.cpp106
-rw-r--r--rpcdriver/datainputstream.h58
-rw-r--r--rpcdriver/dataoutputstream.cpp49
-rw-r--r--rpcdriver/dataoutputstream.h43
-rw-r--r--rpcdriver/inputbuffer.h83
-rw-r--r--rpcdriver/main.cpp62
-rw-r--r--rpcdriver/outputbuffer.h57
-rw-r--r--rpcdriver/protectedbuffer.h86
-rw-r--r--rpcdriver/rpccommandhandler.h81
-rw-r--r--rpcdriver/rpcdriver.cpp510
-rw-r--r--rpcdriver/rpcdriver.h228
-rw-r--r--rpcdriver/rpcexception.h37
-rw-r--r--rpcdriver/socket.cpp138
-rw-r--r--rpcdriver/socket.h53
-rw-r--r--rpcdriver/socketlistener.h26
-rw-r--r--rpcdriver/socketmanager.cpp72
-rw-r--r--rpcdriver/socketmanager.h57
-rw-r--r--rpcdriver/types.h22
-rw-r--r--ui/backendconnection.cpp285
-rw-r--r--ui/backendconnection.h73
-rw-r--r--ui/blockallocator.h102
-rw-r--r--ui/commandhandlers.cpp807
-rw-r--r--ui/commandhandlers.h420
-rw-r--r--ui/commandhandlersinclude.h16
-rw-r--r--ui/connectdialog.cpp67
-rw-r--r--ui/connectdialog.h50
-rw-r--r--ui/connectdialog.ui64
-rw-r--r--ui/createfavourite.ui144
-rw-r--r--ui/createfavouritedialog.cpp34
-rw-r--r--ui/createfavouritedialog.h37
-rw-r--r--ui/exceptions.h43
-rw-r--r--ui/favouritehublist.ui80
-rw-r--r--ui/favouritehubswidget.cpp192
-rw-r--r--ui/favouritehubswidget.h148
-rw-r--r--ui/filelog.cpp136
-rw-r--r--ui/filelog.h43
-rw-r--r--ui/filetransfer.h59
-rw-r--r--ui/finisheditem.h30
-rw-r--r--ui/finishedmodel.cpp92
-rw-r--r--ui/finishedmodel.h36
-rw-r--r--ui/global.h12
-rw-r--r--ui/globalusermodel.cpp51
-rw-r--r--ui/globalusermodel.h42
-rw-r--r--ui/hub.ui72
-rw-r--r--ui/images/arrows.bmpbin0 -> 1592 bytes
-rw-r--r--ui/images/browse.pngbin0 -> 792 bytes
-rw-r--r--ui/images/dldir.pngbin0 -> 1080 bytes
-rw-r--r--ui/images/dldirto.pngbin0 -> 851 bytes
-rw-r--r--ui/images/download.pngbin0 -> 306 bytes
-rw-r--r--ui/images/downloadto.pngbin0 -> 424 bytes
-rw-r--r--ui/images/exit.pngbin0 -> 3110 bytes
-rw-r--r--ui/images/filelist.pngbin0 -> 632 bytes
-rw-r--r--ui/images/folders.bmpbin0 -> 2358 bytes
-rw-r--r--ui/images/publichubs.pngbin0 -> 1018 bytes
-rw-r--r--ui/images/remove.pngbin0 -> 888 bytes
-rw-r--r--ui/images/search.pngbin0 -> 792 bytes
-rw-r--r--ui/images/settings.pngbin0 -> 993 bytes
-rw-r--r--ui/images/toolbar.bmpbin0 -> 16184 bytes
-rw-r--r--ui/images/toolbar20-highlight.bmpbin0 -> 18056 bytes
-rw-r--r--ui/images/toolbar20.bmpbin0 -> 18056 bytes
-rw-r--r--ui/images/tthsearch.pngbin0 -> 987 bytes
-rw-r--r--ui/images/user-dcpp-op.pngbin0 -> 738 bytes
-rw-r--r--ui/images/user-dcpp-passive-op.pngbin0 -> 658 bytes
-rw-r--r--ui/images/user-dcpp-passive.pngbin0 -> 406 bytes
-rw-r--r--ui/images/user-dcpp.pngbin0 -> 594 bytes
-rw-r--r--ui/images/user-normal-op.pngbin0 -> 738 bytes
-rw-r--r--ui/images/user-normal.pngbin0 -> 608 bytes
-rw-r--r--ui/images/user-passive-op.pngbin0 -> 646 bytes
-rw-r--r--ui/images/user-passive.pngbin0 -> 420 bytes
-rw-r--r--ui/images/users.bmpbin0 -> 6200 bytes
-rw-r--r--ui/log.h37
-rw-r--r--ui/main.cpp54
-rw-r--r--ui/mainwindow.cpp453
-rw-r--r--ui/mainwindow.h81
-rw-r--r--ui/mainwindow.ui210
-rw-r--r--ui/nicetreeview.cpp61
-rw-r--r--ui/nicetreeview.h38
-rw-r--r--ui/publichublist.ui67
-rw-r--r--ui/publichubswidget.cpp142
-rw-r--r--ui/publichubswidget.h134
-rw-r--r--ui/queueitem.h30
-rw-r--r--ui/queuemodel.cpp125
-rw-r--r--ui/queuemodel.h53
-rw-r--r--ui/res.qrc24
-rw-r--r--ui/rpctypes.h85
-rw-r--r--ui/search.ui294
-rw-r--r--ui/searchdock.ui58
-rw-r--r--ui/searchentry.h238
-rw-r--r--ui/searchmanager.cpp143
-rw-r--r--ui/searchmanager.h79
-rw-r--r--ui/searchtablemodel.cpp211
-rw-r--r--ui/searchtablemodel.h139
-rw-r--r--ui/searchwidget.cpp165
-rw-r--r--ui/searchwidget.h57
-rw-r--r--ui/session.cpp117
-rw-r--r--ui/session.h73
-rw-r--r--ui/sessionmanager.cpp124
-rw-r--r--ui/sessionmanager.h66
-rw-r--r--ui/settings.ui536
-rw-r--r--ui/settingsdialog.cpp215
-rw-r--r--ui/settingsdialog.h71
-rw-r--r--ui/shareitemmodel.cpp120
-rw-r--r--ui/shareitemmodel.h96
-rw-r--r--ui/transferlistitemdelegate.cpp155
-rw-r--r--ui/transferlistitemdelegate.h38
-rw-r--r--ui/transferlistmodel.cpp158
-rw-r--r--ui/transferlistmodel.h60
-rw-r--r--ui/ui.pro82
-rw-r--r--ui/user.h47
-rw-r--r--ui/userfiledialog.cpp148
-rw-r--r--ui/userfiledialog.h55
-rw-r--r--ui/userfilelisting.ui75
-rw-r--r--ui/userfilemodel.cpp235
-rw-r--r--ui/userfilemodel.h315
-rw-r--r--ui/userlistmodel.cpp141
-rw-r--r--ui/userlistmodel.h85
-rw-r--r--ui/util.h30
275 files changed, 42550 insertions, 0 deletions
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000..bb42c1e
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,3 @@
+Rikard Björklind (olof@users.sourceforge.net)
+Mikael Gransell (mickeg@users.sourceforge.net)
+Arsenij Vodjanov (arsenij@gmail.com)
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..f689608
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,345 @@
+ 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.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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.
+
+ <signature of Ty Coon>, 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/README b/README
new file mode 100644
index 0000000..481a9b2
--- /dev/null
+++ b/README
@@ -0,0 +1,51 @@
+--------------------------------
+README for dcqt backend / qt gui
+--------------------------------
+
+dcqt is a dc++ client for Linux and MacOS.
+Binaries for various platforms can be found at dc-qt.sf.net, and is
+the recommended way of installing this program.
+
+DEPENDENCIES
+------------
+We try to keep the number of required libraries at a minimum.
+To run dcqt you need the following libraries:
+
+libz
+libbz2
+boost (threads, filesystem, ??)
+
+For the GUI you also need qt 4.x (4.1.x is highly recommended).
+
+Qt4 is not yet available in some Linux distributions, for example Fedora Core.
+In case you can't find any binary Qt4 packages, you have to download the
+source and build Qt4 yourself.
+
+To build dcqt from source, you also need the install the "dev" versions of
+the above libraries.
+
+USING THE BINARY DISTRIBUTION FOR OS X
+---------------------------------------
+Insert text here :)
+
+
+USING THE BINARY DISTRIBUTION FOR LINUX
+---------------------------------------
+Simply untar, enter the directory, and run.
+If you want to install the application, copy the binary files
+to /usr/local/bin or some other suitable location :).
+
+
+BUILDING
+--------
+First, see to it that you have the above libraries installed.
+1. Enter the directory which contains the file called SConstruct.
+2. Type "scons"
+3. Wait.
+4. The backend is now built.
+5. Go into the "gui" directory.
+6. Check that you have the qt4 bin directory in your path. This is IMPORTANT!
+7. Type "qmake && make"
+8. Done!
+
+Hoho
diff --git a/SConstruct b/SConstruct
new file mode 100644
index 0000000..e6adab0
--- /dev/null
+++ b/SConstruct
@@ -0,0 +1,66 @@
+#
+# dc-qt build file
+#
+# DOES NOT BUILD THE UI YET!!!
+#
+# targets:
+# no arguments - build
+# install - install backend and ui
+#
+# Arguments:
+# PREFIX - where to install, defaults to /usr/local
+# release=1 - build with optimiztion, turn off debug
+#
+
+import os
+
+env = Environment(CPPPATH = ['#rpcdriver/'], LIBPATH = ['#rpcdriver/'])
+
+# Configure release / debug compiler parameters
+release = ARGUMENTS.get('release',0)
+if int(release):
+ env.Append(CXXFLAGS = '-O2')
+ env.Append(LDFLAGS = '-s')
+else:
+ env.Append(CXXFLAGS = '-g -ggdb -O0 -DDEBUG -Wall')
+ env.Append(LDFLAGS = '-g -ggdb')
+
+
+# DistCC rulz.
+if 'DISTCC_HOSTS' in os.environ:
+ env['ENV']['DISTCC_HOSTS'] = os.environ['DISTCC_HOSTS']
+ env['CC'] = 'distcc %s' % env['CC']
+ env['CXX'] = 'distcc %s' % env['CXX']
+ env['ENV']['PATH'] = os.environ['PATH']
+ env['ENV']['HOME'] = os.environ['HOME']
+
+# Architecture detection
+cpu = os.uname()[4]
+if cpu=='i386' or cpu=='i486' or cpu=='i586' or cpu=='i686':
+ env.Append(CXXFLAGS='-DTARGET_I386')
+ env.Append(CXXFLAGS = '-DHAVE_ASM_ATOMIC_H')
+
+if cpu=='Power Macintosh':
+ env.Append(CXXFLAGS='-DTARGET_PPC')
+ env.Append(CXXFLAGS='-DHAVE_ASM_ATOMIC_H')
+
+# x86_64 is the string for amd, handle it here.
+if cpu=='x86_64':
+ env.Append(CXXFLAGS='-m64')
+ env.Append(CXXFLAGS='-DTARGET_X86_64')
+ env.Append(CXXFLAGS='-DHAVE_ASM_ATOMIC_H')
+
+# Some misc stuff
+env.Append(CXXFLAGS = ['-I.', '-DENABLE_BINRELOC', '-D_FILE_OFFSET_BITS=64'])
+
+prefix = ARGUMENTS.get('PREFIX','/usr/local')
+bindir = ARGUMENTS.get('BINDIR', prefix + '/bin' )
+
+Export('env')
+SConscript('dcpp/SConscript')
+SConscript('backend/SConscript')
+SConscript('rpcdriver/SConscript')
+
+env.Install(dir=bindir,source='backend/backend')
+env.Install(dir=bindir,source='ui/ui')
+env.Alias('install',[bindir])
diff --git a/backend/BackendUtil.h b/backend/BackendUtil.h
new file mode 100644
index 0000000..eff6bbc
--- /dev/null
+++ b/backend/BackendUtil.h
@@ -0,0 +1,27 @@
+#ifndef BACKENDUTIL_H__
+#define BACKENDUTIL_H__
+
+// Util functions
+class BackendUtil {
+public:
+ static string linuxSeparator (const string &ps)
+ {
+ string str = ps;
+ for (string::iterator it=str.begin (); it != str.end (); it++)
+ if ((*it) == '\\')
+ (*it) = '/';
+ return str;
+ }
+
+
+ static string windowsSeparator (const string &ps)
+ {
+ string str = ps;
+ for (string::iterator it=str.begin (); it != str.end (); it++)
+ if ((*it) == '/')
+ (*it) = '\\';
+ return str;
+ }
+};
+
+#endif
diff --git a/backend/ClientNotifier.cpp b/backend/ClientNotifier.cpp
new file mode 100644
index 0000000..f938deb
--- /dev/null
+++ b/backend/ClientNotifier.cpp
@@ -0,0 +1,497 @@
+#include "ClientNotifier.h"
+#include "SessionManager.h"
+
+#include "RpcArgumentFactory.h"
+#include "CommonTypes.h"
+#include "log.h"
+#include <iostream>
+#include <boost/thread/recursive_mutex.hpp>
+#include <boost/format.hpp>
+
+using namespace rpc;
+
+namespace dcqt_backend
+{
+
+ClientNotifier* ClientNotifier::inst = 0;
+
+#define FOR_ALL_CLIENTS(c) driver->queueCommand( -1, c );
+
+void ClientNotifier::connected(int aSessionId)
+{
+ //XmlRpcValue args,result;
+ //args[0] = aSessionId;
+
+ //FOR_ALL_CLIENTS(hubConnected);
+
+ CmdPtr cmd(new list<boost::any>);
+
+ cmd->push_back( string("hubConnected") );
+ cmd->push_back( aSessionId );
+
+ FOR_ALL_CLIENTS(cmd);
+}
+
+void ClientNotifier::connectionFailed(int session,const string& reason)
+{
+ // XmlRpcValue args,result;
+ // args[0] = session;
+ // args[1] = reason;
+ // FOR_ALL_CLIENTS(failed);
+ CmdPtr cmd(new list<boost::any>);
+ cmd->push_back( string("connectionFailed") );
+ cmd->push_back( session );
+ cmd->push_back( reason );
+
+ FOR_ALL_CLIENTS(cmd);
+}
+
+void ClientNotifier::hubUpdated(int aSessionId,const std::string& hubName)
+{
+ // XmlRpcValue args,result;
+ // args[0] = aSessionId;
+ // args[1] = hubName;
+ // FOR_ALL_CLIENTS(hubUpdated);
+ CmdPtr cmd(new list<boost::any>);
+ cmd->push_back( string("hubUpdated") );
+ cmd->push_back( aSessionId );
+ cmd->push_back( hubName );
+ FOR_ALL_CLIENTS( cmd );
+}
+
+void ClientNotifier::hubStats( int aSessionId, int64_t aAvailable )
+{
+ CmdPtr cmd(new list<boost::any>);
+ cmd->push_back( string("hubStats") );
+ cmd->push_back( aSessionId );
+ cmd->push_back( aAvailable );
+ FOR_ALL_CLIENTS( cmd );
+}
+
+void ClientNotifier::message(int aSessionId,const std::string& msg)
+{
+ // XmlRpcValue args,result;
+ // args[0] = aSessionId;
+ // args[1] = msg.c_str();
+ // FOR_ALL_CLIENTS(chatMessage);
+ CmdPtr cmd(new list<boost::any>);
+ cmd->push_back( string("chatMessage") );
+ cmd->push_back( aSessionId );
+ cmd->push_back( msg );
+ FOR_ALL_CLIENTS( cmd );
+}
+
+void ClientNotifier::userUpdated(int aSessionId,const ::User::Ptr& aUser)
+{
+ dcqt_backend::User& user = SessionManager::instance()->getUserPtrMap()[aUser];
+ CmdPtr cmd(new list<boost::any>);
+ cmd->push_back( string("userUpdated") );
+ cmd->push_back( aSessionId );
+ cmd->push_back( rpc_argument::createUser(user) );
+ FOR_ALL_CLIENTS( cmd );
+}
+
+// void ClientNotifier::usersUpdated(int aSessionId, const ::User::List &ll)
+// {
+// // XmlRpcValue args,userlist,result;
+// // int argi=0;
+// // RpcArgumentFactory afac;
+// // User::List::const_iterator it;
+// // for (it = ll.begin (); it != ll.begin (); it++) {
+// // if (!(*it)->isSet(User::HIDDEN)) {
+// // backend::User& user = SessionManager::instance()->getUserPtrMap()[*it];
+// // userlist[argi++] = afac.createUser(user);
+// // }
+// // }
+// // args[0] = aSessionId;
+// // args[1] = userlist;
+// // FOR_ALL_CLIENTS(usersUpdated);
+// RpcDriver::CmdPtr cmd(new list<boost::any>);
+// RpcArgumentFactory afac;
+// list<boost::any> userlist;
+// int argi=0;
+// ::User::List::const_iterator it;
+// for (it = ll.begin (); it != ll.begin (); it++)
+// {
+// if (!(*it)->isSet(::User::HIDDEN))
+// {
+// dcqt_backend::User& user = SessionManager::instance()->getUserPtrMap()[*it];
+// userlist.push_back(afac.createUser(user));
+// }
+// }
+// cmd->push_back( string("usersUpdated"));
+// cmd->push_back( userlist );
+// FOR_ALL_CLIENTS(cmd);
+//
+//
+// }
+
+
+void ClientNotifier::usersUpdated(int sessionId, const vector<int>& updatedUsers)
+{
+ // XmlRpcValue args,userlist,result;
+ CmdPtr cmd(new list<boost::any>);
+
+ cmd->push_back(string("usersUpdated"));
+ cmd->push_back(sessionId);
+ list<boost::any> userlist;
+
+ for(int i=0;i < updatedUsers.size();i++) {
+ if( SessionManager::instance()->getUserIdMap().find(updatedUsers[i]) == SessionManager::instance()->getUserIdMap().end() )
+ continue;
+ ::User::Ptr usr = SessionManager::instance()->getUserIdMap()[updatedUsers[i]];
+ if (!usr->isSet(::User::HIDDEN)) {
+ User& user = SessionManager::instance()->getUserPtrMap()[usr];
+ userlist.push_back(rpc_argument::createUser(user));
+ }
+ }
+
+ cmd->push_back( userlist );
+ FOR_ALL_CLIENTS( cmd );
+}
+
+void ClientNotifier::userRemoved(int aSessionId,int aUserId)
+{
+ CmdPtr cmd(new list<boost::any>);
+ cmd->push_back( string("userRemoved") );
+ cmd->push_back( aSessionId );
+ cmd->push_back( aUserId );
+ FOR_ALL_CLIENTS( cmd );
+}
+
+void ClientNotifier::hashInfo(string& file,int64_t bytesLeft,int filesLeft,int percentComplete)
+{
+ CmdPtr cmd(new list<boost::any>);
+ cmd->push_back( string("hashInfo"));
+ cmd->push_back( file );
+ cmd->push_back( bytesLeft );
+ cmd->push_back( filesLeft );
+ cmd->push_back( percentComplete );
+ FOR_ALL_CLIENTS( cmd );
+}
+
+
+void ClientNotifier::searchResult(int aSessionId, SearchResult* res)
+{
+ CmdPtr cmd(new list<boost::any>);
+ cmd->push_back( string("searchResult") );
+ cmd->push_back( aSessionId );
+
+ cmd->push_back( rpc_argument::createSearchRes( res, SessionManager::instance()->getUserPtrMap()[res->getUser()].id ) );
+ FOR_ALL_CLIENTS( cmd );
+}
+
+void ClientNotifier::searchResults(int aSessionId, vector<SearchResult*>& results)
+{
+ CmdPtr cmd(new list<boost::any>);
+ cmd->push_back( string("searchResults") );
+ cmd->push_back( aSessionId );
+ list<boost::any> srs;
+
+ for(int i = 0;i < results.size();i++) {
+ srs.push_back(rpc_argument::createSearchRes( results[i], SessionManager::instance()->getUserPtrMap()[results[i]->getUser()].id ));
+ }
+ cmd->push_back(srs);
+ FOR_ALL_CLIENTS(cmd);
+}
+
+
+void ClientNotifier::getPassword(int aSessionId)
+{
+ CmdPtr cmd(new list<boost::any>);
+ cmd->push_back( string("passwordRequired") );
+ cmd->push_back( aSessionId );
+ FOR_ALL_CLIENTS(cmd);
+}
+
+void ClientNotifier::privateMessage(int aSessionId,const ::User::Ptr& user,const std::string& msg)
+{
+ CmdPtr cmd(new list<boost::any>);
+ cmd->push_back( string("privateChatMessage") );
+
+ cmd->push_back( aSessionId );
+ cmd->push_back( user->getNick() );
+ cmd->push_back( msg );
+ FOR_ALL_CLIENTS(cmd);
+}
+
+void ClientNotifier::transferEvent(TransferEvent e,Upload* u)
+{
+ // XmlRpcValue args,result;
+ CmdPtr cmd(new list<boost::any>);
+ cmd->push_back( string("transferEvent") );
+
+ cmd->push_back((int)e);
+ cmd->push_back((int)UPLOAD);
+ cmd->push_back( rpc_argument::createUpload(u));
+ FOR_ALL_CLIENTS(cmd);
+}
+
+void ClientNotifier::transferTick(const Upload::List& ul,TransferManager::UploadStatusMap& us)
+{
+ CmdPtr cmd(new list<boost::any>);
+ cmd->push_back( string("transferEvent") );
+
+ // XmlRpcValue args,result;
+ cmd->push_back((int)TICK);
+ cmd->push_back((int)UPLOAD);
+ list<boost::any> uploadList;
+ Upload::List::const_iterator it;
+
+ for(it=ul.begin();it!=ul.end();it++) {
+ if(us[*it]==TransferManager::UPLOAD_STARTING)
+ uploadList.push_back(rpc_argument::createUpload(*it));
+ }
+ cmd->push_back( uploadList );
+ FOR_ALL_CLIENTS( cmd );
+}
+
+void ClientNotifier::transferEvent(TransferEvent e,Download* d)
+{
+ logger->info(boost::format("TransferEvent download"));
+ CmdPtr cmd(new list<boost::any>);
+ cmd->push_back( string("transferEvent") );
+ cmd->push_back((int)e);
+ cmd->push_back( (int) DOWNLOAD );
+ cmd->push_back( rpc_argument::createDownload(d));
+
+ FOR_ALL_CLIENTS(cmd);
+}
+
+
+void ClientNotifier::transferTick(const Download::List& ul)
+{
+ logger->info(boost::format("TransferTick download"));
+ CmdPtr cmd(new list<boost::any>);
+ cmd->push_back( string("transferEvent") );
+ cmd->push_back( (int) TICK );
+ cmd->push_back( (int) DOWNLOAD );
+ list<boost::any> l;
+ Download::List::const_iterator it;
+ for(it=ul.begin();it!=ul.end();it++)
+ l.push_back(rpc_argument::createDownload(*it));
+
+ cmd->push_back( l );
+ FOR_ALL_CLIENTS(cmd);
+}
+
+
+void ClientNotifier::transferEvent(Upload* u,const string& error)
+{
+ CmdPtr cmd(new list<boost::any>);
+ cmd->push_back( string("transferEvent") );
+ cmd->push_back( (int) FAILED );
+ cmd->push_back( (int) UPLOAD );
+ cmd->push_back( rpc_argument::createUpload(u) );
+ cmd->push_back( error );
+ FOR_ALL_CLIENTS(cmd);
+}
+
+void ClientNotifier::transferEvent(Download* d,const string& error)
+{
+ CmdPtr cmd(new list<boost::any>);
+ cmd->push_back( string("transferEvent") );
+ cmd->push_back( (int) FAILED );
+ cmd->push_back( (int) DOWNLOAD );
+ cmd->push_back( rpc_argument::createDownload(d) );
+ cmd->push_back( error );
+ FOR_ALL_CLIENTS(cmd);
+}
+
+void ClientNotifier::queueEvent(QueueEvent event,QueueItem* item,int id)
+{
+ CmdPtr cmd(new list<boost::any>);
+ cmd->push_back( string("queueEvent") );
+
+ QueueItem::Source::List::const_iterator iter;
+ list<boost::any> sources;
+ cmd->push_back((int)event);
+ switch(event) {
+ case QUEUE_ADD:
+ cout << "QueueAdd" << endl;
+ cmd->push_back(rpc_argument::createQueueItem(item,id));
+ break;
+ case QUEUE_REMOVE:
+ cout << "Queueremove" << endl;
+ cmd->push_back(id);
+ break;
+ case QUEUE_FINISH:
+ cout << "Queuefinish" << endl;
+ cmd->push_back(id);
+ break;
+ case QUEUE_SOURCE_UPDATE:
+ {
+ cout << "Queuesu" << endl;
+ cmd->push_back(id);
+ iter = item->getSources().begin();
+ //SessionManager::instance()->aquireLock();
+ boost::recursive_mutex::scoped_lock sl(SessionManager::instance()->sessionLock);
+ while( iter!=item->getSources().end()) {
+ sources.push_back(SessionManager::instance()->getUserPtrMap()[(*iter)->getUser()].id);
+ iter++;
+ }
+ //SessionManager::instance()->releaseLock();
+ cmd->push_back(sources);
+ break;
+ }
+ case QUEUE_STATUS_UPDATE:
+ cout << "Queuestatyup" << endl;
+ cmd->push_back(id);
+ cmd->push_back((int)item->getStatus());
+ cmd->push_back((int)(item->getCurrent() ? (SessionManager::instance()->getUserPtrMap()[item->getCurrent()->getUser()].id) : (int)0));
+ cmd->push_back((int)item->getPriority());
+ break;
+ default:
+ cout << "QueueHELVETE" << endl;
+ return;
+ }
+
+ FOR_ALL_CLIENTS(cmd);
+
+ // Check if this was a user file list download
+ // in that case we need to parse the file list and send it to the client
+ if ( QUEUE_FINISH == event &&
+ item->isSet( QueueItem::FLAG_USER_LIST ) &&
+ TransferManager::getInstance()->isFileListRequested(SessionManager::instance()->getUserPtrMap()[item->getCurrent()->getUser()].id) ) {
+ CmdPtr cmd2(new list<boost::any>);
+ cmd2->push_back( string("userFileListing") );
+ // Store user id
+ cmd2->push_back(SessionManager::instance()->getUserPtrMap()[item->getCurrent()->getUser()].id);
+ // Parse the directory listing
+ cmd2->push_back( createFileListing( item ) );
+ // Notify clients
+ FOR_ALL_CLIENTS(cmd2);
+
+ // Remove the request from the transfermanager
+ TransferManager::getInstance()->removeFileListRequest(SessionManager::instance()->getUserPtrMap()[item->getCurrent()->getUser()].id );
+ }
+}
+
+
+// Added
+void ClientNotifier::finishedEvent(int type,int id,FinishedItem* item)
+{
+ CmdPtr cmd(new list<boost::any>);
+ cmd->push_back( string("finishedEvent") );
+ cmd->push_back((int)FINISHED_EVENT_ADD);
+ cmd->push_back(type);
+ cmd->push_back(rpc_argument::createFinishedItem(item,id));
+ FOR_ALL_CLIENTS(cmd);
+}
+
+// Removed
+void ClientNotifier::finishedEvent(int type,int id)
+{
+ CmdPtr cmd(new list<boost::any>);
+ cmd->push_back( string("finishedEvent") );
+ cmd->push_back((int)FINISHED_EVENT_REMOVE);
+ cmd->push_back(type);
+ cmd->push_back(id);
+ FOR_ALL_CLIENTS(cmd);
+}
+
+// RemovedAll
+void ClientNotifier::finishedEvent(int type)
+{
+ CmdPtr cmd(new list<boost::any>);
+ cmd->push_back( string("finishedEvent") );
+ cmd->push_back((int)FINISHED_EVENT_REMOVEALL);
+ cmd->push_back(type);
+ FOR_ALL_CLIENTS(cmd);
+}
+
+void ClientNotifier::newHubList()
+{
+ CmdPtr cmd(new list<boost::any>);
+ cmd->push_back( string("newHubList") );
+ cmd->push_back( rpc_argument::createHubList(SessionManager::instance()->getPublicHubs()) );
+ FOR_ALL_CLIENTS(cmd);
+}
+
+void ClientNotifier::favouriteHubAdded(const FavoriteHubEntry* e)
+{
+ CmdPtr cmd(new list<boost::any>);
+ cmd->push_back( string("favouriteHubAdded") );
+ cmd->push_back(rpc_argument::createFavouriteHub(e));
+ FOR_ALL_CLIENTS(cmd);
+}
+
+void ClientNotifier::favouriteHubRemoved(const string& server)
+{
+ CmdPtr cmd(new list<boost::any>);
+ cmd->push_back( string("favouriteHubRemoved") );
+ cmd->push_back(server);
+ FOR_ALL_CLIENTS(cmd);
+}
+
+string ClientNotifier::createFileListing( QueueItem* item )
+{
+ std::string tree = "";
+ try {
+ // Create a new directory listing
+ DirectoryListing listing( item->getCurrent()->getUser() );
+ // Load the file from disk
+ listing.loadFile( item->getListName() );
+
+
+ // Add the user name as root
+ tree += listing.getUser()->getNick();
+
+ // Parse the nodes children
+ parseDir( listing.getRoot(), tree, listing.getUtf8() );
+
+ // Remove the last '*'
+ tree = tree.substr( 0, tree.size() - 1 );
+ }
+ catch(...) {
+ cerr << "Major fuck up" << endl;
+ }
+
+ return tree;
+}
+
+void ClientNotifier::parseDir( DirectoryListing::Directory::Ptr dir, std::string& tree, bool isUtf8 )
+{
+
+ // Check if the name is in UTF8
+ tree += isUtf8 ? dir->getName() : Text::acpToUtf8( dir->getName() );
+
+ std::sort( dir->directories.begin(), dir->directories.end(), DirectoryListing::Directory::DirSort() );
+
+ // Add start of directory
+ tree += "/";
+
+ for ( DirectoryListing::Directory::Iter it = dir->directories.begin(); it != dir->directories.end(); it++)
+ {
+
+ parseDir( *it, tree, isUtf8 );
+ }
+
+ for ( DirectoryListing::File::Iter file = dir->files.begin(); file != dir->files.end(); file++ )
+ {
+ // Add file name and seperator
+ tree += (*file)->getName() + "|";
+
+ // Add size
+ boost::format sizeFmter("%1%");
+ sizeFmter % (*file)->getSize();
+ tree += sizeFmter.str();
+
+ // Add TTH
+ tree += "|";
+ tree += (*file)->getTTH() ? (*file)->getTTH()->toBase32() : "";
+
+ // Add seperator between siblings
+ tree += "*";
+ }
+
+ // Check if we added any directories or files. If so we remove the last '*'
+ if ( dir->files.size() != 0 || dir->directories.size() != 0 )
+ {
+ tree = tree.substr( 0, tree.size() - 1 );
+ }
+
+ // Add end of directory
+ tree += "\\*";
+}
+}
diff --git a/backend/ClientNotifier.h b/backend/ClientNotifier.h
new file mode 100644
index 0000000..2d652dc
--- /dev/null
+++ b/backend/ClientNotifier.h
@@ -0,0 +1,88 @@
+#ifndef CLIENT_NOTIFIER_H__
+#define CLIENT_NOTIFIER_H__
+
+#include <string>
+
+#include <dcpp/stdinc.h>
+#include <dcpp/DCPlusPlus.h>
+#include <dcpp/Client.h>
+#include <dcpp/ClientManager.h>
+#include <dcpp/User.h>
+#include <dcpp/SearchManager.h>
+#include <dcpp/UploadManager.h>
+#include <dcpp/DownloadManager.h>
+#include "dcpp/DirectoryListing.h"
+
+#include <list>
+#include <vector>
+#include "TransferManager.h"
+#include <rpcdriver.h>
+
+class FavoriteHubEntry;
+
+using namespace std;
+
+namespace dcqt_backend
+{
+
+//! Lightweight abstraction layer between a Session and the XmlRpcClient
+class ClientNotifier
+{
+ public:
+ enum TransferEvent {STARTING=0,TICK=1,COMPLETE=2,FAILED=3};
+ enum QueueEvent {QUEUE_ADD=1,QUEUE_REMOVE,QUEUE_FINISH,QUEUE_SOURCE_UPDATE,QUEUE_STATUS_UPDATE};
+
+ enum {UPLOAD=0,DOWNLOAD=1};
+ virtual void connected(int aSessionId);
+ virtual void connectionFailed(int session,const string& reason);
+ virtual void hubUpdated(int aSessionId,const std::string& hubName);
+ virtual void message(int aSessionId,const std::string& msg);
+ virtual void userUpdated(int aSessionId,const ::User::Ptr& user);
+ //virtual void usersUpdated(int aSessionId, const ::User::List &list);
+ virtual void usersUpdated(int sessionId, const vector<int>& updatedUsers);
+ virtual void userRemoved(int aSessionId,int aUserId);
+ virtual void hashInfo(string&,int64_t,int,int);
+ virtual void privateMessage(int aSessionId,const ::User::Ptr& user,const std::string& msg);
+ virtual void searchResult(int aSessionId, SearchResult* result);
+ virtual void searchResults(int aSessionId, vector<SearchResult*>& results);
+ virtual void getPassword(int aSessionId);
+ virtual void hubStats(int aSessionId,int64_t aAvailable);
+
+ //! Starting or complete
+ virtual void transferEvent(TransferEvent e,Upload*);
+ //! Failed
+ virtual void transferEvent(Upload*,const string&);
+ virtual void transferTick(const Upload::List&,TransferManager::UploadStatusMap&);
+ virtual void transferEvent(TransferEvent e,Download*);
+ virtual void transferEvent(Download*,const string&);
+ virtual void transferTick(const Download::List&);
+
+ virtual void queueEvent(QueueEvent event,QueueItem*,int);
+
+ // Added
+ virtual void finishedEvent(int type,int,FinishedItem*);
+ // Removed
+ virtual void finishedEvent(int type,int id);
+ // RemovedAll
+ virtual void finishedEvent(int type);
+
+ virtual void newHubList();
+
+ virtual void favouriteHubAdded(const FavoriteHubEntry* e);
+ virtual void favouriteHubRemoved(const string& server);
+
+ static ClientNotifier* instance() {if(!inst) inst = new ClientNotifier;return inst;}
+
+ void setRpcDriver(rpc::RpcDriverPtr aDriver) {driver=aDriver;}
+
+protected:
+ ClientNotifier() {}
+ static ClientNotifier* inst;
+
+ string createFileListing( QueueItem* item );
+ void parseDir( DirectoryListing::Directory::Ptr dir, std::string& tree, bool isUtf8 );
+ rpc::RpcDriverPtr driver;
+};
+}
+
+#endif
diff --git a/backend/CommonTypes.h b/backend/CommonTypes.h
new file mode 100644
index 0000000..069e85c
--- /dev/null
+++ b/backend/CommonTypes.h
@@ -0,0 +1,47 @@
+#ifndef COMMONTYPES_H__
+#define COMMONTYPES_H__
+
+enum {
+QUEUE_EVENT_ADD=1,
+QUEUE_EVENT_REMOVE=2,
+QUEUE_EVENT_FINISHED=3,
+QUEUE_EVENT_SOURCES_UPDATED=4,
+QUEUE_EVENT_STATUS_UPDATED=5
+
+};
+
+enum {
+QUEUE_STATUS_WAITING=0,QUEUE_STATUS_RUNNING=1};
+
+enum {UPLOAD=0,DOWNLOAD=1};
+enum {FINISHED_EVENT_ADD=1,FINISHED_EVENT_REMOVE=2,FINISHED_EVENT_REMOVEALL=3};
+
+namespace backend {
+
+ /**
+ * Size mode values that correspond to values defined in dc++ SearchManager
+ */
+ enum eSizeModes {
+ SIZE_DONTCARE = 0,
+ SIZE_ATLEAST,
+ SIZE_ATMOST
+ };
+
+ /**
+ * File type values that correspond to values defined in dc++ SearchManager
+ */
+ enum eTypeModes {
+ TYPE_ANY = 0,
+ TYPE_AUDIO,
+ TYPE_COMPRESSED,
+ TYPE_DOCUMENT,
+ TYPE_EXECUTABLE,
+ TYPE_PICTURE,
+ TYPE_VIDEO,
+ TYPE_DIRECTORY,
+ TYPE_TTH
+ };
+
+} // namespace backend
+
+#endif
diff --git a/backend/IdAssigner.h b/backend/IdAssigner.h
new file mode 100644
index 0000000..0674d9f
--- /dev/null
+++ b/backend/IdAssigner.h
@@ -0,0 +1,58 @@
+#ifndef ID_ASSIGNER_H__
+#define ID_ASSIGNER_H__
+#include <map>
+#include "Stack.h"
+
+template<typename T>
+class IdAssigner {
+
+public:
+ IdAssigner() {idCounter=0;}
+
+ int getId(T t) {
+ if( contains(t) ) return idmap[t];
+ int id;
+ if( idStack.size() > 0 ) id = idStack.pop();
+ else id = ++idCounter;
+ idmap[t] = id;
+ tmap[id] = t;
+ return id;
+ }
+
+ T get(int id) {
+ return tmap[id];
+ }
+
+ void release(T t)
+ {
+ if( !contains(t) ) return;
+ int id = idmap[t];
+ idStack.push(id);
+ idmap.erase(t);
+ tmap.erase(id);
+
+ }
+
+ bool contains(T t) {
+ return idmap.find(t)!=idmap.end();
+ }
+ bool contains(int id) {
+ return tmap.find(id)!=tmap.end();
+ }
+
+private:
+ typedef map<T,int> IdMap;
+ typedef map<int,T> TMap;
+ IdMap idmap;
+ TMap tmap;
+ int idCounter;
+ Stack<int> idStack;
+
+
+};
+
+
+
+
+
+#endif
diff --git a/backend/RpcArgumentFactory.h b/backend/RpcArgumentFactory.h
new file mode 100644
index 0000000..16eeff8
--- /dev/null
+++ b/backend/RpcArgumentFactory.h
@@ -0,0 +1,151 @@
+#ifndef RPC_ARGUMENT_FACTORY__
+#define RPC_ARGUMENT_FACTORY__
+
+#include "User.h"
+#include "BackendUtil.h"
+#include "SessionManager.h"
+
+class Upload;
+namespace dcqt_backend { namespace rpc_argument {
+
+ inline boost::any createUser(const dcqt_backend::User& u) {
+ list<boost::any> user;
+ user.push_back(u.id);
+ user.push_back(u.nick);
+ user.push_back(u.flags);
+ user.push_back(u.email);
+ user.push_back(u.desc);
+ user.push_back(u.connection);
+ user.push_back(u.tag);
+ user.push_back(u.shared);
+ user.push_back(u.slots);
+ return user;
+ }
+
+ inline list<boost::any> createUpload(Upload* t) {
+ list<boost::any> ret;
+
+ ret.push_back(t->getPos());
+ ret.push_back(t->getStartPos());
+ ret.push_back(t->getActual());
+ ret.push_back(t->getSize());
+ ret.push_back(t->getAverageSpeed());
+ ret.push_back(t->getSecondsLeft());
+ ret.push_back(t->getBytesLeft());
+ ret.push_back(t->getFileName());
+ ret.push_back(t->getLocalFileName());
+ ret.push_back( t->getTTH() ? t->getTTH()->toBase32() : "");
+ ret.push_back( SessionManager::instance()->getUserPtrMap()[t->getUser()].id);
+
+ return ret;
+ }
+
+ inline list<boost::any> createDownload(Download* t) {
+ list<boost::any> ret;
+
+ ret.push_back(t->getPos());
+ ret.push_back(t->getStartPos());
+ ret.push_back(t->getActual());
+ ret.push_back(t->getSize());
+ ret.push_back(t->getAverageSpeed());
+ ret.push_back(t->getSecondsLeft());
+ ret.push_back(t->getBytesLeft());
+ ret.push_back(t->getTargetFileName());
+
+ ret.push_back(t->getTTH() ? t->getTTH()->toBase32() : "");
+ ret.push_back(SessionManager::instance()->getUserPtrMap()[t->getUserConnection()->getUser()].id);
+
+ return ret;
+ }
+
+ inline list<boost::any> createSearchRes(SearchResult* res, int userId) {
+ //XmlRpcValue ret;
+ list<boost::any> ret;
+ ret.push_back(static_cast<string>(res->getFile()));
+ ret.push_back(static_cast<int>( res->getUtf8() ));
+ ret.push_back(static_cast<string>( res->getFileName() ));
+ ret.push_back(static_cast<string>( res->getUser()->getNick() ));
+ ret.push_back(static_cast<string>( res->getHubName() ));
+ ret.push_back(static_cast<int>( userId ));
+ ret.push_back(res->getSize());
+ ret.push_back(res->getSlots());
+ ret.push_back(static_cast<int>( res->getFreeSlots() ));
+ ret.push_back(static_cast<int>( res->getType() ));
+ ret.push_back(res->getTTH() ? res->getTTH()->toBase32() : "");
+
+ return ret;
+ }
+
+ inline list<boost::any> createQueueItem(QueueItem* item,int id) {
+ list<boost::any> ret;
+ ret.push_back(id);
+ ret.push_back(item->getTarget());
+ ret.push_back(static_cast<int64_t>(item->getSize()));
+ ret.push_back(static_cast<int64_t>(item->getDownloadedBytes()));
+ ret.push_back( (int)item->getStatus());
+ ret.push_back((int)item->getPriority());
+ if( item->getCurrent() )
+ ret.push_back(SessionManager::instance()->getUserPtrMap()[item->getCurrent()->getUser()].id);
+ else
+ ret.push_back(0);
+ QueueItem::Source::List sources = item->getSources();
+ QueueItem::Source::Iter sit = sources.begin();
+ list<boost::any> slst;
+ for(;sit!=sources.end();sit++) {
+ ::User::Ptr u = (*sit)->getUser();
+ int uid = SessionManager::instance()->getUserPtrMap()[u].id;
+ slst.push_back(uid);
+ }
+ ret.push_back(slst);
+ return ret;
+ }
+
+ inline list<boost::any> createFinishedItem(FinishedItem* item,int id) {
+ list<boost::any> ret;
+ ret.push_back(id);
+ ret.push_back(item->getTarget());
+ ret.push_back(item->getUser());
+ ret.push_back(item->getHub());
+ return ret;
+ }
+
+ inline list<boost::any> createFavouriteHub(const FavoriteHubEntry* i) {
+ list<boost::any> ret;
+ ret.push_back(i->getNick());
+ ret.push_back(i->getUserDescription());
+ ret.push_back(i->getName());
+ ret.push_back(i->getServer());
+ ret.push_back(i->getDescription());
+ ret.push_back(i->getPassword());
+ ret.push_back(i->getConnect());
+ return ret;
+ }
+
+ inline list<boost::any> createHubList( const HubEntry::List& hubList ){
+
+ list< boost::any > hubs;
+ // Go through all the hubs and add them to the list
+ int i = 0;
+ for(HubEntry::List::const_iterator it = hubList.begin();it!=hubList.end();it++,i++) {
+
+ list<boost::any> hub;
+ hub.push_back((*it).getName());
+ hub.push_back((*it).getServer());
+ hub.push_back((*it).getDescription());
+ hub.push_back((*it).getCountry());
+ hub.push_back((*it).getRating());
+ hub.push_back(static_cast<int>((*it).getReliability()));
+ hub.push_back(static_cast<int64_t>((*it).getShared()));
+ hub.push_back(static_cast<int64_t>((*it).getMinShare()));
+ hub.push_back((*it).getUsers());
+ hub.push_back((*it).getMinSlots());
+ hub.push_back((*it).getMaxHubs());
+ hub.push_back((*it).getMaxUsers());
+
+ hubs.push_back(hub);
+ }
+ return hubs;
+ }
+}
+}
+#endif
diff --git a/backend/SConscript b/backend/SConscript
new file mode 100644
index 0000000..913e045
--- /dev/null
+++ b/backend/SConscript
@@ -0,0 +1,10 @@
+
+## Get the environment that was created by the parent
+Import('env')
+
+## Copy the parent environment and att some libraries and linker paths
+backende = env.Copy(CPPPATH = ['#rpcdriver/', '#dcpp'], LIBS = ['dcpp', 'rpc', 'boost_thread', 'pthread', 'z', 'bz2', 'boost_program_options','boost_filesystem'], LIBPATH = ['/usr/local/lib', '#rpcdriver', '#dcpp'])
+
+## Build the backend executable
+backende.Program('backend', Split('main.cpp commandhandlers.cpp ClientNotifier.cpp SessionManager.cpp Session.cpp TransferManager.cpp Selecter.cpp filelog.cpp SettingsMapper.cpp'))
+
diff --git a/backend/Selecter.cpp b/backend/Selecter.cpp
new file mode 100644
index 0000000..4412d61
--- /dev/null
+++ b/backend/Selecter.cpp
@@ -0,0 +1,72 @@
+#include "Selecter.h"
+#include <iostream>
+#include <signal.h>
+#include <sys/poll.h>
+
+using namespace std;
+
+pthread_t Selecter::thread;
+pthread_mutex_t Selecter::lock;
+bool Selecter::exitPlease, Selecter::running;
+
+void *Selecter::threadFunc(void *arg) {
+
+ cerr << "threadFunc start" << endl;
+
+ ServerSocket *socket = (ServerSocket *)arg;
+ struct pollfd pollData;
+ int retVal;
+ const int TIMEOUT = 1000;
+
+ pthread_mutex_lock(&lock);
+ running = true;
+ pthread_mutex_unlock(&lock);
+
+ pollData.fd = socket->getSocket();
+ pollData.events = (POLLIN | POLLPRI);
+ pollData.revents = 0;
+ while ((retVal = poll(&pollData, 1, TIMEOUT)) != -1) {
+
+ if (exitPlease) {
+ cerr << "threadFunc exitPlease" << endl;
+ running = false;
+ pthread_exit(0);
+ }
+
+ //0 = timeout
+ if (retVal == 0) {
+ // wtf
+ } else {
+ cerr << "threadFunc incoming" << endl;
+ socket->incoming();
+ }
+ }
+
+ cerr << "retval: " << retVal << endl;
+ if(retVal==-1) perror("threadfunc");
+
+ pthread_mutex_lock(&lock);
+ running = false;
+ pthread_mutex_unlock(&lock);
+ pthread_exit(0);
+}
+
+void Selecter::doit(ServerSocket &socket) {
+ exitPlease = false;
+ running = false;
+ pthread_mutex_init(&lock, NULL);
+ pthread_create(&thread, NULL, &threadFunc, (void *)&socket);
+}
+
+void Selecter::quit() {
+ cout << "Selecter::quit()" << endl;
+ pthread_mutex_lock(&lock);
+ if (!running) {
+ pthread_mutex_unlock(&lock);
+ return;
+ }
+ cout << "Selecter set quit" << endl;
+ exitPlease = true;
+ pthread_mutex_unlock(&lock);
+}
+
diff --git a/backend/Selecter.h b/backend/Selecter.h
new file mode 100644
index 0000000..608b643
--- /dev/null
+++ b/backend/Selecter.h
@@ -0,0 +1,25 @@
+#ifndef SELECTER_H__
+#define SELECTER_H__
+
+#include <vector>
+#include <pthread.h>
+
+#include <dcpp/stdinc.h>
+#include <dcpp/DCPlusPlus.h>
+#include <dcpp/ServerSocket.h>
+#include <dcpp/ConnectionManager.h>
+
+class Selecter {
+ public:
+ static void doit(ServerSocket &socket);
+ static void quit();
+
+ private:
+ static void *threadFunc(void *arg);
+ static bool exitPlease, running;
+ static pthread_t thread;
+ static pthread_mutex_t lock;
+
+};
+
+#endif
diff --git a/backend/Session.cpp b/backend/Session.cpp
new file mode 100644
index 0000000..5434af7
--- /dev/null
+++ b/backend/Session.cpp
@@ -0,0 +1,341 @@
+#include "Session.h"
+#include "ClientNotifier.h"
+#include "SessionManager.h"
+#include <iostream>
+#include "CommonTypes.h"
+#include <boost/thread/recursive_mutex.hpp>
+
+using namespace std;
+
+namespace dcqt_backend {
+
+Session::Session(int aId,Client* aClient,string aUrl) : id(aId), client(aClient), url(aUrl) {
+ client->addListener(this);
+ client->setNick("Arnold");
+
+ // Make sure we get search results.
+ SearchManager::getInstance()->addListener( this );
+ TimerManager::getInstance()->addListener( this );
+}
+
+Session::~Session()
+{
+ //cerr << "Session dtor" << endl;
+ SearchManager::getInstance()->removeListener( this );
+ TimerManager::getInstance()->removeListener( this );
+}
+
+void Session::connect() {
+ client->connect();
+}
+
+void Session::sendChat(const string& msg) {
+ client->hubMessage(msg);
+}
+
+void Session::search(int sizeMode, int64_t size, int typeMode, const string& searchString, const string& token) {
+ SearchManager::SizeModes dcppSizeMode;
+ SearchManager::TypeModes dcppTypeMode;
+
+ //cout <<"Searching for '"<<searchString<<"' sz:"<<size<<" szMode: "<<(int)sizeMode<<" typeMode"<<(int)typeMode<<endl;
+
+ // This silly-looking switch-style mapping is here in case TypeModes
+ // and/or SizeModes enums in dc++ SearchManager should change in the
+ // future releases and become different from corresponding enums in
+ // the backend namespace. I see no point in making a proper map for
+ // this, yet.
+
+ switch (sizeMode) {
+ case backend::SIZE_DONTCARE: dcppSizeMode = SearchManager::SIZE_DONTCARE; break;
+ case backend::SIZE_ATLEAST: dcppSizeMode = SearchManager::SIZE_ATLEAST; break;
+ case backend::SIZE_ATMOST: dcppSizeMode = SearchManager::SIZE_ATMOST; break;
+ default:
+ dcppSizeMode = SearchManager::SIZE_DONTCARE; break;
+ };
+
+ switch (typeMode) {
+ case backend::TYPE_ANY: dcppTypeMode = SearchManager::TYPE_ANY; break;
+ case backend::TYPE_AUDIO: dcppTypeMode = SearchManager::TYPE_AUDIO; break;
+ case backend::TYPE_COMPRESSED: dcppTypeMode = SearchManager::TYPE_COMPRESSED; break;
+ case backend::TYPE_DOCUMENT: dcppTypeMode = SearchManager::TYPE_DOCUMENT; break;
+ case backend::TYPE_EXECUTABLE: dcppTypeMode = SearchManager::TYPE_EXECUTABLE; break;
+ case backend::TYPE_PICTURE: dcppTypeMode = SearchManager::TYPE_PICTURE; break;
+ case backend::TYPE_VIDEO: dcppTypeMode = SearchManager::TYPE_VIDEO; break;
+ case backend::TYPE_DIRECTORY: dcppTypeMode = SearchManager::TYPE_DIRECTORY; break;
+ case backend::TYPE_TTH: dcppTypeMode = SearchManager::TYPE_TTH; break;
+ default:
+ dcppTypeMode = SearchManager::TYPE_ANY; break;
+ };
+
+ SearchManager::getInstance()->search(searchString, size, dcppTypeMode, dcppSizeMode, "");
+
+ //client->search(SearchManager::SIZE_DONTCARE, 0, SearchManager::TYPE_ANY, searchString, token);
+}
+
+////////////////////////////////////////
+// TODO userRemoved
+////////////////////////////////////////
+
+void Session::updateUser(const ::User::Ptr& user) {
+ dcqt_backend::User u = SessionManager::instance()->getUserPtrMap()[user];
+ if(u.id==0) {
+ // This user does not have an ID, get one.
+ u.id = SessionManager::instance()->generateUserId();
+ }
+
+ u.flags = 0;
+ // Stupid dc++ does not provide a getflags()
+ if(user->isSet(::User::OP))
+ u.flags|=::User::OP;
+ if(user->isSet(::User::ONLINE))
+ u.flags|=::User::ONLINE;
+ if(user->isSet(::User::DCPLUSPLUS))
+ u.flags|=::User::DCPLUSPLUS;
+ if(user->isSet(::User::PASSIVE))
+ u.flags|=::User::PASSIVE;
+ if(user->isSet(::User::QUIT_HUB))
+ u.flags|=::User::QUIT_HUB;
+ if(user->isSet(::User::HIDDEN))
+ u.flags|=::User::HIDDEN;
+ if(user->isSet(::User::HUB))
+ u.flags|=::User::HUB;
+ if(user->isSet(::User::BOT))
+ u.flags|=::User::BOT;
+ u.nick = user->getNick();
+ u.email = user->getEmail();
+ u.desc = user->getDescription();
+ u.connection = user->getConnection();
+ u.tag = user->getTag();
+ u.shared = user->getBytesShared();
+ u.slots = user->getSlots();
+ SessionManager::instance()->getUserPtrMap()[user] = u;
+ SessionManager::instance()->getUserIdMap()[u.id] = user;
+ userIdSet.insert(u.id);
+ userUpdateCache.push_back(u.id);
+}
+
+void Session::on(ClientListener::Connecting, Client *aClient) throw() {
+ // cout << "Session::on Connecting" << endl;
+}
+
+void Session::on(ClientListener::Connected, Client *aClient) throw() {
+ //cout << "Session::on Connected" << endl;
+ if(aClient==client) {
+
+ ClientNotifier::instance()->connected(id);
+ }
+}
+
+void Session::on(ClientListener::BadPassword, Client *client) throw() {
+ //cout << "Session::on badpass" << endl;
+}
+
+void Session::on(ClientListener::UserUpdated, Client *aClient, const ::User::Ptr& user) throw()
+{
+ if(aClient==client) {
+ // cout << "Session::on userupdated" << endl;
+ boost::recursive_mutex::scoped_lock sl(SessionManager::instance()->sessionLock);
+ //SessionManager::instance()->aquireLock();
+ updateUser(user);
+ //SessionManager::instance()->releaseLock();
+
+ // On large hubs, this function really gets spammed. Therefore, we delay the notification
+ // until we feel we're ready to send.
+ // ClientNotifier::instance()->userUpdated(id,user);
+ }
+}
+
+void Session::on(ClientListener::UsersUpdated, Client *aClient, const ::User::List &list) throw() {
+ //cout << "Session::on usersupdated" << endl;
+
+ if(aClient==client) {
+// SessionManager::instance()->aquireLock();
+ boost::recursive_mutex::scoped_lock sl(SessionManager::instance()->sessionLock);
+ ::User::List::const_iterator it;
+ for (it = list.begin (); it != list.begin (); it++) {
+ updateUser(*it);
+ }
+// SessionManager::instance()->releaseLock();
+ // ClientNotifier::instance()->usersUpdated(id,list);
+ }
+}
+
+void Session::on(ClientListener::UserRemoved, Client *aClient, const ::User::Ptr &user) throw() {
+ //cout << "Session::on userremoved" << endl;
+
+ if(aClient==client) {
+// SessionManager::instance()->aquireLock();
+ boost::recursive_mutex::scoped_lock sl(SessionManager::instance()->sessionLock);
+ // Find the user
+ int userid = SessionManager::instance()->getUserPtrMap()[user].id;
+ userIdSet.erase ( userIdSet.find(userid) );
+ SessionManager::instance()->getUserPtrMap().erase( SessionManager::instance()->getUserPtrMap().find(user) );
+ SessionManager::instance()->getUserIdMap().erase( SessionManager::instance()->getUserIdMap().find(userid) );
+// SessionManager::instance()->releaseLock();
+ ClientNotifier::instance()->userRemoved(id,userid);
+ }
+}
+
+void Session::on(ClientListener::Redirect,
+ Client *aClient, const string &address) throw() {
+ if(client==aClient) {
+ //cout << "Session::on redirect: " << address << endl;
+ if(!address.empty()) {
+
+ client->removeListener(this);
+ ClientManager::getInstance()->putClient(client);
+ client = ClientManager::getInstance()->getClient(Text::fromT(address));
+ client->addListener(this);
+ client->connect();
+
+
+
+ }
+ }
+
+}
+
+void Session::on(ClientListener::Failed,
+ Client *aClient, const string &reason) throw() {
+ // cout << "Session::on failed" << endl;
+
+ if( client == aClient ) {
+
+ ClientNotifier::instance()->connectionFailed(id,reason);
+
+ }
+
+}
+
+void Session::on(ClientListener::GetPassword, Client *aClient) throw() {
+ //cout << "Session::on getpassword" << endl;
+ if(client==aClient)
+ ClientNotifier::instance()->getPassword(id);
+}
+
+void Session::on(ClientListener::HubUpdated, Client *aClient) throw() {
+ //cout << "Session::on hubupdated" << endl;
+ if(aClient==client) {
+ //SessionManager::instance()->aquireLock();
+ boost::recursive_mutex::scoped_lock sl(SessionManager::instance()->sessionLock);
+ if (client->getName().empty())
+ hubName = client->getAddress() + ":" + client->getAddressPort();
+ else
+ hubName = client->getName();
+ //SessionManager::instance()->releaseLock();
+ ClientNotifier::instance()->hubUpdated(id,hubName);
+ }
+}
+
+void Session::on(ClientListener::Message, Client *aClient, const string &msg) throw() {
+ // cout << "Session::on message: " << msg << endl;
+ if(aClient==client) {
+
+ if( Text::isAscii(msg) )
+ cout << "Chat message is ascii\n";
+ else
+ cout << "Chat message is utf8!\n";
+
+ ClientNotifier::instance()->message(id,msg);
+ }
+}
+
+void Session::on(ClientListener::PrivateMessage,
+ Client *aClient, const ::User::Ptr &user, const string &msg) throw() {
+ // cout << "Session::on privmsg" << endl;
+
+ if(aClient==client) {
+ ClientNotifier::instance()->privateMessage(id,user,msg);
+ }
+
+}
+
+
+void Session::on(ClientListener::UserCommand, Client *client,
+ int, int, const string&, const string&) throw() {
+ //cout << "Session::on usercommand" << endl;
+}
+
+void Session::on(ClientListener::HubFull, Client *client) throw() {
+ //cout << "Session::on hubfull" << endl;
+}
+
+void Session::on(ClientListener::NickTaken, Client *client) throw() {
+ //cout << "Session::on nicktaken" << endl;
+}
+
+void Session::on(ClientListener::SearchFlood, Client *client, const string &msg) throw() {
+ // cout << "Session::on searchflood" << endl;
+}
+
+void Session::on(ClientListener::NmdcSearch, Client *client, const string&, int, int64_t, int, const string&) throw() {
+ //cout << "Session::on nmcdsearch" << endl;
+}
+
+void Session::on(SearchManagerListener::SR, SearchResult* result) throw() {
+ //cout << "Session::on SearchResult" << endl;
+ // Check if the user the result corresponds to belongs to our hub
+ if( result->getUser()->isClient( client ) ) {
+
+ // cout << "Session::on SearchResult with correct client" << endl;
+
+ // if( Text::isAscii(result->getFileName())) cout << "Search result is ASCII\n";
+ //else cout << "Search result is UTF8\n";
+ //cout << "SearchResult utf8 flag: " << result->getUtf8();
+
+ // Send the result to the client notifier so clients get the result
+ // ClientNotifier::instance()->searchResult( id, result );
+
+ //TTHValue* tth = result->getTTH();
+ //[printf("TTH: ");
+ //[for(int i=0;i < TTHValue::SIZE;i++)
+ // printf("%x ",tth->data[i]);
+
+
+ result->incRef();
+ searchCS.enter();
+ searchResultCache.push_back(result);
+ searchCS.leave();
+ }
+}
+
+void Session::on(TimerManagerListener::Second, u_int32_t s) throw()
+{
+
+ // Take care of our userUpdated cache
+ if( (s % 2) && userUpdateCache.size() ) {
+ //SessionManager::instance()->aquireLock();
+ boost::recursive_mutex::scoped_lock sl(SessionManager::instance()->sessionLock);
+ ClientNotifier::instance()->usersUpdated(id,userUpdateCache);
+ userUpdateCache.clear();
+ //SessionManager::instance()->releaseLock();
+ }
+
+ // Flush the search result cache
+ if( (s%2==0) && searchResultCache.size() ) {
+ // assert(0);
+
+ // Lock
+ searchCS.enter();
+
+ // Send
+ ClientNotifier::instance()->searchResults(id,searchResultCache);
+
+ // Release & clear
+ for(int i=0;i < searchResultCache.size();i++)
+ searchResultCache[i]->decRef();
+
+ searchResultCache.clear();
+ // Unlock
+ searchCS.leave();
+
+
+ }
+
+ // Send total share size each 10 seconds
+ if( s%10==0 ) {
+ ClientNotifier::instance()->hubStats(id,client->getAvailable());
+ }
+
+ }
+}
diff --git a/backend/Session.h b/backend/Session.h
new file mode 100644
index 0000000..24a5b74
--- /dev/null
+++ b/backend/Session.h
@@ -0,0 +1,91 @@
+#ifndef SESSION_H__
+#define SESSION_H__
+
+#include <dcpp/stdinc.h>
+#include <dcpp/DCPlusPlus.h>
+#include <dcpp/User.h>
+#include <dcpp/Client.h>
+#include <dcpp/SearchManagerListener.h>
+#include <dcpp/TimerManager.h>
+
+#include <ext/hash_map>
+#include <ext/hash_set>
+#include <vector>
+
+namespace dcqt_backend {
+
+class Session : public ClientListener, public SearchManagerListener, TimerManagerListener
+{
+public:
+
+ // Constructors
+ Session(int aId,Client* aClient,string aUrl);
+ ~Session();
+
+ // Getters and setters
+ int getId() const {return id;}
+ string& getHubName() {return hubName;}
+ hash_set<int>* getUsers() {return &userIdSet;}
+ Client* getClient() {return client;}
+ string& getUrl() {return url;}
+
+ // Actions
+ void connect();
+ void sendChat(const string& msg);
+ void search(int sizeMode, int64_t size, int type, const string& searchString, const string& token);
+ void password(const string& p) {client->password(p);}
+
+ // Callbacks
+ void on(ClientListener::Connecting, Client *client) throw();
+ void on(ClientListener::Connected, Client *client) throw();
+ void on(ClientListener::BadPassword, Client *client) throw();
+ void on(ClientListener::UserUpdated, Client *client,
+ const ::User::Ptr&) throw();
+ void on(ClientListener::UsersUpdated,
+ Client *client, const ::User::List &list) throw();
+ void on(ClientListener::UserRemoved,
+ Client *client, const ::User::Ptr &user) throw();
+ void on(ClientListener::Redirect,
+ Client *client, const string &address) throw();
+ void on(ClientListener::Failed,
+ Client *client, const string &reason) throw();
+ void on(ClientListener::GetPassword, Client *client) throw();
+ void on(ClientListener::HubUpdated, Client *client) throw();
+ void on(ClientListener::Message,
+ Client *client, const string &msg) throw();
+ void on(ClientListener::PrivateMessage,
+ Client *client, const ::User::Ptr &user, const string &msg) throw();
+ void on(ClientListener::UserCommand, Client *client,
+ int, int, const string&, const string&) throw();
+ void on(ClientListener::HubFull, Client *client) throw();
+ void on(ClientListener::NickTaken, Client *client) throw();
+ void on(ClientListener::SearchFlood, Client *client,
+ const string &msg) throw();
+ void on(ClientListener::NmdcSearch, Client *client, const string&,
+ int, int64_t, int, const string&) throw();
+
+ // Implement SearchManagerListener interface
+ void on(SearchManagerListener::SR, SearchResult*) throw();
+
+ void on(TimerManagerListener::Second, u_int32_t) throw();
+
+private:
+ void updateUser(const ::User::Ptr&);
+
+ int id;
+ Client* client;
+ string hubName;
+ string url;
+
+ //! So that we know which user id's belong to this session.
+ hash_set<int> userIdSet;
+ //! This one caches updated users before they are sent to the client.
+ vector<int> userUpdateCache;
+
+ //! Caches search result before they are sent to the client.
+ vector<SearchResult*> searchResultCache;
+ CriticalSection searchCS;
+};
+
+}
+#endif
diff --git a/backend/SessionManager.cpp b/backend/SessionManager.cpp
new file mode 100644
index 0000000..07c5f28
--- /dev/null
+++ b/backend/SessionManager.cpp
@@ -0,0 +1,190 @@
+#include "SessionManager.h"
+#include "ClientNotifier.h"
+#include <boost/thread/recursive_mutex.hpp>
+#include <iostream>
+
+using namespace std;
+
+namespace dcqt_backend {
+
+SessionManager* SessionManager::inst = 0;
+
+SessionManager::SessionManager() : sessionIdCounter(0), userIdCounter(1)
+{
+ ClientManager::getInstance()->addListener(this);
+ //pthread_mutex_init( &sessionLock, NULL );
+ string file;
+ size_t files;
+ HashManager::getInstance()->getStats(file,hashBytesStart,files);
+ TimerManager::getInstance()->addListener(this);
+ HubManager::getInstance()->addListener(this);
+ HashManager::getInstance()->setPriority(Thread::NORMAL);
+ hashingCompleted = files==0;
+}
+
+SessionManager::~SessionManager()
+{
+// pthread_mutex_destroy( &sessionLock );
+}
+
+int SessionManager::generateUserId()
+{
+ if( userIdStack.size() > 0 ) return userIdStack.pop();
+ return userIdCounter++;
+}
+
+//void SessionManager::aquireLock()
+//{
+ //pthread_mutex_lock( &sessionLock );
+// sessionLock.do_lock();
+//}
+
+//void SessionManager::releaseLock()
+//{
+ // pthread_mutex_unlock( &sessionLock );
+// sessionLock.do_unlock();
+//}
+
+int SessionManager::createSession(const string& url)
+{
+ Client* client = ClientManager::getInstance()->getClient(url);
+ Session* session = new Session(sessionIdCounter++,client,url);
+ intSessionMap[session->getId()] = session;
+ clientSessionMap[client] = session;
+ session->connect();
+ return session->getId();
+}
+
+void SessionManager::closeSession(int session)
+{
+ //aquireLock();
+ boost::recursive_mutex::scoped_lock sl(sessionLock);
+ if( intSessionMap.find(session)!=intSessionMap.end()) {
+
+ Session *s = intSessionMap[session];
+ intSessionMap.erase(session);
+ ClientManager::getInstance()->putClient(s->getClient());
+ delete s;
+ }
+ //releaseLock();
+}
+
+Session* SessionManager::getSession(int aId)
+{
+ return intSessionMap[aId];
+}
+
+Session* SessionManager::getSession(Client* aClient)
+{
+ return clientSessionMap[aClient];
+}
+
+ void SessionManager::on(ClientManagerListener::ClientConnected, Client* c) throw()
+{
+ cout << "SessionManager::on ClientConnected\n";
+}
+
+void SessionManager::on(ClientManagerListener::ClientUpdated, Client* c) throw()
+{
+ cout << "SessionManager::on ClienUpdated\n";
+}
+
+ void SessionManager::on(ClientManagerListener::ClientDisconnected, Client* c) throw()
+{
+ cout << "SessionManager::on ClientDisconnected\n";
+}
+
+void SessionManager::on(TimerManagerListener::Second, u_int32_t) throw()
+{
+ string file;
+ int64_t bytesLeft;
+ size_t files;
+ HashManager::getInstance()->getStats(file,bytesLeft,files);
+ if( bytesLeft > hashBytesStart ) hashBytesStart = bytesLeft;
+
+ // Are we hashing?
+ if(files > 0 )
+ {
+ hashingCompleted = false;
+ // Calculate progress
+ double percentComplete = (hashBytesStart-bytesLeft) / (double)hashBytesStart;
+ percentComplete *= 100.0;
+ // Notify client
+ ClientNotifier::instance()->hashInfo(file,bytesLeft,files,(int)percentComplete);
+ }
+ else
+ {
+ // We are not hashing.
+ // Does the client know that we are finished?
+ if( !hashingCompleted ) {
+ hashingCompleted = true;
+ // Notify client
+ ClientNotifier::instance()->hashInfo(file,0,0,100);
+ hashBytesStart = 0;
+ }
+ }
+}
+
+HubEntry::List SessionManager::getPublicHubs()
+{
+ if(HubManager::getInstance()->isDownloading()) return HubEntry::List();
+ HubEntry::List hubs = HubManager::getInstance()->getPublicHubs();
+ if(hubs.size()==0) HubManager::getInstance()->refresh();
+ return hubs;
+}
+
+FavoriteHubEntry::List& SessionManager::getFavouriteHubs()
+{
+ return HubManager::getInstance()->getFavoriteHubs();
+}
+
+void SessionManager::addFavouriteHub(FavoriteHubEntry& entry)
+{
+ HubManager::getInstance()->addFavorite(entry);
+
+}
+
+void SessionManager::removeFavouriteHub(string& server)
+{
+ HubManager::getInstance()->removeFavorite(
+ HubManager::getInstance()->getFavoriteHubEntry(server)
+ );
+}
+
+void SessionManager::refreshHublist()
+{
+ if(HubManager::getInstance()->isDownloading()) return;
+ HubManager::getInstance()->refresh();
+}
+
+void SessionManager::on(HubManagerListener::DownloadStarting, const string& file) throw()
+{
+// XmlRpcUtil::log(1,"HubMgr download starting: %s",file.c_str());
+}
+
+void SessionManager::on(HubManagerListener::DownloadFailed, const string& file) throw()
+{
+// XmlRpcUtil::log(1,"HubMgr download failed: %s",file.c_str());
+
+}
+
+void SessionManager::on(HubManagerListener::DownloadFinished, const string& file) throw()
+{
+// XmlRpcUtil::log(1,"HubMgr download finished: %s",file.c_str());
+ ClientNotifier::instance()->newHubList();
+}
+
+void SessionManager::on(HubManagerListener::FavoriteAdded, const FavoriteHubEntry* e) throw()
+{
+// XmlRpcUtil::log(2,"HubMgr favadd");
+ ClientNotifier::instance()->favouriteHubAdded(e);
+}
+
+
+void SessionManager::on(HubManagerListener::FavoriteRemoved, const FavoriteHubEntry* e) throw()
+{
+// XmlRpcUtil::log(2,"HubMgr favremov");
+ ClientNotifier::instance()->favouriteHubRemoved(e->getServer());
+}
+
+}
diff --git a/backend/SessionManager.h b/backend/SessionManager.h
new file mode 100644
index 0000000..f2fcab4
--- /dev/null
+++ b/backend/SessionManager.h
@@ -0,0 +1,100 @@
+#ifndef SESSION_MANAGER_H__
+#define SESSION_MANAGER_H__
+
+#include "../dcpp/stdinc.h"
+#include "../dcpp/DCPlusPlus.h"
+#include "../dcpp/User.h"
+#include "../dcpp/Util.h"
+#include "../dcpp/Client.h"
+#include <dcpp/ClientManager.h>
+#include "../dcpp/ClientManagerListener.h"
+#include "../dcpp/HashManager.h"
+#include "../dcpp/TimerManager.h"
+#include <dcpp/HubManager.h>
+
+#include "Session.h"
+#include "User.h"
+
+#include <pthread.h>
+#include "Stack.h"
+#include <ext/hash_map>
+#include <boost/thread/recursive_mutex.hpp>
+
+namespace dcqt_backend {
+
+//! Provides mapping between dc++ Client objects and our own Session's.
+class SessionManager : public ClientManagerListener, public TimerManagerListener, public HubManagerListener
+{
+public:
+
+ virtual ~SessionManager();
+
+ int createSession(const string& url);
+ void closeSession(int);
+ Session *getSession(int);
+ Session *getSession(Client*);
+
+ virtual void on(ClientManagerListener::ClientConnected, Client* c) throw();
+ virtual void on(ClientManagerListener::ClientUpdated, Client* c) throw();
+ virtual void on(ClientManagerListener::ClientDisconnected, Client* c) throw();
+ virtual void on(TimerManagerListener::Second, u_int32_t) throw();
+ virtual void on(HubManagerListener::DownloadStarting, const string&) throw();
+ virtual void on(HubManagerListener::DownloadFailed, const string&) throw();
+ virtual void on(HubManagerListener::DownloadFinished, const string&) throw();
+ virtual void on(HubManagerListener::FavoriteAdded, const FavoriteHubEntry*) throw();
+ virtual void on(HubManagerListener::FavoriteRemoved, const FavoriteHubEntry*) throw();
+
+ static SessionManager* instance() {if(!inst) inst = new SessionManager;return inst;}
+
+ map<int,Session*>* getSessionMap() { return &intSessionMap; }
+
+ //void aquireLock();
+ //void releaseLock();
+
+ //! Used to map requests from the gui to a user, for example when clicking in the transfer view.
+ typedef hash_map<int,::User::Ptr> IntUserPtrMap;
+ //! Used to cache backend::User structs.
+ typedef map< ::User::Ptr, dcqt_backend::User> UserPtrUserMap;
+
+ UserPtrUserMap& getUserPtrMap() {return userPtrMap;}
+ int generateUserId();
+ IntUserPtrMap& getUserIdMap() {return userIdMap;}
+
+ HubEntry::List getPublicHubs();
+ void refreshHublist();
+ FavoriteHubEntry::List& getFavouriteHubs();
+ void addFavouriteHub(FavoriteHubEntry& entry);
+ void removeFavouriteHub(string& server);
+
+ boost::recursive_mutex sessionLock;
+
+private:
+ static SessionManager* inst;
+ SessionManager();
+
+
+ typedef map<Client*,Session*> ClientSessionMap;
+ typedef map<int,Session*> IntSessionMap;
+
+
+ ClientSessionMap clientSessionMap;
+ IntSessionMap intSessionMap;
+
+ int sessionIdCounter;
+
+ // These two are part of the user id allocation scheme.
+ int userIdCounter;
+ Stack<int> userIdStack;
+ IntUserPtrMap userIdMap;
+ UserPtrUserMap userPtrMap;
+
+ //pthread_mutex_t sessionLock;
+
+
+ // Hashing
+ int64_t hashBytesStart;
+ bool hashingCompleted;
+};
+}
+
+#endif
diff --git a/backend/SettingsMapper.cpp b/backend/SettingsMapper.cpp
new file mode 100644
index 0000000..52feb18
--- /dev/null
+++ b/backend/SettingsMapper.cpp
@@ -0,0 +1,118 @@
+#include "SettingsMapper.h"
+
+#define PREPMAP(x) sm[#x]=SettingsManager::x;
+
+SettingsMapper* SettingsMapper::inst = 0;
+using boost::any_cast;
+
+boost::any SettingsMapper::getSetting(const string& skey)
+{
+ int key = sm[skey];
+ if(key >= SettingsManager::STR_FIRST && key < SettingsManager::STR_LAST)
+ return SettingsManager::getInstance()->get((SettingsManager::StrSetting)key);
+ else if(key >= SettingsManager::INT_FIRST && key < SettingsManager::INT_LAST)
+ return SettingsManager::getInstance()->get((SettingsManager::IntSetting)key);
+ else if(key >= SettingsManager::INT64_FIRST && key < SettingsManager::INT64_LAST)
+ return SettingsManager::getInstance()->get((SettingsManager::Int64Setting)key);
+}
+
+void SettingsMapper::setSetting(const string& skey,const boost::any& value)
+{
+ int key = sm[skey];
+ if(key >= SettingsManager::STR_FIRST && key < SettingsManager::STR_LAST)
+ SettingsManager::getInstance()->set((SettingsManager::StrSetting)key,any_cast<string>(value));
+ else if(key >= SettingsManager::INT_FIRST && key < SettingsManager::INT_LAST)
+ SettingsManager::getInstance()->set((SettingsManager::IntSetting)key,any_cast<int>(value));
+ else if(key >= SettingsManager::INT64_FIRST && key < SettingsManager::INT64_LAST)
+ SettingsManager::getInstance()->set((SettingsManager::Int64Setting)key,any_cast<int64_t>(value));
+}
+
+SettingsMapper::SettingsMapper()
+{
+ // HEHE
+ // The secret map
+ PREPMAP(CONNECTION);
+ PREPMAP(DESCRIPTION);
+ PREPMAP(DOWNLOAD_DIRECTORY);
+ PREPMAP(EMAIL);
+ PREPMAP(NICK);
+ PREPMAP(SERVER);
+ PREPMAP(TEXT_FONT);
+ PREPMAP(MAINFRAME_ORDER);
+ PREPMAP(MAINFRAME_WIDTHS);
+ PREPMAP(HUBFRAME_ORDER);
+ PREPMAP(HUBFRAME_WIDTHS);
+ PREPMAP(LANGUAGE_FILE);
+ PREPMAP(SEARCHFRAME_ORDER);
+ PREPMAP(SEARCHFRAME_WIDTHS);
+ PREPMAP(FAVORITESFRAME_ORDER);
+ PREPMAP(FAVORITESFRAME_WIDTHS);
+ PREPMAP(HUBLIST_SERVERS);
+ PREPMAP(QUEUEFRAME_ORDER);
+ PREPMAP(QUEUEFRAME_WIDTHS);
+ PREPMAP(PUBLICHUBSFRAME_ORDER);
+ PREPMAP(PUBLICHUBSFRAME_WIDTHS);
+ PREPMAP(USERSFRAME_ORDER);
+ PREPMAP(USERSFRAME_WIDTHS);
+ PREPMAP(HTTP_PROXY);
+ PREPMAP(LOG_DIRECTORY);
+ PREPMAP(NOTEPAD_TEXT);
+ PREPMAP(LOG_FORMAT_POST_DOWNLOAD);
+ PREPMAP(LOG_FORMAT_POST_UPLOAD);
+ PREPMAP(LOG_FORMAT_MAIN_CHAT);
+ PREPMAP(LOG_FORMAT_PRIVATE_CHAT);
+ PREPMAP(FINISHED_ORDER);
+ PREPMAP(FINISHED_WIDTHS);
+ PREPMAP(TEMP_DOWNLOAD_DIRECTORY);
+ PREPMAP(BIND_ADDRESS);
+ PREPMAP(SOCKS_SERVER);
+ PREPMAP(SOCKS_USER);
+ PREPMAP(SOCKS_PASSWORD);
+ PREPMAP(CONFIG_VERSION);
+ PREPMAP(DEFAULT_AWAY_MESSAGE);
+ PREPMAP(TIME_STAMPS_FORMAT);
+ PREPMAP(ADLSEARCHFRAME_ORDER);
+ PREPMAP(ADLSEARCHFRAME_WIDTHS);
+ PREPMAP(FINISHED_UL_WIDTHS);
+ PREPMAP(FINISHED_UL_ORDER);
+ PREPMAP(CLIENT_ID);
+ PREPMAP(SPYFRAME_WIDTHS);
+ PREPMAP(SPYFRAME_ORDER);
+ PREPMAP(LOG_FILE_MAIN_CHAT);
+ PREPMAP(LOG_FILE_PRIVATE_CHAT);
+ PREPMAP(LOG_FILE_STATUS);
+ PREPMAP(LOG_FILE_UPLOAD);
+ PREPMAP(LOG_FILE_DOWNLOAD);
+ PREPMAP(LOG_FILE_SYSTEM);
+ PREPMAP(LOG_FORMAT_SYSTEM);
+ PREPMAP(LOG_FORMAT_STATUS);
+ PREPMAP(DIRECTORLISTINGFRAME_ORDER);
+ PREPMAP(DIRECTORLISTINGFRAME_WIDTHS);
+
+ // Ints
+ PREPMAP(CONNECTION_TYPE);PREPMAP( IN_PORT);PREPMAP( SLOTS);PREPMAP( ROLLBACK);PREPMAP( AUTO_FOLLOW);PREPMAP( CLEAR_SEARCH);
+ PREPMAP(BACKGROUND_COLOR);PREPMAP( TEXT_COLOR);PREPMAP( USE_OEM_MONOFONT);PREPMAP( SHARE_HIDDEN);PREPMAP( FILTER_MESSAGES);PREPMAP( MINIMIZE_TRAY);
+ PREPMAP(AUTO_SEARCH);PREPMAP( TIME_STAMPS);PREPMAP( CONFIRM_EXIT);PREPMAP( IGNORE_OFFLINE);PREPMAP( POPUP_OFFLINE);PREPMAP(LIST_DUPES);PREPMAP( BUFFER_SIZE);
+PREPMAP( DOWNLOAD_SLOTS);PREPMAP( MAX_DOWNLOAD_SPEED);PREPMAP( LOG_MAIN_CHAT);PREPMAP( LOG_PRIVATE_CHAT);
+PREPMAP(LOG_DOWNLOADS);PREPMAP( LOG_UPLOADS);PREPMAP( STATUS_IN_CHAT);PREPMAP( SHOW_JOINS);PREPMAP( PRIVATE_MESSAGE_BEEP);PREPMAP( PRIVATE_MESSAGE_BEEP_OPEN);
+PREPMAP(USE_SYSTEM_ICONS);PREPMAP( POPUP_PMS);PREPMAP( MIN_UPLOAD_SPEED);PREPMAP( GET_USER_INFO);PREPMAP( URL_HANDLER);PREPMAP( MAIN_WINDOW_STATE);
+ PREPMAP(MAIN_WINDOW_SIZE_X);PREPMAP( MAIN_WINDOW_SIZE_Y);PREPMAP( MAIN_WINDOW_POS_X);PREPMAP( MAIN_WINDOW_POS_Y);PREPMAP( AUTO_AWAY);
+ PREPMAP(SMALL_SEND_BUFFER);PREPMAP( SOCKS_PORT);PREPMAP( SOCKS_RESOLVE);PREPMAP( KEEP_LISTS);PREPMAP( AUTO_KICK);PREPMAP( QUEUEFRAME_SHOW_TREE);
+ PREPMAP(COMPRESS_TRANSFERS);PREPMAP( SHOW_PROGRESS_BARS);PREPMAP( SFV_CHECK);PREPMAP( MAX_TAB_ROWS);PREPMAP( AUTO_UPDATE_LIST);
+ PREPMAP(MAX_COMPRESSION);PREPMAP( FINISHED_DIRTY);PREPMAP( QUEUE_DIRTY);PREPMAP( TAB_DIRTY);PREPMAP( ANTI_FRAG);PREPMAP( MDI_MAXIMIZED);PREPMAP( NO_AWAYMSG_TO_BOTS);
+ PREPMAP(SKIP_ZERO_BYTE);PREPMAP( ADLS_BREAK_ON_FIRST);PREPMAP( TAB_COMPLETION);PREPMAP(HUB_USER_COMMANDS);PREPMAP( AUTO_SEARCH_AUTO_MATCH);PREPMAP( UPLOAD_BAR_COLOR);
+ PREPMAP( DOWNLOAD_BAR_COLOR);PREPMAP( LOG_SYSTEM);PREPMAP(LOG_FILELIST_TRANSFERS);PREPMAP( SEND_UNKNOWN_COMMANDS);PREPMAP( MAX_HASH_SPEED);PREPMAP( OPEN_USER_CMD_HELP);
+PREPMAP(GET_USER_COUNTRY);PREPMAP( FAV_SHOW_JOINS);PREPMAP( LOG_STATUS_MESSAGES);PREPMAP( SHOW_STATUSBAR);
+ PREPMAP(
+ SHOW_TOOLBAR);PREPMAP( SHOW_TRANSFERVIEW);PREPMAP( POPUNDER_PM);PREPMAP( POPUNDER_FILELIST);PREPMAP( MAGNET_ASK);PREPMAP( MAGNET_ACTION);PREPMAP( MAGNET_REGISTER);PREPMAP(
+ ADD_FINISHED_INSTANTLY);PREPMAP( SETTINGS_USE_UPNP);PREPMAP( DONT_DL_ALREADY_SHARED);PREPMAP( SETTINGS_USE_CTRL_FOR_LINE_HISTORY);PREPMAP( CONFIRM_HUB_REMOVAL);PREPMAP(
+ SETTINGS_OPEN_NEW_WINDOW);PREPMAP( UDP_PORT);PREPMAP( SEARCH_ONLY_TTH);PREPMAP( SHOW_LAST_LINES_LOG);PREPMAP( CONFIRM_ITEM_REMOVAL);PREPMAP(
+ ADVANCED_RESUME);PREPMAP( ADC_DEBUG);PREPMAP( TOGGLE_ACTIVE_WINDOW);PREPMAP( SEARCH_HISTORY);PREPMAP( SET_MINISLOT_SIZE);PREPMAP(
+ PRIO_HIGHEST_SIZE);PREPMAP( PRIO_HIGH_SIZE);PREPMAP( PRIO_NORMAL_SIZE);PREPMAP( PRIO_LOW_SIZE);PREPMAP( PRIO_LOWEST);PREPMAP(
+ OPEN_PUBLIC);PREPMAP( OPEN_FAVORITE_HUBS);PREPMAP( OPEN_FAVORITE_USERS);PREPMAP( OPEN_QUEUE);PREPMAP( OPEN_FINISHED_DOWNLOADS);PREPMAP(
+ OPEN_FINISHED_UPLOADS);PREPMAP( OPEN_SEARCH_SPY);PREPMAP( OPEN_NETWORK_STATISTICS);PREPMAP( OPEN_NOTEPAD);
+
+ // Int64
+ PREPMAP(TOTAL_UPLOAD);
+ PREPMAP(TOTAL_DOWNLOAD);
+}
diff --git a/backend/SettingsMapper.h b/backend/SettingsMapper.h
new file mode 100644
index 0000000..94273e8
--- /dev/null
+++ b/backend/SettingsMapper.h
@@ -0,0 +1,26 @@
+#ifndef SETTINGSMAPPER_H__
+#define SETTINGSMAPPER_H__
+
+#include <dcpp/stdinc.h>
+#include <dcpp/DCPlusPlus.h>
+#include <dcpp/SettingsManager.h>
+#include <map>
+#include <boost/any.hpp>
+using namespace std;
+
+//! Maps settings between dc-qt and dc++ format, keys have same names but as strings.
+class SettingsMapper
+{
+ public:
+ boost::any getSetting(const string& key);
+ void setSetting(const string& key,const boost::any& value);
+ static SettingsMapper* instance() {if(!inst) inst=new SettingsMapper;return inst;}
+private:
+ static SettingsMapper* inst;
+ SettingsMapper();
+ map<string,int> sm;
+};
+
+
+
+#endif
diff --git a/backend/Stack.h b/backend/Stack.h
new file mode 100644
index 0000000..e3faed7
--- /dev/null
+++ b/backend/Stack.h
@@ -0,0 +1,52 @@
+#ifndef STACK_H__
+#define STACK_H__
+
+template<typename T>
+class Stack
+{
+public:
+ Stack() {
+ m_data = new T[1000];
+ m_size = 1000;
+ m_top = 0;
+ }
+
+ Stack(int iSize) {
+ m_data = new T[iSize];
+ m_size = iSize;
+ m_top = 0;
+ }
+
+ ~Stack() {delete[] m_data;}
+
+ T pop() {
+ return m_data[--m_top];
+ }
+ void push(T d)
+ {
+ m_data[m_top++] = d;
+ if(m_top < m_size) return;
+ expand();
+ }
+ int size() {return m_top;}
+ int capacity() {return m_size;}
+
+
+private:
+ T* m_data;
+ int m_top;
+ int m_size;
+
+ void expand() {
+ T* newdata = new T[2*m_size];
+ memcpy(newdata,m_data,sizeof(T)*m_size);
+ delete[] m_data;
+ m_data = newdata;
+ m_size*=2;
+ }
+};
+
+
+
+
+#endif
diff --git a/backend/TransferManager.cpp b/backend/TransferManager.cpp
new file mode 100644
index 0000000..21814ea
--- /dev/null
+++ b/backend/TransferManager.cpp
@@ -0,0 +1,282 @@
+#include "TransferManager.h"
+#include "ClientNotifier.h"
+#include "SessionManager.h"
+#include <dcpp/QueueManager.h>
+#include <dcpp/ConnectionManager.h>
+#include "Selecter.h"
+#include "CommonTypes.h"
+#include "BackendUtil.h"
+#include <iostream>
+#include <boost/thread/recursive_mutex.hpp>
+
+using namespace std;
+
+namespace dcqt_backend {
+
+TransferManager::TransferManager()
+{
+ UploadManager::getInstance()->addListener(this);
+ DownloadManager::getInstance()->addListener(this);
+ QueueManager::getInstance()->addListener(this);
+ FinishedManager::getInstance()->addListener(this);
+ if( SETTING(CONNECTION_TYPE) == SettingsManager::CONNECTION_ACTIVE ) {
+ ConnectionManager::getInstance()->setPort( (short)SETTING(IN_PORT));
+ Selecter::doit(ConnectionManager::getInstance()->getServerSocket());
+ }
+}
+
+TransferManager::~TransferManager()
+{
+ UploadManager::getInstance()->removeListener(this);
+ DownloadManager::getInstance()->removeListener(this);
+ QueueManager::getInstance()->removeListener(this);
+ FinishedManager::getInstance()->removeListener(this);
+ Selecter::quit();
+}
+
+void TransferManager::downloadFile(const string& file,int64_t size,int userid, const string& dir, const string& tth)
+{
+ ::User::Ptr user = SessionManager::instance()->getUserIdMap()[userid];
+
+ // The user pointer could be 0 because it has been released.
+ // TODO we could really make use of a return status value here
+ if(!user) {
+ return;
+ }
+ // If dir is empty we use the default directory
+ string target = !dir.empty() ? dir : SETTING (DOWNLOAD_DIRECTORY);
+
+ target += Util::getFileName(BackendUtil::linuxSeparator (file));
+ // HERE we COULD catch an exception and return false and propagate that back to the GUI.
+
+ try {
+ if ( size == -1 ) {
+ string winFile = BackendUtil::windowsSeparator(file);
+ cout << "#################" << endl <<
+ "File: " << winFile.c_str() <<
+ " Nick: " << user->getNick().c_str() <<
+ " Target " << target.c_str() << endl;
+ QueueManager::getInstance()->addDirectory( BackendUtil::windowsSeparator(file), user, target );
+ } else {
+ TTHValue tthvalue(tth);
+ QueueManager::getInstance()->add
+ ( file, size, user, target, &tthvalue, QueueItem::FLAG_RESUME);// | QueueItem::FLAG_SOURCE_UTF8);
+ }
+ }
+ catch( const QueueException& qe ) {
+ }
+ catch( const FileException& fe ) {
+ }
+}
+
+void TransferManager::getUserFileList( int userId )
+{
+ try {
+
+ ::User::Ptr user = SessionManager::instance()->getUserIdMap()[userId];
+
+ if ( user ) {
+ QueueManager::getInstance()->addList(user, QueueItem::FLAG_NORMAL);
+ fileListRequests.push_back( userId );
+ } else {
+ }
+ }
+ catch( const QueueException& qe ) {
+ }
+ catch( const FileException& fe ) {
+ }
+}
+
+void TransferManager::forceConnect(int userId)
+{
+ boost::recursive_mutex::scoped_lock sl(SessionManager::instance()->sessionLock);
+ //SessionManager::instance()->aquireLock();
+ ::User::Ptr user = SessionManager::instance()->getUserIdMap()[userId];
+ try {
+ if(user) user->connect();
+ } catch (...) {}
+ //SessionManager::instance()->releaseLock();
+}
+
+void TransferManager::on(UploadManagerListener::Starting, Upload* u) throw()
+{
+ uploadStatus[u] = UPLOAD_STARTING; //TODO t3mp solution, mem leaks and no throttling
+ ClientNotifier::instance()->transferEvent( ClientNotifier::STARTING, u );
+}
+
+void TransferManager::on(UploadManagerListener::Tick, const Upload::List& ul) throw()
+{
+ ClientNotifier::instance()->transferTick( ul,uploadStatus );
+}
+
+void TransferManager::on(UploadManagerListener::Complete, Upload* u) throw()
+{
+ uploadStatus[u] = UPLOAD_COMPLETE;
+ ClientNotifier::instance()->transferEvent( ClientNotifier::COMPLETE, u );
+}
+
+void TransferManager::on(UploadManagerListener::Failed, Upload* u, const string& s) throw() {
+
+ uploadStatus[u] = UPLOAD_COMPLETE;
+ ClientNotifier::instance()->transferEvent( u, s );
+}
+
+void TransferManager::on(DownloadManagerListener::Starting, Download* d) throw()
+{
+ ClientNotifier::instance()->transferEvent( ClientNotifier::STARTING, d );
+}
+
+void TransferManager::on(DownloadManagerListener::Tick, const Download::List& dl) throw()
+{
+ ClientNotifier::instance()->transferTick( dl );
+}
+
+
+void TransferManager::on(DownloadManagerListener::Complete, Download* d) throw()
+{
+ ClientNotifier::instance()->transferEvent( ClientNotifier::COMPLETE, d );
+}
+
+
+void TransferManager::on(DownloadManagerListener::Failed, Download* d, const string& s) throw()
+{
+ ClientNotifier::instance()->transferEvent( d, s );
+}
+
+void TransferManager::on(QueueManagerListener::Added, QueueItem* item) throw()
+{
+ int qid = queueItemIds.getId(item);
+ ClientNotifier::instance()->queueEvent( ClientNotifier::QUEUE_ADD, item, qid);
+}
+
+void TransferManager::on(QueueManagerListener::Finished, QueueItem* item) throw()
+{
+ int qid = queueItemIds.getId(item);
+ ClientNotifier::instance()->queueEvent( ClientNotifier::QUEUE_FINISH, item, qid);
+}
+
+void TransferManager::on(QueueManagerListener::Removed, QueueItem* item) throw()
+{
+ int qid = queueItemIds.getId(item);
+ ClientNotifier::instance()->queueEvent( ClientNotifier::QUEUE_REMOVE, item, qid);
+ queueItemIds.release(item);
+}
+
+void TransferManager::on(QueueManagerListener::Moved, QueueItem*) throw()
+{
+}
+
+void TransferManager::on(QueueManagerListener::SourcesUpdated, QueueItem* item) throw()
+{
+ int qid = queueItemIds.getId(item);
+ ClientNotifier::instance()->queueEvent( ClientNotifier::QUEUE_SOURCE_UPDATE, item, qid);
+}
+
+void TransferManager::on(QueueManagerListener::StatusUpdated, QueueItem* item) throw()
+{
+ int qid = queueItemIds.getId(item);
+ ClientNotifier::instance()->queueEvent( ClientNotifier::QUEUE_STATUS_UPDATE, item, qid);
+}
+
+void TransferManager::on(QueueManagerListener::SearchStringUpdated, QueueItem*) throw()
+{
+}
+
+void TransferManager::on(QueueManagerListener::PartialList, const ::User::Ptr&, const string&) throw()
+{
+}
+
+
+void TransferManager::on(FinishedManagerListener::AddedDl, FinishedItem* item) throw()
+{
+ int id = finishedItemIds.getId( item );
+ ClientNotifier::instance()->finishedEvent(DOWNLOAD,id,item);
+}
+
+void TransferManager::on(FinishedManagerListener::RemovedDl, FinishedItem* item) throw()
+{
+ int id = finishedItemIds.getId( item );
+ ClientNotifier::instance()->finishedEvent(DOWNLOAD,id);
+ finishedItemIds.release(item);
+}
+
+void TransferManager::on(FinishedManagerListener::RemovedAllDl) throw()
+{
+ ClientNotifier::instance()->finishedEvent(DOWNLOAD);
+}
+
+void TransferManager::on(FinishedManagerListener::AddedUl, FinishedItem* item) throw()
+{
+ int id = finishedItemIds.getId( item );
+ ClientNotifier::instance()->finishedEvent(UPLOAD,id,item);
+}
+
+void TransferManager::on(FinishedManagerListener::RemovedUl, FinishedItem* item) throw()
+{
+ int id = finishedItemIds.getId( item );
+ ClientNotifier::instance()->finishedEvent(UPLOAD,id);
+ finishedItemIds.release(item);
+}
+
+void TransferManager::on(FinishedManagerListener::RemovedAllUl) throw()
+{
+ ClientNotifier::instance()->finishedEvent(UPLOAD);
+}
+
+
+bool TransferManager::isFileListRequested( int uId ) {
+ bool ret = false;
+ vector<int>::const_iterator it = fileListRequests.begin();
+
+ while( it != fileListRequests.end() ) {
+ if( *it == uId ) {
+ ret = true;
+ break;
+ }
+ it++;
+ }
+
+ return ret;
+}
+
+void TransferManager::removeFileListRequest( int uId )
+{
+
+ vector<int>::iterator it = fileListRequests.begin();
+
+ // Loop through the whole list of requests
+ while( it != fileListRequests.end() ) {
+ if( *it == uId ) {
+ // Remove the item from the list
+ it = fileListRequests.erase(it);
+ } else {
+ it++;
+ }
+ }
+}
+
+void TransferManager::removeQueueItem(int qid)
+{
+ // find ze item
+ if( queueItemIds.contains(qid) ) {
+ QueueItem::Ptr qptr = queueItemIds.get(qid);
+ QueueManager::getInstance()->remove(qptr->getTarget());
+ }
+}
+
+void TransferManager::removeSource(int qid, int uid)
+{
+ boost::recursive_mutex::scoped_lock sl(SessionManager::instance()->sessionLock);
+ if( queueItemIds.contains(qid) )
+ {
+ // Aquire the QueueItem
+ QueueItem::Ptr qptr = queueItemIds.get(qid);
+ // Aquire the User
+ ::User::Ptr uptr = SessionManager::instance()->getUserIdMap()[uid];
+ // Remove
+ QueueManager::getInstance()->removeSource(qptr->getTarget(),uptr,QueueItem::Source::FLAG_REMOVED);
+ }
+}
+
+
+}
diff --git a/backend/TransferManager.h b/backend/TransferManager.h
new file mode 100644
index 0000000..9f9829d
--- /dev/null
+++ b/backend/TransferManager.h
@@ -0,0 +1,75 @@
+#ifndef TRANSFER_MANAGER_H__
+#define TRANSFER_MANAGER_H__
+#include "../dcpp/stdinc.h"
+#include "../dcpp/DCPlusPlus.h"
+#include <dcpp/UploadManager.h>
+#include <dcpp/DownloadManager.h>
+#include <dcpp/QueueManagerListener.h>
+#include <dcpp/QueueItem.h>
+#include <dcpp/FinishedManager.h>
+#include <map>
+#include "IdAssigner.h"
+
+namespace dcqt_backend {
+
+class TransferManager :
+ public UploadManagerListener,
+ public DownloadManagerListener,
+ public QueueManagerListener,
+ public FinishedManagerListener,
+ public Singleton<TransferManager>
+{
+public:
+ void on(UploadManagerListener::Starting, Upload*) throw();
+ void on(UploadManagerListener::Tick, const Upload::List&) throw();
+ void on(UploadManagerListener::Complete, Upload*) throw();
+ void on(UploadManagerListener::Failed, Upload*, const string&) throw();
+ void on(DownloadManagerListener::Starting, Download*) throw();
+ void on(DownloadManagerListener::Tick, const Download::List&) throw();
+ void on(DownloadManagerListener::Complete, Download*) throw();
+ void on(DownloadManagerListener::Failed, Download*, const string&) throw();
+
+ void on(QueueManagerListener::Added, QueueItem*) throw();
+ void on(QueueManagerListener::Finished, QueueItem*) throw();
+ void on(QueueManagerListener::Removed, QueueItem*) throw();
+ void on(QueueManagerListener::Moved, QueueItem*) throw();
+ void on(QueueManagerListener::SourcesUpdated, QueueItem*) throw();
+ void on(QueueManagerListener::StatusUpdated, QueueItem*) throw();
+ void on(QueueManagerListener::SearchStringUpdated, QueueItem*) throw();
+ void on(QueueManagerListener::PartialList, const ::User::Ptr&, const string&) throw();
+
+ void on(FinishedManagerListener::AddedDl, FinishedItem*) throw();
+ void on(FinishedManagerListener::RemovedDl, FinishedItem*) throw();
+ void on(FinishedManagerListener::RemovedAllDl) throw();
+ void on(FinishedManagerListener::AddedUl, FinishedItem*) throw();
+ void on(FinishedManagerListener::RemovedUl, FinishedItem*) throw();
+ void on(FinishedManagerListener::RemovedAllUl) throw();
+
+
+ TransferManager();
+ ~TransferManager();
+
+ void downloadFile(const string& file,int64_t size,int userid, const string& dir, const string& tth );
+ void getUserFileList( int userId );
+ void removeQueueItem( int id );
+ void forceConnect( int userId );
+ void removeSource(int qid, int uid);
+
+ typedef map<Upload*,int> UploadStatusMap;
+ enum {UPLOAD_STARTING=1,UPLOAD_COMPLETE=2};
+ IdAssigner<QueueItem::Ptr> queueItemIds;
+ IdAssigner<FinishedItem*> finishedItemIds;
+
+ bool isFileListRequested( int uId );
+ void removeFileListRequest( int uId );
+
+protected:
+ UploadStatusMap uploadStatus;
+
+ // Needed to keep track of which users we have requested file lists for,
+ // so we know if we should send the list to clients or not when we get notified that a
+ // file list has been downloaded
+ vector<int> fileListRequests;
+};
+}
+#endif
diff --git a/backend/User.h b/backend/User.h
new file mode 100644
index 0000000..e3dc15d
--- /dev/null
+++ b/backend/User.h
@@ -0,0 +1,21 @@
+#ifndef USER_H__
+#define USER_H__
+
+namespace dcqt_backend {
+
+struct User
+{
+ User() : id(0) {}
+ int id;
+ string nick;
+ int flags;
+ string email;
+ string desc;
+ string connection;
+ string tag;
+ int64_t shared;
+ int slots;
+};
+
+}
+#endif
diff --git a/backend/commandhandlers.cpp b/backend/commandhandlers.cpp
new file mode 100644
index 0000000..84cd1bc
--- /dev/null
+++ b/backend/commandhandlers.cpp
@@ -0,0 +1,554 @@
+/*
+ * commandhandlers.cpp
+ * backend
+ *
+ * Created by Mikael Gransell on 2/28/06.
+ * Copyright 2006 __MyCompanyName__. All rights reserved.
+ *
+ */
+
+#include "commandhandlers.h"
+
+#include "SessionManager.h"
+#include "ShareManager.h"
+#include "TransferManager.h"
+#include "QueueManager.h"
+#include "RpcArgumentFactory.h"
+#include "SettingsMapper.h"
+#include "log.h"
+
+#include <iostream>
+#include <boost/thread/recursive_mutex.hpp>
+
+using namespace std;
+
+namespace dcqt_backend
+{
+
+
+void AuthenticateCmdHandler::handleCommand(int clientId, const std::list<boost::any>& params)
+{
+ cout << "authenticate" << endl;
+ // Check if clientid is allowed to authenticate
+ time_t currt = time(NULL);
+ if( clientAccessMap.find(clientId)==clientAccessMap.end() ) clientAccessMap[clientId] = currt;
+ else {
+ time_t lastAttempt = clientAccessMap[clientId];
+ if( currt - lastAttempt < 2 ) {
+ logger->warn( boost::format("Authenticate: client rejected (too many tries in too short time)"));
+ return;
+ }
+ }
+
+ try{
+ string p = boost::any_cast<string>(params.front());
+
+ if(p==password) {
+ // register clientId as an authenticated client.
+ driver->setClientAuthenticated(clientId,true);
+ logger->info( boost::format("Authenticate: login success" ));
+ }
+ else {
+ // Wrong password, do something clever here maybe.
+ logger->warn( boost::format("Authenticate: wrong password"));
+ }
+ }
+ catch(const boost::bad_any_cast& e) {}
+}
+
+void TestBoolHandler::handleCommand(int cid,const list<boost::any>& params) {
+ bool b = boost::any_cast<bool>(params.front());
+ cout << "bool: " << b << endl;
+}
+
+void CreateSessionCmdHandler::handleCommand(int clientId,
+ const list<boost::any>& params)
+{
+ try {
+ // Params inly include the url that we should connect to
+ string url = boost::any_cast<string>(params.front());
+ SessionManager::instance()->createSession(url);
+ }
+ catch( const boost::bad_any_cast& e ) {
+ cout << "Invalid params to createSession. " << e.what() << endl;
+ }
+}
+
+
+void CloseSessionCmdHandler::handleCommand(int clientId,
+ const list<boost::any>& params)
+{
+ try {
+ int sessionId = boost::any_cast<int>(params.front());
+ SessionManager::instance()->closeSession(sessionId);
+ }
+ catch( const boost::bad_any_cast& e ) {
+ cout << "Invalid param to closeSession. " << e.what() << endl;
+ }
+}
+
+void RequestRunningSessionsCmdHandler::handleCommand(int clientId,
+ const list<boost::any>& params)
+{
+ try {
+ // Will contain the shares
+ rpc::CmdPtr cmd( new list<boost::any> );
+ cmd->push_back( string("runningSessions") );
+
+ list< boost::any > sessionList;
+
+ //SessionManager::instance()->aquireLock();
+ boost::recursive_mutex::scoped_lock sl(SessionManager::instance()->sessionLock);
+ map<int,Session*> *sm = SessionManager::instance()->getSessionMap();
+
+ map<int,Session*>::iterator it = sm->begin();
+ int index = 0;
+ while(it != sm->end() ) {
+ Session *session = it->second;
+
+ list<boost::any> sessionInfo;
+
+ sessionInfo.push_back(session->getId());
+ sessionInfo.push_back(session->getHubName());
+ sessionInfo.push_back(session->getUrl());
+
+ list<boost::any> userList;
+ hash_set<int>* users = session->getUsers();
+ hash_set<int>::iterator userit= users->begin();
+ while(userit!=users->end()) {
+ int userId = (*userit);
+ dcqt_backend::User& u = SessionManager::instance()->getUserPtrMap()[SessionManager::instance()->getUserIdMap()[userId]];
+ userList.push_back(rpc_argument::createUser(u));
+ userit++;
+ }
+
+ sessionInfo.push_back(userList);
+
+ sessionList.push_back(sessionInfo);
+
+ index++;
+ it++;
+ } // end building session list
+ //SessionManager::instance()->releaseLock();
+
+ // Build the queue list
+ list<boost::any> queueList;
+ QueueItem::StringMap& queue = QueueManager::getInstance()->lockQueue();
+ for(QueueItem::StringMap::iterator it=queue.begin();it!=queue.end();it++) {
+ QueueItem* item = it->second;
+ int itemId = TransferManager::getInstance()->queueItemIds.getId(item);
+ queueList.push_back( rpc_argument::createQueueItem( item, itemId ));
+ }
+ QueueManager::getInstance()->unlockQueue();
+
+ // Add the two lists
+ cmd->push_back( sessionList );
+ cmd->push_back( queueList );
+
+ // Send the info back to the client that requested it
+ cmdSenderMethod( clientId, cmd );
+ }
+ catch( const boost::bad_any_cast& ce ) {
+ cout << "Invalid params to search. " << ce.what() << endl;
+ }
+ catch( const boost::bad_function_call& fe ) {
+ cout << "Invalid sender method set" << fe.what() << endl;
+ }
+}
+
+
+void SendPasswordCmdHandler::handleCommand(int clientId,
+ const list<boost::any>& params)
+{
+ try {
+ list<boost::any>::const_iterator it = params.begin();
+
+ int sessionId = boost::any_cast<int>(*it);
+ ++it;
+ string pass = boost::any_cast<string>(*it);
+
+ Session* session = SessionManager::instance()->getSession(sessionId);
+ if( session != NULL ) {
+ session->password(pass);
+ }
+ else {
+ cout << "Invalid session id(" << sessionId << ")" << endl;
+ }
+ }
+ catch( const boost::bad_any_cast& e ) {
+ cout << "Invalid param to sendChat. " << e.what() << endl;
+ }
+}
+
+
+void SendChatCmdHandler::handleCommand(int clientId,
+ const list<boost::any>& params)
+{
+ try {
+
+ list<boost::any>::const_iterator it = params.begin();
+
+ int sessionId = boost::any_cast<int>(*it);
+ ++it;
+ string msg = boost::any_cast<string>(*it);
+
+ Session* s = SessionManager::instance()->getSession(sessionId);
+ if( s != NULL ) {
+ s->sendChat(msg);
+ }
+ else {
+ cout << "Invalid session id(" << sessionId << ")" << endl;
+ }
+ }
+ catch( const boost::bad_any_cast& e ) {
+ cout << "Invalid param to sendChat. " << e.what() << endl;
+ }
+}
+
+void SearchCmdHandler::handleCommand(int clientId,
+ const list<boost::any>& params)
+{
+ try {
+ list<boost::any>::const_iterator it = params.begin();
+
+ int sessionId = boost::any_cast<int>(*it);
+ ++it;
+ string searchString = boost::any_cast<string>(*it);
+ ++it;
+ int64_t size = boost::any_cast<int64_t>(*it);
+ ++it;
+ int sizeMode = boost::any_cast<int>(*it);
+ ++it;
+ int typeMode = boost::any_cast<int>(*it);
+
+ Session* session = SessionManager::instance()->getSession(sessionId);
+ if( session != NULL ) {
+ // We dont send the token at the moment. Need to find
+ // out what it means first
+ session->search( sizeMode, size, typeMode, searchString, "" );
+ }
+ else {
+ //cout << "Invalid session id(" << sessionId << ")"
+ //<< endl;
+
+ // Since we search all hubs anyway at the moment, we
+ // might as well just search here.
+ map<int,Session*>* arne = SessionManager::instance()->getSessionMap();
+ map<int,Session*>::iterator sit = arne->begin();
+ Session* session = sit->second;
+ session->search(sizeMode, size, typeMode, searchString, "");
+
+ }
+ }
+ catch( const boost::bad_any_cast& e ) {
+ cout << "Invalid params to search. " << e.what() << endl;
+ }
+}
+
+void GetSharedDirectoriesCmdHandler::handleCommand(int clientId,
+ const list<boost::any>& params)
+{
+ try {
+ // Will contain the shares
+ rpc::CmdPtr cmd( new list<boost::any> );
+ cmd->push_back( string("sharedDirs") );
+
+ list< boost::any > shares;
+
+ StringPairList dirs = ShareManager::getInstance()->getDirectories();
+ for(StringPairList::iterator it = dirs.begin(); it != dirs.end(); it++) {
+ string vdir = it->first;
+ string sdir = it->second;
+ int64_t size = ShareManager::getInstance()->getShareSize(it->second);
+
+ list<boost::any> share;
+ share.push_back( vdir );
+ share.push_back( sdir );
+ share.push_back( size );
+
+ shares.push_back(share);
+ }
+
+ cmd->push_back( shares );
+
+ // Send the info back to the client that requested it
+ cmdSenderMethod( clientId, cmd );
+ }
+ catch( const boost::bad_any_cast& ce ) {
+ cout << "Invalid params to search. " << ce.what() << endl;
+ }
+ catch( const boost::bad_function_call& fe ) {
+ cout << "Invalid sender method set" << fe.what() << endl;
+ }
+}
+
+void AddShareCmdHandler::handleCommand(int clientId,
+ const list<boost::any>& params)
+{
+ try {
+ list<boost::any>::const_iterator it = params.begin();
+ string name = boost::any_cast<string>(*it);
+ ++it;
+ string dir = boost::any_cast<string>(*it);
+
+ ShareManager::getInstance()->addDirectory(dir,name);
+ }
+ catch( const ShareException& se ) {
+ cout << "Could not add share. " << se.getError() << endl;
+ }
+ catch( const boost::bad_any_cast& ce ) {
+ cout << "Invalid params to addShare. " << ce.what() << endl;
+ }
+}
+
+void RemoveShareCmdHandler::handleCommand(int clientId,
+ const list<boost::any>& params)
+{
+ try {
+ string name = boost::any_cast<string>(params.front());
+
+ ShareManager::getInstance()->removeDirectory(name);
+ }
+ catch( const boost::bad_any_cast& ce ) {
+ cout << "Invalid params to removeShare. " << ce.what() << endl;
+ }
+}
+
+void DownloadFileCmdHandler::handleCommand(int clientId,
+ const list<boost::any>& params)
+{
+ try {
+ list<boost::any>::const_iterator it = params.begin();
+ string file = boost::any_cast<string>(*it);
+ ++it;
+ int64_t size = boost::any_cast<int64_t>(*it);
+ ++it;
+ int userid = boost::any_cast<int>(*it);
+ ++it;
+ string dir = boost::any_cast<string>(*it);
+ ++it;
+ string tth = boost::any_cast<string>(*it);
+
+ //SessionManager::instance()->aquireLock();
+ boost::recursive_mutex::scoped_lock sl(SessionManager::instance()->sessionLock);
+ TransferManager::getInstance()->downloadFile(file,size,userid,dir,tth);
+ //SessionManager::instance()->releaseLock();
+ }
+ catch( const boost::bad_any_cast& ce ) {
+ cout << "Invalid params to downloadFile. " << ce.what() << endl;
+ }
+}
+
+void GetUserFileListCmdHandler::handleCommand(int clientId,
+ const list<boost::any>& params)
+{
+ try {
+ int userId = boost::any_cast<int>(params.front());
+ //SessionManager::instance()->aquireLock();
+ boost::recursive_mutex::scoped_lock sl(SessionManager::instance()->sessionLock);
+ TransferManager::getInstance()->getUserFileList( userId );
+ //SessionManager::instance()->releaseLock();
+ }
+ catch( const boost::bad_any_cast& ce ) {
+ cout << "Invalid params to getUserFileList. " << ce.what() << endl;
+ }
+}
+
+void GetHubListCmdHandler::handleCommand(int clientId,
+ const list<boost::any>& params)
+{
+ try {
+ bool refresh = boost::any_cast<bool>(params.front());
+ if( !refresh ) {
+
+ rpc::CmdPtr cmd( new list<boost::any> );
+ cmd->push_back( string("newHubList") );
+
+ cmd->push_back( rpc_argument::createHubList(SessionManager::instance()->getPublicHubs()) );
+
+ // Send the info back to the client that requested it
+ cmdSenderMethod( clientId, cmd );
+ }
+ else {
+ SessionManager::instance()->refreshHublist();
+ }
+ }
+ catch( const boost::bad_function_call& fe ) {
+ cout << "Invalid sender method set" << fe.what() << endl;
+ }
+ catch( const boost::bad_any_cast& ce ) {
+ cout << "Invalid params to getHubList. " << ce.what() << endl;
+ }
+}
+
+
+void ConnectToUserCmdHandler::handleCommand(int clientId,
+ const list<boost::any>& params)
+{
+ try {
+ int userId = boost::any_cast<int>(params.front());
+ TransferManager::getInstance()->forceConnect(userId);
+ }
+ catch( const boost::bad_any_cast& ce ) {
+ cout << "Invalid params to connectToUser. " << ce.what() << endl;
+ }
+}
+
+void RemoveQueueItemCmdHandler::handleCommand(int clientId,
+ const list<boost::any>& params)
+{
+ try {
+ int qid = boost::any_cast<int>(params.front());
+ TransferManager::getInstance()->removeQueueItem(qid);
+ }
+ catch( const boost::bad_any_cast& ce ) {
+ cout << "Invalid params to removeQueueItem. " << ce.what() << endl;
+ }
+}
+
+void AddFavoriteHubCmdHandler::handleCommand(int clientId,
+ const list<boost::any>& params)
+{
+ try {
+ list<boost::any>::const_iterator it = params.begin();
+
+ FavoriteHubEntry he;
+ he.setNick(boost::any_cast<string>(*it));
+ ++it;
+ he.setUserDescription(boost::any_cast<string>(*it));
+ ++it;
+ he.setName(boost::any_cast<string>(*it));
+ ++it;
+ he.setServer(boost::any_cast<string>(*it));
+ ++it;
+ he.setDescription(boost::any_cast<string>(*it));
+ ++it;
+ he.setPassword(boost::any_cast<string>(*it));
+ ++it;
+ he.setConnect(boost::any_cast<bool>(*it));
+ SessionManager::instance()->addFavouriteHub(he);
+ }
+ catch( const boost::bad_any_cast& ce ) {
+ cout << "Invalid params to addFavouriteHub. " << ce.what() << endl;
+ }
+}
+
+void RemoveFavoriteHubCmdHandler::handleCommand(int clientId,
+ const list<boost::any>& params)
+{
+ try {
+ string server = boost::any_cast<string>(params.front());
+ SessionManager::instance()->removeFavouriteHub(server);
+ }
+ catch( const boost::bad_any_cast& ce ) {
+ cout << "Invalid params to removeFavouriteHub. " << ce.what() << endl;
+ }
+}
+
+
+void GetSettingsHandler::handleCommand(int clientId,const list<boost::any>& params)
+{
+ try {
+ list<boost::any> settings = boost::any_cast< list<boost::any> >(params.front());
+ list<boost::any>::const_iterator it = settings.begin();
+ list<boost::any> result;
+
+ // Put all requested settings in a result list
+ while(it!=settings.end()) {
+ result.push_back(SettingsMapper::instance()->getSetting( boost::any_cast<string>(*it) ));
+ it++;
+ }
+
+ // Send the result back together with the original key list (easier handling on client side)
+ rpc::CmdPtr cmd( new list<boost::any> );
+ cmd->push_back( string("settingsInfo") );
+ cmd->push_back( settings );
+ cmd->push_back( result );
+ cmdSenderMethod( clientId, cmd );
+ }
+ catch( const boost::bad_function_call& fe ) {
+ cout << "Invalid sender method set" << fe.what() << endl;
+ }
+ catch( const boost::bad_any_cast& ce ) {
+ cout << "Invalid params to getHubList. " << ce.what() << endl;
+ }
+}
+
+void SetSettingsHandler::handleCommand( int clientId, const list<boost::any>& params)
+{
+ try {
+ list<boost::any>::const_iterator it = params.begin();
+ list<boost::any> keys = boost::any_cast< list<boost::any> >(*it);
+ it++;
+ list<boost::any> values = boost::any_cast< list<boost::any> >(*it);
+
+ it = keys.begin();
+ list<boost::any>::const_iterator vit = values.begin();
+
+ while(it!=keys.end()) {
+ SettingsMapper::instance()->setSetting(boost::any_cast<string>(*it),*vit);
+ it++;
+ vit++;
+ }
+ }
+ catch( const boost::bad_any_cast& ce ) {
+ cout << "Invalid params to getHubList. " << ce.what() << endl;
+ }
+}
+
+void GetFavHubsHandler::handleCommand( int clientId, const list<boost::any>& params)
+{
+ try {
+ FavoriteHubEntry::List& favHubs = SessionManager::instance()->getFavouriteHubs();
+ list<boost::any> result;
+
+ for(FavoriteHubEntry::List::iterator it=favHubs.begin();it!=favHubs.end(); it++)
+ {
+ list<boost::any> favStruct;
+ favStruct.push_back((*it)->getNick());
+ favStruct.push_back((*it)->getUserDescription());
+ favStruct.push_back((*it)->getName());
+ favStruct.push_back((*it)->getServer());
+ favStruct.push_back( (*it)->getDescription());
+ favStruct.push_back((*it)->getPassword());
+ favStruct.push_back((*it)->getConnect());
+ result.push_back(favStruct);
+ }
+ rpc::CmdPtr cmd( new list<boost::any> );
+ cmd->push_back( string("favouriteHubList") );
+ cmd->push_back( result );
+ cmdSenderMethod( clientId, cmd );
+
+ }
+ catch( const boost::bad_function_call& fe ) {
+ cout << "Invalid sender method set" << fe.what() << endl;
+ }
+ catch( const boost::bad_any_cast& ce ) {
+ cout << "Invalid params to getHubList. " << ce.what() << endl;
+ }
+
+}
+
+void RemoveSourceHandler::handleCommand( int clientId, const list<boost::any>& params)
+{
+ try {
+ list<boost::any>::const_iterator it = params.begin();
+ int qid = boost::any_cast<int>(*it);
+ it++;
+ int uid = boost::any_cast<int>(*it);
+ TransferManager::getInstance()->removeSource(qid,uid);
+ }
+ catch( const boost::bad_any_cast& ce ) {
+ cout << "Invalid params to removeSource. " << ce.what() << endl;
+ }
+
+}
+
+
+void DieCmdHandler::handleCommand( int clientId, const list<boost::any>& params)
+{
+ cout << "die" << endl;
+ driver->stopSender();
+}
+
+} \ No newline at end of file
diff --git a/backend/commandhandlers.h b/backend/commandhandlers.h
new file mode 100644
index 0000000..8561f80
--- /dev/null
+++ b/backend/commandhandlers.h
@@ -0,0 +1,375 @@
+/*
+ * commandhandlers.h
+ * backend
+ *
+ * Created by Mikael Gransell on 2/28/06.
+ * Copyright 2006 __MyCompanyName__. All rights reserved.
+ *
+ */
+
+
+#ifndef COMMAND_HANDLERS_H_
+#define COMMAND_HANDLERS_H_
+
+#include <string>
+#include <list>
+#include <map>
+
+#include <rpcdriver/rpccommandhandler.h>
+
+#include <rpcdriver/rpcdriver.h>
+namespace dcqt_backend
+{
+#define DEF_RPC_FUNC(classname,name,numparam) \
+class classname : public rpc::RpcCommandHandler \
+{\
+public:\
+classname(boost::function<void( int, const rpc::CmdPtr&)> sender ) : rpc::RpcCommandHandler( name, sender, NUM_PARAMS ) {}\
+virtual void handleCommand(int clientId,const std::list<boost::any>& params);\
+private:\
+static const short NUM_PARAMS = numparam;\
+};
+
+class AuthenticateCmdHandler : public rpc::RpcCommandHandler
+{
+ public:
+ AuthenticateCmdHandler( boost::function<void( int, const rpc::CmdPtr&)> sender, std::string& aPassword, rpc::RpcDriverPtr aDriver ) :
+ rpc::RpcCommandHandler("authenticate", sender, NUM_PARAMS ), password(aPassword),driver(aDriver) {}
+
+ virtual void handleCommand(int clientId, const std::list<boost::any>& params);
+ private:
+ static const short NUM_PARAMS = 1;
+ std::string password;
+ rpc::RpcDriverPtr driver;
+ std::map<int,time_t> clientAccessMap;
+};
+
+/**
+ * Handles the createSession command.
+ */
+class CreateSessionCmdHandler : public rpc::RpcCommandHandler
+{
+public:
+ CreateSessionCmdHandler( boost::function<void( int, const rpc::CmdPtr&)> sender )
+ : rpc::RpcCommandHandler( "createSession", sender, NUM_PARAMS )
+ {}
+
+ /**
+ * Extracts the url of the hub we should connect to and calls
+ * the session manager to connect to the given url.
+ * @param cmdName
+ */
+ virtual void handleCommand(int clientId,
+ const std::list<boost::any>& params);
+
+private:
+ /// The amount of params that we should receive
+ static const short NUM_PARAMS = 1;
+};
+
+/**
+* Handles the closeSession command.
+ */
+class CloseSessionCmdHandler : public rpc::RpcCommandHandler
+{
+public:
+ CloseSessionCmdHandler( boost::function<void( int, const rpc::CmdPtr&)> sender )
+ : rpc::RpcCommandHandler( "closeSession", sender, NUM_PARAMS )
+ {}
+
+ virtual void handleCommand(int clientId,
+ const std::list<boost::any>& params);
+
+private:
+ /// The amount of params that we should receive
+ static const short NUM_PARAMS = 1;
+};
+
+/**
+* Handles the requestRunningSessions command.
+ */
+class RequestRunningSessionsCmdHandler : public rpc::RpcCommandHandler
+{
+public:
+ RequestRunningSessionsCmdHandler( boost::function<void( int, const rpc::CmdPtr&)> sender )
+ : rpc::RpcCommandHandler( "requestRunningSessions", sender, NUM_PARAMS )
+ {}
+
+ virtual void handleCommand(int clientId,
+ const std::list<boost::any>& params);
+
+private:
+ /// The amount of params that we should receive
+ static const short NUM_PARAMS = 0;
+};
+
+/**
+ * Handles the getSharedDirectories command.
+ */
+class GetSharedDirectoriesCmdHandler : public rpc::RpcCommandHandler
+{
+public:
+ GetSharedDirectoriesCmdHandler( boost::function<void( int, const rpc::CmdPtr&)> sender )
+ : rpc::RpcCommandHandler( "getSharedDirectories", sender, NUM_PARAMS )
+ {}
+
+ virtual void handleCommand(int clientId,
+ const std::list<boost::any>& params);
+
+private:
+ /// The amount of params that we should receive
+ static const short NUM_PARAMS = 0;
+};
+
+/**
+ * Handles the sendPassword command.
+ */
+class SendPasswordCmdHandler : public rpc::RpcCommandHandler
+{
+public:
+ SendPasswordCmdHandler( boost::function<void( int, const rpc::CmdPtr&)> sender )
+ : rpc::RpcCommandHandler( "sendPassword", sender, NUM_PARAMS )
+ {}
+
+ virtual void handleCommand(int clientId,
+ const std::list<boost::any>& params);
+
+private:
+ /// The amount of params that we should receive
+ static const short NUM_PARAMS = 2;
+};
+
+
+/**
+* Handles the sendChat command.
+ */
+class SendChatCmdHandler : public rpc::RpcCommandHandler
+{
+public:
+ SendChatCmdHandler( boost::function<void( int, const rpc::CmdPtr&)> sender )
+ : rpc::RpcCommandHandler( "sendChat", sender, NUM_PARAMS )
+ {}
+
+ virtual void handleCommand(int clientId,
+ const std::list<boost::any>& params);
+
+private:
+ /// The amount of params that we should receive
+ static const short NUM_PARAMS = 2;
+};
+
+/**
+* Handles the sendChat command.
+ */
+class SearchCmdHandler : public rpc::RpcCommandHandler
+{
+public:
+ SearchCmdHandler( boost::function<void( int, const rpc::CmdPtr&)> sender )
+ : rpc::RpcCommandHandler( "search", sender, NUM_PARAMS )
+ {}
+
+ virtual void handleCommand(int clientId,
+ const std::list<boost::any>& params);
+
+private:
+ /// The amount of params that we should receive
+ static const short NUM_PARAMS = 5;
+};
+
+/**
+* Handles the addShare command.
+ */
+class AddShareCmdHandler : public rpc::RpcCommandHandler
+{
+public:
+ AddShareCmdHandler( boost::function<void( int, const rpc::CmdPtr&)> sender )
+ : rpc::RpcCommandHandler( "addShare", sender, NUM_PARAMS )
+ {}
+
+ virtual void handleCommand(int clientId,
+ const std::list<boost::any>& params);
+
+private:
+ /// The amount of params that we should receive
+ static const short NUM_PARAMS = 2;
+};
+
+/**
+* Handles the removeShare command.
+ */
+class RemoveShareCmdHandler : public rpc::RpcCommandHandler
+{
+public:
+ RemoveShareCmdHandler( boost::function<void( int, const rpc::CmdPtr&)> sender )
+ : rpc::RpcCommandHandler( "removeShare", sender, NUM_PARAMS )
+ {}
+
+ virtual void handleCommand(int clientId,
+ const std::list<boost::any>& params);
+
+private:
+ /// The amount of params that we should receive
+ static const short NUM_PARAMS = 1;
+};
+
+/**
+* Handles the downloadFile command.
+ */
+class DownloadFileCmdHandler : public rpc::RpcCommandHandler
+{
+public:
+ DownloadFileCmdHandler( boost::function<void( int, const rpc::CmdPtr&)> sender )
+ : rpc::RpcCommandHandler( "downloadFile", sender, NUM_PARAMS )
+ {}
+
+ virtual void handleCommand(int clientId,
+ const std::list<boost::any>& params);
+
+private:
+ /// The amount of params that we should receive
+ static const short NUM_PARAMS = 5;
+};
+
+/**
+* Handles the getUserFileList command.
+ */
+
+class GetUserFileListCmdHandler : public rpc::RpcCommandHandler
+{
+public:
+ GetUserFileListCmdHandler( boost::function<void( int, const rpc::CmdPtr&)> sender )
+ : rpc::RpcCommandHandler( "getUserFileList", sender, NUM_PARAMS )
+ {}
+
+ virtual void handleCommand(int clientId,
+ const std::list<boost::any>& params);
+
+private:
+ /// The amount of params that we should receive
+ static const short NUM_PARAMS = 1;
+};
+
+/**
+* Handles the getHubList command.
+ */
+class GetHubListCmdHandler : public rpc::RpcCommandHandler
+{
+public:
+ GetHubListCmdHandler( boost::function<void( int, const rpc::CmdPtr&)> sender )
+ : rpc::RpcCommandHandler( "getHubList", sender, NUM_PARAMS )
+ {}
+
+ virtual void handleCommand(int clientId,
+ const std::list<boost::any>& params);
+
+private:
+ /// The amount of params that we should receive
+ static const short NUM_PARAMS = 1;
+};
+
+class TestBoolHandler : public rpc::RpcCommandHandler
+{
+public:
+ TestBoolHandler(boost::function<void( int, const rpc::CmdPtr&)> sender )
+ : rpc::RpcCommandHandler( "testbool", sender, NUM_PARAMS ) {}
+
+ virtual void handleCommand(int clientId,const std::list<boost::any>& params);
+
+
+private:
+ /// The amount of params that we should receive
+ static const short NUM_PARAMS = 1;
+};
+
+class ConnectToUserCmdHandler : public rpc::RpcCommandHandler
+{
+public:
+ ConnectToUserCmdHandler(boost::function<void( int, const rpc::CmdPtr&)> sender )
+ : rpc::RpcCommandHandler( "connectToUser", sender, NUM_PARAMS ) {}
+
+ virtual void handleCommand(int clientId,const std::list<boost::any>& params);
+
+
+private:
+ /// The amount of params that we should receive
+ static const short NUM_PARAMS = 1;
+};
+
+class RemoveQueueItemCmdHandler : public rpc::RpcCommandHandler
+{
+public:
+ RemoveQueueItemCmdHandler(boost::function<void( int, const rpc::CmdPtr&)> sender )
+ : rpc::RpcCommandHandler( "removeQueueItem", sender, NUM_PARAMS ) {}
+
+ virtual void handleCommand(int clientId,const std::list<boost::any>& params);
+
+
+private:
+ /// The amount of params that we should receive
+ static const short NUM_PARAMS = 1;
+};
+
+
+class AddFavoriteHubCmdHandler : public rpc::RpcCommandHandler
+{
+public:
+ AddFavoriteHubCmdHandler(boost::function<void( int, const rpc::CmdPtr&)> sender )
+ : rpc::RpcCommandHandler( "addFavouriteHub", sender, NUM_PARAMS ) {}
+
+ virtual void handleCommand(int clientId,const std::list<boost::any>& params);
+
+
+private:
+ /// The amount of params that we should receive
+ static const short NUM_PARAMS = 7;
+};
+
+class RemoveFavoriteHubCmdHandler : public rpc::RpcCommandHandler
+{
+public:
+ RemoveFavoriteHubCmdHandler(boost::function<void( int, const rpc::CmdPtr&)> sender )
+ : rpc::RpcCommandHandler( "removeFavouriteHub", sender, NUM_PARAMS ) {}
+
+ virtual void handleCommand(int clientId,const std::list<boost::any>& params);
+
+
+private:
+ /// The amount of params that we should receive
+ static const short NUM_PARAMS = 1;
+};
+
+class GetFavHubsHandler : public rpc::RpcCommandHandler
+{
+public:
+ GetFavHubsHandler(boost::function<void( int, const rpc::CmdPtr&)> sender )
+ : rpc::RpcCommandHandler( "getFavouriteHubs", sender, NUM_PARAMS ) {}
+
+ virtual void handleCommand(int clientId,const std::list<boost::any>& params);
+
+
+private:
+ /// The amount of params that we should receive
+ static const short NUM_PARAMS = 0;
+};
+
+
+class DieCmdHandler : public rpc::RpcCommandHandler
+{
+public:
+ DieCmdHandler(boost::function<void( int, const rpc::CmdPtr&)> sender,rpc::RpcDriverPtr aDriver )
+ : rpc::RpcCommandHandler( "die", sender, NUM_PARAMS ), driver(aDriver) {}
+
+ virtual void handleCommand(int clientId,const std::list<boost::any>& params);
+
+private:
+ /// The amount of params that we should receive
+ static const short NUM_PARAMS = 0;
+ rpc::RpcDriverPtr driver;
+};
+
+DEF_RPC_FUNC(GetSettingsHandler,"getSettings",1);
+DEF_RPC_FUNC(SetSettingsHandler,"setSettings",2);
+DEF_RPC_FUNC(RemoveSourceHandler,"removeSource",2);
+}
+
+#endif
diff --git a/backend/filelog.cpp b/backend/filelog.cpp
new file mode 100644
index 0000000..7dfbedb
--- /dev/null
+++ b/backend/filelog.cpp
@@ -0,0 +1,65 @@
+//
+// C++ Implementation: filelog
+//
+// Description:
+//
+//
+// Author: Arsenij Vodjanov <arsenij@gmail.com>, (C) 2005
+//
+// Copyright: See COPYING file that comes with this distribution
+//
+//
+
+#include "filelog.h"
+
+#define FLOG_ENABLE
+
+// This should be retrieved from gui settings
+#define FLOG_FILE_NAME "/tmp/backend.log"
+
+using namespace std;
+
+FileLog::FileLog() : fos(FLOG_FILE_NAME) {
+ fos << "--- Starting Log ----" << endl;
+}
+
+void FileLog::info(const boost::format& fmt)
+{
+#ifdef FLOG_ENABLE
+ fos << fmt << endl;
+#endif
+}
+
+void FileLog::error(const boost::format& fmt)
+{
+#ifdef FLOG_ENABLE
+ fos << fmt << endl;
+#endif
+}
+
+void FileLog::warn(const boost::format& fmt)
+{
+#ifdef FLOG_ENABLE
+ fos << fmt << endl;
+#endif
+}
+
+
+void FileLog::debug(const boost::format& fmt,int level)
+{
+#ifdef FLOG_ENABLE
+ if(level <= debugLevel )
+ fos << fmt << endl;
+#endif
+}
+
+FileLog::~FileLog()
+{
+#ifdef FLOG_ENABLE
+ fos << "--- Ending Log ----" << endl;
+ fos.flush();
+ fos.close();
+#endif
+}
+
+
diff --git a/backend/filelog.h b/backend/filelog.h
new file mode 100644
index 0000000..0ebb50e
--- /dev/null
+++ b/backend/filelog.h
@@ -0,0 +1,36 @@
+//
+// C++ Interface: filelog
+//
+// Description:
+//
+//
+// Author: Arsenij Vodjanov <arsenij@gmail.com>, (C) 2005
+//
+// Copyright: See COPYING file that comes with this distribution
+//
+//
+
+#ifndef FILELOG_H_
+#define FILELOG_H_
+
+#include "log.h"
+#include <iostream>
+#include <fstream>
+
+//! Log implementation that logs to a file.
+class FileLog : public Log
+{
+public:
+ FileLog();
+
+ virtual void info(const boost::format& fmt);
+ virtual void error(const boost::format& fmt);
+ virtual void warn(const boost::format& fmt);
+ virtual void debug(const boost::format& fmt,int level=1);
+
+ virtual ~FileLog();
+private:
+ std::ofstream fos;
+};
+
+#endif
diff --git a/backend/log.h b/backend/log.h
new file mode 100644
index 0000000..b0acc5f
--- /dev/null
+++ b/backend/log.h
@@ -0,0 +1,35 @@
+//
+// C++ Interface: log
+//
+// Description:
+//
+//
+// Author: Rikard Björklind <olof@linux.nu>, (C) 2005
+//
+// Copyright: See COPYING file that comes with this distribution
+//
+//
+#ifndef LOG_H__
+#define LOG_H__
+
+
+#include <boost/format.hpp>
+//! Logging interface
+class Log
+{
+ public:
+ Log() {debugLevel=1000;}
+ virtual void info(const boost::format& fmt) = 0;
+ virtual void error(const boost::format& fmt) = 0;
+ virtual void warn(const boost::format& fmt) = 0;
+ virtual void debug(const boost::format& fmt,int level=1) = 0;
+ void setLevel(int l) {debugLevel=l;}
+ virtual ~Log() {}
+ protected:
+ int debugLevel;
+};
+
+extern Log* logger;
+
+
+#endif
diff --git a/backend/main.cpp b/backend/main.cpp
new file mode 100644
index 0000000..77914dd
--- /dev/null
+++ b/backend/main.cpp
@@ -0,0 +1,250 @@
+
+#include <iostream>
+#include <string>
+#include <signal.h>
+
+#include <boost/program_options.hpp>
+#include <boost/bind.hpp>
+
+#include <rpcdriver/rpcdriver.h>
+
+#include <dcpp/stdinc.h>
+#include <dcpp/DCPlusPlus.h>
+#include <dcpp/SettingsManager.h>
+#include <dcpp/ShareManager.h>
+#include <dcpp/QueueManager.h>
+
+#include "SessionManager.h"
+#include "TransferManager.h"
+#include "ClientNotifier.h"
+
+#include "commandhandlers.h"
+#include "ClientNotifier.h"
+#include "filelog.h"
+
+namespace prog_opts = boost::program_options;
+
+Log* logger = new FileLog();
+
+void callBack(void* x, const std::string& a);
+
+namespace {
+
+/**
+ * Start the rpc server and register the neccesary commands.
+ * @param port The port that we should listen to.
+ * @return Pointer to the created instance of the rpc server.
+ */
+ rpc::RpcDriverPtr initRpcServer( int port, std::string password )
+{
+ rpc::RpcServerDriver *fulpekare = new rpc::RpcServerDriver(port);
+ rpc::RpcDriverPtr driver( fulpekare );
+
+ dcqt_backend::ClientNotifier::instance()->setRpcDriver( driver );
+
+ rpc::RpcCommandHandlerPtr authenticateHandler( new dcqt_backend::AuthenticateCmdHandler( boost::bind( &rpc::RpcDriver::queueCommand,
+ driver.get(),
+ _1, _2) ,password,driver) );
+ driver->registerCommand(authenticateHandler);
+
+ rpc::RpcCommandHandlerPtr createSessionHandler( new dcqt_backend::CreateSessionCmdHandler( boost::bind( &rpc::RpcDriver::queueCommand,
+ driver.get(),
+ _1, _2) ) );
+ driver->registerCommand( createSessionHandler );
+
+ rpc::RpcCommandHandlerPtr closeSessionHandler( new dcqt_backend::CloseSessionCmdHandler( boost::bind( &rpc::RpcDriver::queueCommand,
+ driver.get(),
+ _1, _2) ) );
+ driver->registerCommand( closeSessionHandler );
+
+ rpc::RpcCommandHandlerPtr sendChatHandler( new dcqt_backend::SendChatCmdHandler( boost::bind( &rpc::RpcDriver::queueCommand,
+ driver.get(),
+ _1, _2) ) );
+ driver->registerCommand( sendChatHandler );
+
+ rpc::RpcCommandHandlerPtr searchHandler( new dcqt_backend::SearchCmdHandler( boost::bind( &rpc::RpcDriver::queueCommand,
+ driver.get(),
+ _1, _2) ) );
+ driver->registerCommand( searchHandler );
+
+ rpc::RpcCommandHandlerPtr getSharedDirs( new dcqt_backend::GetSharedDirectoriesCmdHandler( boost::bind( &rpc::RpcDriver::queueCommand,
+ driver.get(),
+ _1, _2) ) );
+ driver->registerCommand( getSharedDirs );
+
+ rpc::RpcCommandHandlerPtr addShareDir( new dcqt_backend::AddShareCmdHandler( boost::bind( &rpc::RpcDriver::queueCommand,
+ driver.get(),
+ _1, _2) ) );
+ driver->registerCommand( addShareDir );
+
+ rpc::RpcCommandHandlerPtr removeSharedDir( new dcqt_backend::RemoveShareCmdHandler( boost::bind( &rpc::RpcDriver::queueCommand,
+ driver.get(),
+ _1, _2) ) );
+ driver->registerCommand( removeSharedDir );
+
+ rpc::RpcCommandHandlerPtr downloadFile( new dcqt_backend::DownloadFileCmdHandler( boost::bind( &rpc::RpcDriver::queueCommand,
+ driver.get(),
+ _1, _2) ) );
+ driver->registerCommand( downloadFile );
+
+ rpc::RpcCommandHandlerPtr getFileList( new dcqt_backend::GetUserFileListCmdHandler( boost::bind( &rpc::RpcDriver::queueCommand,
+ driver.get(),
+ _1, _2) ) );
+ driver->registerCommand( getFileList );
+
+
+ rpc::RpcCommandHandlerPtr getHubList( new dcqt_backend::GetHubListCmdHandler( boost::bind( &rpc::RpcDriver::queueCommand,
+ driver.get(),
+ _1, _2) ) );
+ driver->registerCommand( getHubList );
+
+ rpc::RpcCommandHandlerPtr testbool( new dcqt_backend::TestBoolHandler( boost::bind( &rpc::RpcDriver::queueCommand,
+ driver.get(),
+ _1, _2) ) );
+ driver->registerCommand( testbool );
+
+ rpc::RpcCommandHandlerPtr getRunning( new dcqt_backend::RequestRunningSessionsCmdHandler( boost::bind( &rpc::RpcDriver::queueCommand,
+ driver.get(),
+ _1, _2) ) );
+ driver->registerCommand( getRunning );
+
+ rpc::RpcCommandHandlerPtr connectToUser( new dcqt_backend::ConnectToUserCmdHandler( boost::bind( &rpc::RpcDriver::queueCommand,
+ driver.get(),
+ _1, _2) ) );
+ driver->registerCommand( connectToUser );
+
+ rpc::RpcCommandHandlerPtr removeQueueItem( new dcqt_backend::RemoveQueueItemCmdHandler( boost::bind( &rpc::RpcDriver::queueCommand,
+ driver.get(),
+ _1, _2) ) );
+ driver->registerCommand( removeQueueItem );
+
+ rpc::RpcCommandHandlerPtr addFav( new dcqt_backend::AddFavoriteHubCmdHandler( boost::bind( &rpc::RpcDriver::queueCommand,
+ driver.get(),
+ _1, _2) ) );
+ driver->registerCommand( addFav );
+
+ rpc::RpcCommandHandlerPtr removeFav( new dcqt_backend::RemoveFavoriteHubCmdHandler( boost::bind( &rpc::RpcDriver::queueCommand,
+ driver.get(),
+ _1, _2) ) );
+ driver->registerCommand( removeFav );
+
+ rpc::RpcCommandHandlerPtr favList( new dcqt_backend::GetFavHubsHandler( boost::bind( &rpc::RpcDriver::queueCommand,
+ driver.get(),
+ _1, _2) ) );
+ driver->registerCommand( favList );
+
+ rpc::RpcCommandHandlerPtr getSettings( new dcqt_backend::GetSettingsHandler( boost::bind(&rpc::RpcDriver::queueCommand,
+ driver.get(),_1,_2) ));
+ driver->registerCommand( getSettings );
+
+ rpc::RpcCommandHandlerPtr setSettings( new dcqt_backend::SetSettingsHandler( boost::bind(&rpc::RpcDriver::queueCommand,
+ driver.get(),_1,_2) ));
+ driver->registerCommand( setSettings );
+
+ rpc::RpcCommandHandlerPtr die( new dcqt_backend::DieCmdHandler( boost::bind(&rpc::RpcDriver::queueCommand,
+ driver.get(),_1,_2), driver ));
+ driver->registerCommand( die );
+
+ rpc::RpcCommandHandlerPtr removeSource( new dcqt_backend::RemoveSourceHandler( boost::bind(&rpc::RpcDriver::queueCommand,
+ driver.get(),_1,_2) ));
+ driver->registerCommand( removeSource );
+
+ fulpekare->startListening();
+
+ return driver;
+}
+
+
+bool dcppInited = false;
+
+/**
+ * Initialze the dcpp thingies.
+ */
+void initDcpp()
+{
+ logger->info( boost::format("Init Dcpp") );
+ startup(callBack, NULL);
+ dcppInited = true;
+ SettingsManager::getInstance()->load();
+ TimerManager::getInstance()->start();
+ // Set the port that the SearchManager should use
+ SearchManager::getInstance()->setPort( SettingsManager::getInstance()->get( SettingsManager::UDP_PORT, true ) );
+}
+
+
+void initDcqtBackend()
+{
+ dcqt_backend::SessionManager::instance();
+ dcqt_backend::TransferManager::newInstance();
+}
+
+}
+
+// For dcpp to send messages
+void callBack(void* x, const string& a) {
+ cout << "dcpp: " << a << endl;
+ logger->debug(boost::format("dcpp: %1%") % a,1);
+}
+
+rpc::RpcDriverPtr driver;
+
+
+void quithandler(int sig) {
+ driver->stopSender();
+}
+
+
+int main (int argc, char *argv[]) {
+
+ prog_opts::options_description desc("Allowed options");
+ desc.add_options()
+ ("help",
+ "Display help message")
+ ("addr",
+ prog_opts::value<std::string>()->default_value("localhost"),
+ "Address to connect to")
+ ("port",
+ prog_opts::value<int>()->default_value(6161),
+ "Specify port")
+ ("password",
+ prog_opts::value<std::string>()->default_value(""),
+ "Specify a password that the backend will require from connecting clients");
+
+ prog_opts::variables_map vm;
+ prog_opts::store(prog_opts::parse_command_line(argc, argv, desc), vm);
+
+ if(vm.count("help")) {
+ std::cout << desc << std::endl;
+ return 1;
+ }
+
+ //----------------------------------------------------------
+ // Install signal handler
+ struct sigaction act;
+ act.sa_handler = quithandler;
+ act.sa_flags = 0;
+ sigaction(SIGINT,&act,0);
+
+ try {
+ driver = initRpcServer( vm["port"].as<int>(),vm["password"].as<std::string>() );
+
+ initDcpp();
+ //wtf?
+ signal(SIGPIPE, SIG_IGN);
+ initDcqtBackend();
+
+ driver->waitForCompletion();
+ }
+ catch(...) {
+ }
+
+ if(dcppInited) {
+ dcqt_backend::TransferManager::deleteInstance();
+ shutdown();
+ }
+
+ logger->info(boost::format("Exiting normally"));
+
+
+ return 0;
+}
diff --git a/dcpp/ADLSearch.cpp b/dcpp/ADLSearch.cpp
new file mode 100644
index 0000000..96c4570
--- /dev/null
+++ b/dcpp/ADLSearch.cpp
@@ -0,0 +1,327 @@
+/*
+* Copyright (C) 2001-2005 Jacek Sieka, arnetheduck on gmail point 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 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.
+*/
+
+/*
+* Automatic Directory Listing Search
+* Henrik Engstr�m, henrikengstrom at home se
+*/
+
+#include "stdinc.h"
+#include "DCPlusPlus.h"
+
+#include "ADLSearch.h"
+#include "QueueManager.h"
+
+#include "File.h"
+#include "SimpleXML.h"
+
+#define ADLS_STORE_FILENAME "ADLSearch.xml"
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// Load old searches from disk
+//
+///////////////////////////////////////////////////////////////////////////////
+void ADLSearchManager::Load()
+{
+ // Clear current
+ collection.clear();
+
+ // Load file as a string
+ try {
+ SimpleXML xml;
+ xml.fromXML(File(Util::getAppPath() + ADLS_STORE_FILENAME, File::READ, File::OPEN).read());
+
+ if(xml.findChild("ADLSearch")) {
+ xml.stepIn();
+
+ // Predicted several groups of searches to be differentiated
+ // in multiple categories. Not implemented yet.
+ if(xml.findChild("SearchGroup")) {
+ xml.stepIn();
+
+ // Loop until no more searches found
+ while(xml.findChild("Search")) {
+ xml.stepIn();
+
+ // Found another search, load it
+ ADLSearch search;
+
+ if(xml.findChild("SearchString")) {
+ search.searchString = xml.getChildData();
+ }
+ if(xml.findChild("SourceType")) {
+ search.sourceType = search.StringToSourceType(xml.getChildData());
+ }
+ if(xml.findChild("DestDirectory")) {
+ search.destDir = xml.getChildData();
+ }
+ if(xml.findChild("IsActive")) {
+ search.isActive = (Util::toInt(xml.getChildData()) != 0);
+ }
+ if(xml.findChild("MaxSize")) {
+ search.maxFileSize = Util::toInt64(xml.getChildData());
+ }
+ if(xml.findChild("MinSize")) {
+ search.minFileSize = Util::toInt64(xml.getChildData());
+ }
+ if(xml.findChild("SizeType")) {
+ search.typeFileSize = search.StringToSizeType(xml.getChildData());
+ }
+ if(xml.findChild("IsAutoQueue")) {
+ search.isAutoQueue = (Util::toInt(xml.getChildData()) != 0);
+ }
+
+ // Add search to collection
+ if(search.searchString.size() > 0) {
+ collection.push_back(search);
+ }
+
+ // Go to next search
+ xml.stepOut();
+ }
+ }
+ }
+ } catch(const SimpleXMLException&) {
+ return;
+ } catch(const FileException&) {
+ return;
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// Save current searches to disk
+//
+///////////////////////////////////////////////////////////////////////////////
+void ADLSearchManager::Save()
+{
+ // Prepare xml string for saving
+ try {
+ SimpleXML xml;
+
+ xml.addTag("ADLSearch");
+ xml.stepIn();
+
+ // Predicted several groups of searches to be differentiated
+ // in multiple categories. Not implemented yet.
+ xml.addTag("SearchGroup");
+ xml.stepIn();
+
+ // Save all searches
+ for(SearchCollection::iterator i = collection.begin(); i != collection.end(); ++i) {
+ ADLSearch& search = *i;
+ if(search.searchString.size() == 0) {
+ continue;
+ }
+ string type = "type";
+ xml.addTag("Search");
+ xml.stepIn();
+
+ xml.addTag("SearchString", search.searchString);
+ xml.addChildAttrib(type, string("string"));
+
+ xml.addTag("SourceType", search.SourceTypeToString(search.sourceType));
+ xml.addChildAttrib(type, string("string"));
+
+ xml.addTag("DestDirectory", search.destDir);
+ xml.addChildAttrib(type, string("string"));
+
+ xml.addTag("IsActive", search.isActive);
+ xml.addChildAttrib(type, string("int"));
+
+ xml.addTag("MaxSize", search.maxFileSize);
+ xml.addChildAttrib(type, string("int64"));
+
+ xml.addTag("MinSize", search.minFileSize);
+ xml.addChildAttrib(type, string("int64"));
+
+ xml.addTag("SizeType", search.SizeTypeToString(search.typeFileSize));
+ xml.addChildAttrib(type, string("string"));
+
+ xml.addTag("IsAutoQueue", search.isAutoQueue);
+ xml.addChildAttrib(type, string("int"));
+
+ xml.stepOut();
+ }
+
+ xml.stepOut();
+
+ xml.stepOut();
+
+ // Save string to file
+ try {
+ File fout(Util::getAppPath() + ADLS_STORE_FILENAME, File::WRITE, File::CREATE | File::TRUNCATE);
+ fout.write(SimpleXML::utf8Header);
+ fout.write(xml.toXML());
+ fout.close();
+ } catch(const FileException&) {
+ return;
+ }
+ } catch(const SimpleXMLException&) {
+ return;
+ }
+}
+
+void ADLSearchManager::MatchesFile(DestDirList& destDirVector, DirectoryListing::File *currentFile, string& fullPath) {
+ // Add to any substructure being stored
+ for(DestDirList::iterator id = destDirVector.begin(); id != destDirVector.end(); ++id) {
+ if(id->subdir != NULL) {
+ DirectoryListing::File *copyFile = new DirectoryListing::File(*currentFile, true);
+ dcassert(id->subdir->getAdls());
+
+ id->subdir->files.push_back(copyFile);
+ }
+ id->fileAdded = false; // Prepare for next stage
+ }
+
+ // Prepare to match searches
+ if(currentFile->getName().size() < 1) {
+ return;
+ }
+
+ string filePath = fullPath + "\\" + currentFile->getName();
+ // Match searches
+ for(SearchCollection::iterator is = collection.begin(); is != collection.end(); ++is) {
+ if(destDirVector[is->ddIndex].fileAdded) {
+ continue;
+ }
+ if(is->MatchesFile(currentFile->getName(), filePath, currentFile->getSize())) {
+ DirectoryListing::File *copyFile = new DirectoryListing::File(*currentFile, true);
+ destDirVector[is->ddIndex].dir->files.push_back(copyFile);
+ destDirVector[is->ddIndex].fileAdded = true;
+
+ if(is->isAutoQueue){
+ QueueManager::getInstance()->add(currentFile->getName(), currentFile->getSize(), getUser(), SETTING(DOWNLOAD_DIRECTORY) + currentFile->getName(), currentFile->getTTH());
+ }
+
+ if(breakOnFirst) {
+ // Found a match, search no more
+ break;
+ }
+ }
+ }
+}
+
+void ADLSearchManager::MatchesDirectory(DestDirList& destDirVector, DirectoryListing::Directory* currentDir, string& fullPath) {
+ // Add to any substructure being stored
+ for(DestDirList::iterator id = destDirVector.begin(); id != destDirVector.end(); ++id) {
+ if(id->subdir != NULL) {
+ DirectoryListing::Directory* newDir =
+ new DirectoryListing::AdlDirectory(fullPath, id->subdir, currentDir->getName());
+ id->subdir->directories.push_back(newDir);
+ id->subdir = newDir;
+ }
+ }
+
+ // Prepare to match searches
+ if(currentDir->getName().size() < 1) {
+ return;
+ }
+
+ // Match searches
+ for(SearchCollection::iterator is = collection.begin(); is != collection.end(); ++is) {
+ if(destDirVector[is->ddIndex].subdir != NULL) {
+ continue;
+ }
+ if(is->MatchesDirectory(currentDir->getName())) {
+ destDirVector[is->ddIndex].subdir =
+ new DirectoryListing::AdlDirectory(fullPath, destDirVector[is->ddIndex].dir, currentDir->getName());
+ destDirVector[is->ddIndex].dir->directories.push_back(destDirVector[is->ddIndex].subdir);
+ if(breakOnFirst) {
+ // Found a match, search no more
+ break;
+ }
+ }
+ }
+}
+
+void ADLSearchManager::PrepareDestinationDirectories(DestDirList& destDirVector, DirectoryListing::Directory* root, StringMap& params) {
+ // Load default destination directory (index = 0)
+ destDirVector.clear();
+ vector<DestDir>::iterator id = destDirVector.insert(destDirVector.end(), DestDir());
+ id->name = "ADLSearch";
+ id->dir = new DirectoryListing::Directory(root, "<<<" + id->name + ">>>", true, true);
+
+ // Scan all loaded searches
+ for(SearchCollection::iterator is = collection.begin(); is != collection.end(); ++is) {
+ // Check empty destination directory
+ if(is->destDir.size() == 0) {
+ // Set to default
+ is->ddIndex = 0;
+ continue;
+ }
+
+ // Check if exists
+ bool isNew = true;
+ long ddIndex = 0;
+ for(id = destDirVector.begin(); id != destDirVector.end(); ++id, ++ddIndex) {
+ if(Util::stricmp(is->destDir.c_str(), id->name.c_str()) == 0) {
+ // Already exists, reuse index
+ is->ddIndex = ddIndex;
+ isNew = false;
+ break;
+ }
+ }
+
+ if(isNew) {
+ // Add new destination directory
+ id = destDirVector.insert(destDirVector.end(), DestDir());
+ id->name = is->destDir;
+ id->dir = new DirectoryListing::Directory(root, "<<<" + id->name + ">>>", true, true);
+ is->ddIndex = ddIndex;
+ }
+ }
+ // Prepare all searches
+ for(SearchCollection::iterator ip = collection.begin(); ip != collection.end(); ++ip) {
+ ip->Prepare(params);
+ }
+}
+
+void ADLSearchManager::matchListing(DirectoryListing* aDirList) throw() {
+ StringMap params;
+ params["nick"] = aDirList->getUser()->getNick();
+ setUser(aDirList->getUser());
+
+ DestDirList destDirs;
+ PrepareDestinationDirectories(destDirs, aDirList->getRoot(), params);
+ setBreakOnFirst(BOOLSETTING(ADLS_BREAK_ON_FIRST));
+
+ string path(aDirList->getRoot()->getName());
+ matchRecurse(destDirs, aDirList->getRoot(), path);
+
+ FinalizeDestinationDirectories(destDirs, aDirList->getRoot());
+}
+
+void ADLSearchManager::matchRecurse(DestDirList &aDestList, DirectoryListing::Directory* aDir, string &aPath) {
+ for(DirectoryListing::Directory::Iter dirIt = aDir->directories.begin(); dirIt != aDir->directories.end(); ++dirIt) {
+ string tmpPath = aPath + "\\" + (*dirIt)->getName();
+ MatchesDirectory(aDestList, *dirIt, tmpPath);
+ matchRecurse(aDestList, *dirIt, tmpPath);
+ }
+ for(DirectoryListing::File::Iter fileIt = aDir->files.begin(); fileIt != aDir->files.end(); ++fileIt) {
+ MatchesFile(aDestList, *fileIt, aPath);
+ }
+ StepUpDirectory(aDestList);
+}
+
+/**
+* @file
+* $Id: ADLSearch.cpp,v 1.2 2005/08/21 14:03:43 olof Exp $
+*/
+
diff --git a/dcpp/ADLSearch.h b/dcpp/ADLSearch.h
new file mode 100644
index 0000000..21b6f29
--- /dev/null
+++ b/dcpp/ADLSearch.h
@@ -0,0 +1,313 @@
+/*
+ * Copyright (C) 2001-2005 Jacek Sieka, arnetheduck on gmail point 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 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.
+ */
+
+/*
+ * Automatic Directory Listing Search
+ * Henrik Engstr�m, henrikengstrom at home se
+ */
+
+#if !defined(__ADLSEARCH_H__)
+#define __ADLSEARCH_H__
+
+#if _MSC_VER > 1000
+#pragma once
+#endif
+
+#include "Util.h"
+
+#include "SettingsManager.h"
+#include "ResourceManager.h"
+
+#include "StringSearch.h"
+#include "StringTokenizer.h"
+#include "Singleton.h"
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// Class that represent an ADL search
+//
+///////////////////////////////////////////////////////////////////////////////
+class ADLSearch
+{
+public:
+
+ // Constructor
+ ADLSearch() : searchString("<Enter string>"), isActive(true), isAutoQueue(false), sourceType(OnlyFile),
+ minFileSize(-1), maxFileSize(-1), typeFileSize(SizeBytes), destDir("ADLSearch"), ddIndex(0) {}
+
+ // Prepare search
+ void Prepare(StringMap& params) {
+ // Prepare quick search of substrings
+ stringSearchList.clear();
+
+ // Replace parameters such as %[nick]
+ string stringParams = Util::formatParams(searchString, params);
+
+ // Split into substrings
+ StringTokenizer<string> st(stringParams, ' ');
+ for(StringList::iterator i = st.getTokens().begin(); i != st.getTokens().end(); ++i) {
+ if(i->size() > 0) {
+ // Add substring search
+ stringSearchList.push_back(StringSearch(*i));
+ }
+ }
+ }
+
+ // The search string
+ string searchString;
+
+ // Active search
+ bool isActive;
+
+ // Auto Queue Results
+ bool isAutoQueue;
+
+ // Search source type
+ enum SourceType {
+ TypeFirst = 0,
+ OnlyFile = TypeFirst,
+ OnlyDirectory,
+ FullPath,
+ TypeLast
+ } sourceType;
+
+ SourceType StringToSourceType(const string& s) {
+ if(Util::stricmp(s.c_str(), "Filename") == 0) {
+ return OnlyFile;
+ } else if(Util::stricmp(s.c_str(), "Directory") == 0) {
+ return OnlyDirectory;
+ } else if(Util::stricmp(s.c_str(), "Full Path") == 0) {
+ return FullPath;
+ } else {
+ return OnlyFile;
+ }
+ }
+
+ string SourceTypeToString(SourceType t) {
+ switch(t) {
+ default:
+ case OnlyFile: return "Filename";
+ case OnlyDirectory: return "Directory";
+ case FullPath: return "Full Path";
+ }
+ }
+
+ tstring SourceTypeToDisplayString(SourceType t) {
+ switch(t) {
+ default:
+ case OnlyFile: return TSTRING(FILENAME);
+ case OnlyDirectory: return TSTRING(DIRECTORY);
+ case FullPath: return TSTRING(ADLS_FULL_PATH);
+ }
+ }
+
+ // Maximum & minimum file sizes (in bytes).
+ // Negative values means do not check.
+ int64_t minFileSize;
+ int64_t maxFileSize;
+ enum SizeType {
+ SizeBytes = TypeFirst,
+ SizeKibiBytes,
+ SizeMebiBytes,
+ SizeGibiBytes
+ };
+ SizeType typeFileSize;
+ SizeType StringToSizeType(const string& s) {
+ if(Util::stricmp(s.c_str(), "B") == 0) {
+ return SizeBytes;
+ } else if(Util::stricmp(s.c_str(), "KiB") == 0) {
+ return SizeKibiBytes;
+ } else if(Util::stricmp(s.c_str(), "MiB") == 0) {
+ return SizeMebiBytes;
+ } else if(Util::stricmp(s.c_str(), "GiB") == 0) {
+ return SizeGibiBytes;
+ } else {
+ return SizeBytes;
+ }
+ }
+ string SizeTypeToString(SizeType t) {
+ switch(t) {
+ default:
+ case SizeBytes: return "B";
+ case SizeKibiBytes: return "KiB";
+ case SizeMebiBytes: return "MiB";
+ case SizeGibiBytes: return "GiB";
+ }
+ }
+ tstring SizeTypeToDisplayString(SizeType t) {
+ switch(t) {
+ default:
+ case SizeBytes: return CTSTRING(B);
+ case SizeKibiBytes: return CTSTRING(KiB);
+ case SizeMebiBytes: return CTSTRING(MiB);
+ case SizeGibiBytes: return CTSTRING(GiB);
+ }
+ }
+ int64_t GetSizeBase() {
+ switch(typeFileSize) {
+ default:
+ case SizeBytes: return (int64_t)1;
+ case SizeKibiBytes: return (int64_t)1024;
+ case SizeMebiBytes: return (int64_t)1024 * (int64_t)1024;
+ case SizeGibiBytes: return (int64_t)1024 * (int64_t)1024 * (int64_t)1024;
+ }
+ }
+
+ // Name of the destination directory (empty = 'ADLSearch') and its index
+ string destDir;
+ unsigned long ddIndex;
+
+ // Search for file match
+ bool MatchesFile(const string& f, const string& fp, int64_t size) {
+ // Check status
+ if(!isActive) {
+ return false;
+ }
+
+ // Check size for files
+ if(size >= 0 && (sourceType == OnlyFile || sourceType == FullPath)) {
+ if(minFileSize >= 0 && size < minFileSize * GetSizeBase()) {
+ // Too small
+ return false;
+ }
+ if(maxFileSize >= 0 && size > maxFileSize * GetSizeBase()) {
+ // Too large
+ return false;
+ }
+ }
+
+ // Do search
+ switch(sourceType) {
+ default:
+ case OnlyDirectory: return false;
+ case OnlyFile: return SearchAll(f);
+ case FullPath: return SearchAll(fp);
+ }
+ }
+
+ // Search for directory match
+ bool MatchesDirectory(const string& d) {
+ // Check status
+ if(!isActive) {
+ return false;
+ }
+ if(sourceType != OnlyDirectory) {
+ return false;
+ }
+
+ // Do search
+ return SearchAll(d);
+ }
+
+private:
+
+ // Substring searches
+ StringSearch::List stringSearchList;
+ bool SearchAll(const string& s) {
+ // Match all substrings
+ for(StringSearch::Iter i = stringSearchList.begin(); i != stringSearchList.end(); ++i) {
+ if(!i->match(s)) {
+ return false;
+ }
+ }
+ return (stringSearchList.size() != 0);
+ }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// Class that holds all active searches
+//
+///////////////////////////////////////////////////////////////////////////////
+#include "DirectoryListing.h"
+class ADLSearchManager : public Singleton<ADLSearchManager>
+{
+public:
+ // Destination directory indexing
+ struct DestDir {
+ string name;
+ DirectoryListing::Directory* dir;
+ DirectoryListing::Directory* subdir;
+ bool fileAdded;
+ DestDir() : name(""), dir(NULL), subdir(NULL) {}
+ };
+ typedef vector<DestDir> DestDirList;
+
+ // Constructor/destructor
+ ADLSearchManager() { Load(); }
+ virtual ~ADLSearchManager() { Save(); }
+
+ // Search collection
+ typedef vector<ADLSearch> SearchCollection;
+ SearchCollection collection;
+
+ // Load/save search collection to XML file
+ void Load();
+ void Save();
+
+ // Settings
+ GETSET(bool, breakOnFirst, BreakOnFirst)
+ GETSET(User::Ptr, user, User)
+
+ // @remarks Used to add ADLSearch directories to an existing DirectoryListing
+ void matchListing(DirectoryListing* /*aDirList*/) throw();
+
+private:
+ // @internal
+ void matchRecurse(DestDirList& /*aDestList*/, DirectoryListing::Directory* /*aDir*/, string& /*aPath*/);
+ // Search for file match
+ void MatchesFile(DestDirList& destDirVector, DirectoryListing::File *currentFile, string& fullPath);
+ // Search for directory match
+ void MatchesDirectory(DestDirList& destDirVector, DirectoryListing::Directory* currentDir, string& fullPath);
+ // Step up directory
+ void StepUpDirectory(DestDirList& destDirVector) {
+ for(DestDirList::iterator id = destDirVector.begin(); id != destDirVector.end(); ++id) {
+ if(id->subdir != NULL) {
+ id->subdir = id->subdir->getParent();
+ if(id->subdir == id->dir) {
+ id->subdir = NULL;
+ }
+ }
+ }
+ }
+
+ // Prepare destination directory indexing
+ void PrepareDestinationDirectories(DestDirList& destDirVector, DirectoryListing::Directory* root, StringMap& params);
+ // Finalize destination directories
+ void FinalizeDestinationDirectories(DestDirList& destDirVector, DirectoryListing::Directory* root) {
+ string szDiscard = "<<<" + STRING(ADLS_DISCARD) + ">>>";
+
+ // Add non-empty destination directories to the top level
+ for(vector<DestDir>::iterator id = destDirVector.begin(); id != destDirVector.end(); ++id) {
+ if(id->dir->files.size() == 0 && id->dir->directories.size() == 0) {
+ delete (id->dir);
+ } else if(Util::stricmp(id->dir->getName(), szDiscard) == 0) {
+ delete (id->dir);
+ } else {
+ root->directories.push_back(id->dir);
+ }
+ }
+ }
+};
+
+#endif
+
+/**
+ * @file
+ * $Id: ADLSearch.h,v 1.2 2005/08/21 14:03:43 olof Exp $
+ */
diff --git a/dcpp/AdcCommand.cpp b/dcpp/AdcCommand.cpp
new file mode 100644
index 0000000..7138df1
--- /dev/null
+++ b/dcpp/AdcCommand.cpp
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2001-2005 Jacek Sieka, arnetheduck on gmail point 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 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.
+ */
+
+#include "stdinc.h"
+#include "DCPlusPlus.h"
+
+#include "AdcCommand.h"
+
+void AdcCommand::parse(const string& aLine, bool nmdc /* = false */) throw(ParseException) {
+ string::size_type i = 5;
+
+ if(nmdc) {
+ // "$ADCxxx ..."
+ if(aLine.length() < 7)
+ throw ParseException("Too short");
+ type = TYPE_CLIENT;
+ memcpy(cmd, &aLine[4], 3);
+ i += 3;
+ } else {
+ // "yxxx cidcidcidcid..."
+ if(aLine.length() < 5 + (8 * 8 + 7) / 5)
+ throw ParseException("Too short");
+ type = aLine[0];
+ memcpy(cmd, &aLine[1], 3);
+ }
+
+ string::size_type len = aLine.length();
+ const char* buf = aLine.c_str();
+ string cur;
+ cur.reserve(128);
+
+ bool toSet = false;
+ bool fromSet = nmdc; // $ADCxxx never have a from CID...
+
+ while(i < len) {
+ switch(buf[i]) {
+ case '\\':
+ ++i;
+ if(i == len)
+ throw ParseException("Escape at eol");
+ if(buf[i] == 's')
+ cur += ' ';
+ else if(buf[i] == 'n')
+ cur += '\n';
+ else if(buf[i] == '\\')
+ cur += '\\';
+ else if(buf[i] == ' ' && nmdc) // $ADCGET escaping, leftover from old specs
+ cur += ' ';
+ else
+ throw ParseException("Unknown escape");
+ break;
+ case ' ':
+ // New parameter...
+ {
+ if(!fromSet) {
+ from = CID(cur);
+ fromSet = true;
+ } else if(type == TYPE_DIRECT && !toSet) {
+ to = CID(cur);
+ toSet = true;
+ } else {
+ parameters.push_back(cur);
+ }
+ cur.clear();
+ }
+ break;
+ default:
+ cur += buf[i];
+ }
+ ++i;
+ }
+ if(!cur.empty()) {
+ if(!fromSet) {
+ to = CID(cur);
+ fromSet = true;
+ } else if(type == TYPE_DIRECT && !toSet) {
+ from = CID(cur);
+ toSet = true;
+ } else {
+ parameters.push_back(cur);
+ }
+ }
+}
+
+string AdcCommand::toString(bool nmdc /* = false */, bool old /* = false */) const {
+ string tmp;
+ if(nmdc) {
+ tmp += "$ADC";
+ } else {
+ tmp += getType();
+ }
+
+ tmp += cmdChar;
+
+ if(!nmdc) {
+ tmp += ' ';
+ tmp += from.toBase32();
+ }
+
+ if(getType() == TYPE_DIRECT) {
+ tmp += ' ';
+ tmp += to.toBase32();
+ }
+
+ for(StringIterC i = getParameters().begin(); i != getParameters().end(); ++i) {
+ tmp += ' ';
+ tmp += escape(*i, old);
+ }
+ if(nmdc) {
+ tmp += '|';
+ } else {
+ tmp += '\n';
+ }
+ return tmp;
+}
+
+bool AdcCommand::getParam(const char* name, size_t start, string& ret) const {
+ for(string::size_type i = start; i < getParameters().size(); ++i) {
+ if(toCode(name) == toCode(getParameters()[i].c_str())) {
+ ret = getParameters()[i].substr(2);
+ return true;
+ }
+ }
+ return false;
+}
+
+bool AdcCommand::hasFlag(const char* name, size_t start) const {
+ for(string::size_type i = start; i < getParameters().size(); ++i) {
+ if(toCode(name) == toCode(getParameters()[i].c_str()) &&
+ getParameters()[i][2] == '1' &&
+ getParameters()[i].size() == 3)
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+/**
+ * @file
+ * $Id: AdcCommand.cpp,v 1.2 2005/08/21 14:03:43 olof Exp $
+ */
diff --git a/dcpp/AdcCommand.h b/dcpp/AdcCommand.h
new file mode 100644
index 0000000..188d849
--- /dev/null
+++ b/dcpp/AdcCommand.h
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2001-2005 Jacek Sieka, arnetheduck on gmail point 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 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.
+ */
+
+#ifndef ADC_COMMAND_H
+#define ADC_COMMAND_H
+
+#include "CID.h"
+#include "SettingsManager.h"
+#include "Exception.h"
+
+STANDARD_EXCEPTION(ParseException);
+
+class AdcCommand {
+public:
+ template<u_int32_t T>
+ struct Type {
+ enum { CMD = T };
+ };
+
+ enum Error {
+ ERROR_GENERIC = 0,
+ ERROR_HUB_GENERIC = 10,
+ ERROR_HUB_FULL = 11,
+ ERROR_HUB_DISABLED = 12,
+ ERROR_LOGIN_GENERIC = 20,
+ ERROR_NICK_INVALID = 21,
+ ERROR_NICK_TAKEN = 22,
+ ERROR_BAD_PASSWORD = 23,
+ ERROR_CID_TAKEN = 24,
+ ERROR_COMMAND_ACCESS = 25,
+ ERROR_REGGED_ONLY = 26,
+ ERROR_BANNED_GENERIC = 30,
+ ERROR_PERM_BANNED = 31,
+ ERROR_TEMP_BANNED = 32,
+ ERROR_PROTOCOL_GENERIC = 40,
+ ERROR_PROTOCOL_UNSUPPORTED = 41,
+ ERROR_INF_MISSING = 42,
+ ERROR_BAD_STATE = 43,
+ ERROR_TRANSFER_GENERIC = 50,
+ ERROR_FILE_NOT_AVAILABLE = 51,
+ ERROR_FILE_PART_NOT_AVAILABLE = 52,
+ ERROR_SLOTS_FULL = 53
+ };
+
+ enum Severity {
+ SEV_SUCCESS = 0,
+ SEV_RECOVERABLE = 1,
+ SEV_FATAL = 2
+ };
+
+ static const char TYPE_ACTIVE = 'A';
+ static const char TYPE_BROADCAST = 'B';
+ static const char TYPE_CLIENT = 'C';
+ static const char TYPE_DIRECT = 'D';
+ static const char TYPE_INFO = 'I';
+ static const char TYPE_HUB = 'H';
+ static const char TYPE_PASSIVE = 'P';
+ static const char TYPE_UDP = 'U';
+#ifdef TARGET_I386
+#define CMD(n, a, b, c) static const u_int32_t CMD_##n = (((u_int32_t)a) | (((u_int32_t)b)<<8) | (((u_int32_t)c)<<16)); typedef Type<CMD_##n> n
+#else
+#define CMD(n, a, b, c) static const u_int32_t CMD_##n = ((((u_int32_t)a)<<24) | (((u_int32_t)b)<<16) | (((u_int32_t)c)<<8)); typedef Type<CMD_##n> n
+#endif
+ CMD(SUP, 'S','U','P');
+ CMD(STA, 'S','T','A');
+ CMD(INF, 'I','N','F');
+ CMD(MSG, 'M','S','G');
+ CMD(SCH, 'S','C','H');
+ CMD(RES, 'R','E','S');
+ CMD(CTM, 'C','T','M');
+ CMD(RCM, 'R','C','M');
+ CMD(GPA, 'G','P','A');
+ CMD(PAS, 'P','A','S');
+ CMD(QUI, 'Q','U','I');
+ CMD(DSC, 'D','S','C');
+ CMD(GET, 'G','E','T');
+ CMD(GFI, 'G','F','I');
+ CMD(SND, 'S','N','D');
+ CMD(NTD, 'N','T','D');
+#undef CMD
+
+ explicit AdcCommand(u_int32_t aCmd, char aType = TYPE_CLIENT) : cmdInt(aCmd), from(SETTING(CLIENT_ID)), type(aType) { }
+ explicit AdcCommand(u_int32_t aCmd, const CID& aTarget) : cmdInt(aCmd), from(SETTING(CLIENT_ID)), to(aTarget), type(TYPE_DIRECT) { }
+
+ explicit AdcCommand(const string& aLine, bool nmdc = false) throw(ParseException) : cmdInt(0), type(TYPE_CLIENT) {
+ parse(aLine, nmdc);
+ }
+
+ void parse(const string& aLine, bool nmdc = false) throw(ParseException);
+
+ u_int32_t getCommand() const { return cmdInt; }
+ char getType() const { return type; }
+ void setType(char t) { type = t; }
+
+ StringList& getParameters() { return parameters; }
+ const StringList& getParameters() const { return parameters; }
+
+ string toString(bool nmdc = false, bool old = false) const;
+
+ AdcCommand& addParam(const string& name, const string& value) {
+ parameters.push_back(name);
+ parameters.back() += value;
+ return *this;
+ }
+ AdcCommand& addParam(const string& str) {
+ parameters.push_back(str);
+ return *this;
+ }
+ const string& getParam(size_t n) const {
+ return getParameters().size() > n ? getParameters()[n] : Util::emptyString;
+ }
+ /** Return a named parameter where the name is a two-letter code */
+ bool getParam(const char* name, size_t start, string& ret) const;
+ bool hasFlag(const char* name, size_t start) const;
+ static u_int16_t toCode(const char* x) { return *((u_int16_t*)x); }
+
+ bool operator==(u_int32_t aCmd) { return cmdInt == aCmd; }
+
+ static string escape(const string& str, bool old) {
+ string tmp = str;
+ string::size_type i = 0;
+ while( (i = tmp.find_first_of(" \n\\", i)) != string::npos) {
+ if(old) {
+ tmp.insert(i, "\\");
+ } else {
+ switch(tmp[i]) {
+ case ' ': tmp.replace(i, 1, "\\s"); break;
+ case '\n': tmp.replace(i, 1, "\\n"); break;
+ case '\\': tmp.replace(i, 1, "\\\\"); break;
+ }
+ }
+ i+=2;
+ }
+ return tmp;
+ }
+ const CID& getTo() const { return to; }
+ void setTo(const CID& cid) { to = cid; }
+ const CID& getFrom() const { return from; }
+
+private:
+ StringList parameters;
+ union {
+ char cmdChar[4];
+ u_int8_t cmd[4];
+ u_int32_t cmdInt;
+ };
+ CID from;
+ CID to;
+ char type;
+
+};
+
+template<class T>
+class CommandHandler {
+public:
+ void dispatch(const string& aLine, bool nmdc = false) {
+ try {
+ AdcCommand c(aLine, nmdc);
+
+#define CMD(n) case AdcCommand::CMD_##n: ((T*)this)->handle(AdcCommand::n(), c); break;
+ switch(c.getCommand()) {
+ CMD(SUP);
+ CMD(STA);
+ CMD(INF);
+ CMD(MSG);
+ CMD(SCH);
+ CMD(RES);
+ CMD(CTM);
+ CMD(RCM);
+ CMD(GPA);
+ CMD(PAS);
+ CMD(QUI);
+ CMD(DSC);
+ CMD(GET);
+ CMD(GFI);
+ CMD(SND);
+ CMD(NTD);
+ default:
+ dcdebug("Unknown ADC command: %.50s\n", aLine.c_str());
+ break;
+#undef CMD
+
+ }
+ } catch(const ParseException&) {
+ dcdebug("Invalid ADC command: %.50s\n", aLine.c_str());
+ return;
+ }
+ }
+};
+
+#endif // ADC_COMMAND_H
+/**
+* @file
+* $Id: AdcCommand.h,v 1.3 2005/11/16 21:35:59 mickeg Exp $
+*/
diff --git a/dcpp/AdcHub.cpp b/dcpp/AdcHub.cpp
new file mode 100644
index 0000000..3d2152e
--- /dev/null
+++ b/dcpp/AdcHub.cpp
@@ -0,0 +1,432 @@
+/*
+ * Copyright (C) 2001-2005 Jacek Sieka, arnetheduck on gmail point 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 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.
+ */
+
+#include "stdinc.h"
+#include "DCPlusPlus.h"
+
+#include "AdcHub.h"
+#include "ClientManager.h"
+#include "ShareManager.h"
+#include "StringTokenizer.h"
+#include "AdcCommand.h"
+#include "ConnectionManager.h"
+#include "version.h"
+#include "Util.h"
+const string AdcHub::CLIENT_PROTOCOL("ADC/0.9");
+
+AdcHub::AdcHub(const string& aHubURL) : Client(aHubURL, '\n'), state(STATE_PROTOCOL) {
+}
+
+AdcHub::~AdcHub() throw() {
+ Lock l(cs);
+ clearUsers();
+}
+
+void AdcHub::handle(AdcCommand::INF, AdcCommand& c) throw() {
+ if(c.getParameters().empty())
+ return;
+
+ User::Ptr u = ClientManager::getInstance()->getUser(c.getFrom(), this, true);
+ cidMap[u->getCID()] = u;
+
+ int op = 0;
+ int reg = 0;
+ int norm = 0;
+ string ve;
+ int sl = 0;
+
+ for(StringIterC i = c.getParameters().begin(); i != c.getParameters().end(); ++i) {
+ if(i->length() < 2)
+ continue;
+
+ if(i->compare(0, 2, "NI") == 0) {
+ Lock l(cs);
+ if(!u->getNick().empty()) {
+ nickMap.erase(u->getNick());
+ }
+ u->setNick(i->substr(2));
+ nickMap.insert(make_pair(u->getNick(), u));
+ } else if(i->compare(0, 2, "HU") == 0) {
+ hub = u;
+ } else if(i->compare(0, 2, "DE") == 0) {
+ u->setDescription(i->substr(2));
+ } else if(i->compare(0, 2, "I4") == 0) {
+ u->setIp(i->substr(2));
+ } else if(i->compare(0, 2, "SS") == 0) {
+ u->setBytesShared(i->substr(2));
+ } else if(i->compare(0, 2, "VE") == 0) {
+ ve = i->substr(2);
+ } else if(i->compare(0, 2, "EM") == 0) {
+ u->setEmail(i->substr(2));
+ } else if(i->compare(0, 2, "OP") == 0) {
+ if(i->length() == 2) {
+ u->unsetFlag(User::OP);
+ } else {
+ u->setFlag(User::OP);
+ }
+ } else if(i->compare(0, 2, "HO") == 0) {
+ op = Util::toInt(i->substr(2));
+ } else if(i->compare(0, 2, "HR") == 0) {
+ reg = Util::toInt(i->substr(2));
+ } else if(i->compare(0, 2, "HN") == 0) {
+ norm = Util::toInt(i->substr(2));
+ } else if(i->compare(0, 2, "SL") == 0) {
+ sl = Util::toInt(i->substr(2));
+ u->setSlots(sl);
+ } else if(i->compare(0, 2, "BO") == 0) {
+ if(i->length() == 2) {
+ u->unsetFlag(User::BOT);
+ } else {
+ u->setFlag(User::BOT);
+ }
+ } else if(i->compare(0, 2, "HI") == 0) {
+ if(i->length() == 2) {
+ u->unsetFlag(User::HIDDEN);
+ } else {
+ u->setFlag(User::HIDDEN);
+ }
+ } else if(i->compare(0, 2, "HU") == 0) {
+ if(i->length() == 2) {
+ u->unsetFlag(User::HUB);
+ } else {
+ u->setFlag(User::HUB);
+ }
+ } else if(i->compare(0, 2, "U4") == 0) {
+ u->setUDPPort((short)Util::toInt(i->substr(2)));
+ }
+ }
+
+ if(!ve.empty()) {
+ if(ve.find(' ') != string::npos) {
+ ve.insert(ve.find(' ') + 1, "V:");
+ }
+ u->setTag("<" + ve + ",M:" + string(u->getIp().empty() ? "P" : "A") + ",H:" + Util::toString(norm) + "/" +
+ Util::toString(reg) + "/" + Util::toString(op) + ",S:" +
+ Util::toString(sl) + ">" );
+ }
+
+ if(u == getMe())
+ state = STATE_NORMAL;
+
+ fire(ClientListener::UserUpdated(), this, u);
+}
+
+void AdcHub::handle(AdcCommand::SUP, AdcCommand& c) throw() {
+ if(state != STATE_PROTOCOL) /** @todo SUP changes */
+ return;
+ if(find(c.getParameters().begin(), c.getParameters().end(), "+BASE") == c.getParameters().end()) {
+ disconnect();
+ return;
+ }
+ state = STATE_IDENTIFY;
+ info(true);
+}
+
+void AdcHub::handle(AdcCommand::MSG, AdcCommand& c) throw() {
+ if(c.getParameters().empty())
+ return;
+
+ User::Ptr p = cidMap[c.getFrom()];
+ if(!p)
+ return;
+ string pmFrom;
+ if(c.getParam("PM", 1, pmFrom)) { // add PM<group-cid> as well
+ User::Ptr pm = cidMap[CID(pmFrom)];
+ if(!pm)
+ return;
+
+ if(pm == getMe()) {
+ return;
+ }
+ string msg = '<' + p->getNick() + "> " + c.getParam(0);
+ fire(ClientListener::PrivateMessage(), this, pm, msg);
+ } else {
+ string msg = '<' + p->getNick() + "> " + c.getParam(0);
+ fire(ClientListener::Message(), this, msg);
+ }
+}
+
+void AdcHub::handle(AdcCommand::GPA, AdcCommand& c) throw() {
+ if(c.getParameters().empty())
+ return;
+ salt = c.getParam(0);
+ state = STATE_VERIFY;
+
+ fire(ClientListener::GetPassword(), this);
+}
+
+void AdcHub::handle(AdcCommand::QUI, AdcCommand& c) throw() {
+ User::Ptr p = cidMap[CID(c.getParam(0))];
+ if(!p)
+ return;
+ if(!p->getNick().empty()) {
+ Lock l(cs);
+ nickMap.erase(p->getNick());
+ }
+ ClientManager::getInstance()->putUserOffline(p);
+ fire(ClientListener::UserRemoved(), this, p);
+ cidMap.erase(CID(c.getParam(0)));
+}
+
+void AdcHub::handle(AdcCommand::CTM, AdcCommand& c) throw() {
+ User::Ptr p = cidMap[c.getFrom()];
+ if(!p || p == getMe())
+ return;
+ if(c.getParameters().size() < 3)
+ return;
+
+ if(c.getParam(0) != CLIENT_PROTOCOL) {
+ // Protocol unhandled...
+ AdcCommand cc(AdcCommand::CMD_STA, p->getCID());
+ cc.addParam(Util::toString(AdcCommand::ERROR_PROTOCOL_UNSUPPORTED));
+ cc.addParam(c.getParam(0));
+ cc.addParam(c.getParam(1));
+ cc.addParam("Protocol unsupported");
+ send(cc);
+ return;
+ }
+ string token;
+ c.getParam("TO", 2, token);
+ ConnectionManager::getInstance()->adcConnect(p->getIp(), (short)Util::toInt(c.getParameters()[1]), token);
+}
+
+void AdcHub::handle(AdcCommand::RCM, AdcCommand& c) throw() {
+ if(SETTING(CONNECTION_TYPE) != SettingsManager::CONNECTION_ACTIVE)
+ return;
+ User::Ptr p = cidMap[c.getFrom()];
+ if(!p || p == getMe())
+ return;
+ if(c.getParameters().empty() || c.getParameters()[0] != CLIENT_PROTOCOL)
+ return;
+ string token;
+ c.getParam("TO", 1, token);
+ connect(&*p, token);
+}
+
+void AdcHub::sendUDP(const AdcCommand& cmd) {
+ try {
+ Socket s;
+ s.create(Socket::TYPE_UDP);
+
+ string tmp = cmd.toString();
+ for(User::NickIter i = nickMap.begin(); i != nickMap.end(); ++i) {
+ if(i->second->getUDPPort() != 0 && !i->second->getIp().empty()) {
+ try {
+ s.writeTo(i->second->getIp(), i->second->getUDPPort(), tmp);
+ } catch(const SocketException& e) {
+ dcdebug("AdcHub::sendUDP: write failed: %s\n", e.getError().c_str());
+ }
+ }
+ }
+ } catch(SocketException&) {
+ dcdebug("Can't create udp socket\n");
+ }
+}
+
+void AdcHub::handle(AdcCommand::STA, AdcCommand& c) throw() {
+ if(c.getParameters().size() < 2)
+ return;
+
+ fire(ClientListener::Message(), this, c.getParam(1));
+}
+
+void AdcHub::handle(AdcCommand::SCH, AdcCommand& c) throw() {
+ fire(ClientListener::AdcSearch(), this, c);
+}
+
+void AdcHub::connect(const User* user) {
+ u_int32_t r = Util::rand();
+ connect(user, Util::toString(r));
+}
+
+void AdcHub::connect(const User* user, string const& token) {
+ if(state != STATE_NORMAL)
+ return;
+
+ if(SETTING(CONNECTION_TYPE) == SettingsManager::CONNECTION_ACTIVE) {
+ send(AdcCommand(AdcCommand::CMD_CTM, user->getCID()).addParam(CLIENT_PROTOCOL).addParam(Util::toString(SETTING(IN_PORT))).addParam(token));
+ } else {
+ send(AdcCommand(AdcCommand::CMD_RCM, user->getCID()).addParam(CLIENT_PROTOCOL));
+ }
+}
+
+void AdcHub::disconnect() {
+ state = STATE_PROTOCOL;
+ Client::disconnect();
+ {
+ Lock l(cs);
+ clearUsers();
+ }
+}
+
+void AdcHub::hubMessage(const string& aMessage) {
+ if(state != STATE_NORMAL)
+ return;
+ send(AdcCommand(AdcCommand::CMD_MSG, AdcCommand::TYPE_BROADCAST).addParam(aMessage));
+}
+
+void AdcHub::privateMessage(const User* user, const string& aMessage) {
+ if(state != STATE_NORMAL)
+ return;
+ send(AdcCommand(AdcCommand::CMD_MSG, user->getCID()).addParam(aMessage).addParam("PM", SETTING(CLIENT_ID)));
+}
+
+void AdcHub::search(int aSizeMode, int64_t aSize, int aFileType, const string& aString, const string& aToken) {
+ if(state != STATE_NORMAL)
+ return;
+
+
+ AdcCommand c(AdcCommand::CMD_SCH, AdcCommand::TYPE_UDP);
+
+ if(aFileType == SearchManager::TYPE_TTH) {
+ c.addParam("TR", aString);
+ } else {
+ if(aSizeMode == SearchManager::SIZE_ATLEAST) {
+ c.addParam(">=", Util::toString(aSize));
+ } else if(aSizeMode == SearchManager::SIZE_ATMOST) {
+ c.addParam("<=", Util::toString(aSize));
+ }
+ StringTokenizer<string> st(aString, ' ');
+ for(StringIter i = st.getTokens().begin(); i != st.getTokens().end(); ++i) {
+ c.addParam("++", *i);
+ }
+ }
+
+ if(!aToken.empty())
+ c.addParam("TO", aToken);
+
+ sendUDP(c);
+
+ if(SETTING(CONNECTION_TYPE) == SettingsManager::CONNECTION_ACTIVE) {
+ c.setType(AdcCommand::TYPE_PASSIVE);
+ send(c);
+ }
+}
+
+void AdcHub::password(const string& pwd) {
+ if(state != STATE_VERIFY)
+ return;
+ if(!salt.empty()) {
+ size_t saltBytes = salt.size() * 5 / 8;
+ AutoArray<u_int8_t> buf(saltBytes);
+ Encoder::fromBase32(salt.c_str(), buf, saltBytes);
+ TigerHash th;
+ th.update(SETTING(CLIENT_ID).c_str(), SETTING(CLIENT_ID).length());
+ th.update(pwd.data(), pwd.length());
+ th.update(buf, saltBytes);
+ send(AdcCommand(AdcCommand::CMD_PAS, AdcCommand::TYPE_HUB).addParam(Encoder::toBase32(th.finalize(), TigerHash::HASH_SIZE)));
+ salt.clear();
+ }
+}
+
+void AdcHub::info(bool /*alwaysSend*/) {
+ if(state != STATE_IDENTIFY && state != STATE_NORMAL)
+ return;
+ if(!getMe())
+ return;
+
+ AdcCommand c(AdcCommand::CMD_INF, AdcCommand::TYPE_BROADCAST);
+ string tmp;
+
+ StringMapIter i;
+#define ADDPARAM(var, content) \
+ tmp = content; \
+ if((i = lastInfoMap.find(var)) != lastInfoMap.end()) { \
+ if(i->second != tmp) { \
+ if(tmp.empty()) \
+ lastInfoMap.erase(i); \
+ else \
+ i->second = tmp; \
+ c.addParam(var, tmp); \
+ } \
+ } else if(!tmp.empty()) { \
+ c.addParam(var, tmp); \
+ lastInfoMap[var] = tmp; \
+ }
+
+ ADDPARAM("NI", getNick());
+ ADDPARAM("DE", getDescription());
+ ADDPARAM("SL", Util::toString(SETTING(SLOTS)));
+ ADDPARAM("SS", ShareManager::getInstance()->getShareSizeString());
+ ADDPARAM("SF", Util::toString(ShareManager::getInstance()->getSharedFiles()));
+ ADDPARAM("EM", SETTING(EMAIL));
+ ADDPARAM("HN", Util::toString(counts.normal));
+ ADDPARAM("HR", Util::toString(counts.registered));
+ ADDPARAM("HO", Util::toString(counts.op));
+ ADDPARAM("VE", "++ " VERSIONSTRING);
+ if(SETTING(CONNECTION_TYPE) == SettingsManager::CONNECTION_ACTIVE) {
+ ADDPARAM("I4", "0.0.0.0");
+ ADDPARAM("U4", Util::toString(SETTING(UDP_PORT)));
+ } else {
+ ADDPARAM("U4", "");
+ }
+
+#undef ADDPARAM
+
+ if(c.getParameters().size() > 0) {
+ send(c);
+ }
+}
+
+string AdcHub::checkNick(const string& aNick) {
+ string tmp = aNick;
+ string::size_type i = 0;
+ while( (i = tmp.find_first_of(" ", i)) != string::npos) {
+ tmp[i++]='_';
+ }
+ return tmp;
+}
+
+string AdcHub::getHubURL() {
+ return getAddressPort();
+}
+
+void AdcHub::clearUsers() {
+ for(User::NickIter i = nickMap.begin(); i != nickMap.end(); ++i) {
+ ClientManager::getInstance()->putUserOffline(i->second);
+ }
+ nickMap.clear();
+ cidMap.clear();
+}
+
+void AdcHub::on(Connected) throw() {
+ dcassert(state == STATE_PROTOCOL);
+ setMe(ClientManager::getInstance()->getUser(CID(SETTING(CLIENT_ID)), this, false));
+ lastInfoMap.clear();
+ send(AdcCommand(AdcCommand::CMD_SUP, AdcCommand::TYPE_HUB).addParam("+BAS0"));
+
+ fire(ClientListener::Connected(), this);
+}
+
+void AdcHub::on(Line, const string& aLine) throw() {
+ if(BOOLSETTING(ADC_DEBUG)) {
+ fire(ClientListener::Message(), this, "<ADC>" + aLine + "</ADC>");
+ }
+ dispatch(aLine);
+}
+
+void AdcHub::on(Failed, const string& aLine) throw() {
+ clearUsers();
+ setMe(NULL);
+ state = STATE_PROTOCOL;
+ fire(ClientListener::Failed(), this, aLine);
+}
+/**
+ * @file
+ * $Id: AdcHub.cpp,v 1.2 2005/08/21 14:03:43 olof Exp $
+ */
diff --git a/dcpp/AdcHub.h b/dcpp/AdcHub.h
new file mode 100644
index 0000000..1afd81a
--- /dev/null
+++ b/dcpp/AdcHub.h
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2001-2005 Jacek Sieka, arnetheduck on gmail point 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 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.
+ */
+
+#include "Client.h"
+#include "AdcCommand.h"
+#include "User.h"
+
+class ClientManager;
+
+class AdcHub : public Client, public CommandHandler<AdcHub> {
+public:
+
+ virtual void connect(const User* user);
+ virtual void connect(const User* user, string const& token);
+ virtual void disconnect();
+
+ virtual void hubMessage(const string& aMessage);
+ virtual void privateMessage(const User* user, const string& aMessage);
+ virtual void send(const string& aMessage) { socket->write(aMessage); };
+ virtual void sendUserCmd(const string& aUserCmd) { send(aUserCmd); }
+ virtual void search(int aSizeMode, int64_t aSize, int aFileType, const string& aString, const string& aToken);
+ virtual void password(const string& pwd);
+ virtual void info(bool alwaysSend);
+
+ virtual size_t getUserCount() const { return 0;};
+ virtual int64_t getAvailable() const { return 0; };
+ virtual const string& getName() const { return (hub ? hub->getNick() : getAddressPort()); };
+ virtual bool getOp() const { return getMe() ? getMe()->isSet(User::OP) : false; };
+
+ virtual User::NickMap& lockUserList() { return nickMap; };
+ virtual void unlockUserList() { };
+
+ template<typename T> void handle(T, AdcCommand&) {
+ //Speaker<AdcHubListener>::fire(t, this, c);
+ }
+
+ void send(const AdcCommand& cmd) { socket->write(cmd.toString(false)); };
+ void sendUDP(const AdcCommand& cmd);
+
+ void handle(AdcCommand::SUP, AdcCommand& c) throw();
+ void handle(AdcCommand::MSG, AdcCommand& c) throw();
+ void handle(AdcCommand::INF, AdcCommand& c) throw();
+ void handle(AdcCommand::GPA, AdcCommand& c) throw();
+ void handle(AdcCommand::QUI, AdcCommand& c) throw();
+ void handle(AdcCommand::CTM, AdcCommand& c) throw();
+ void handle(AdcCommand::RCM, AdcCommand& c) throw();
+ void handle(AdcCommand::STA, AdcCommand& c) throw();
+ void handle(AdcCommand::SCH, AdcCommand& c) throw();
+
+ virtual string escape(string const& str) const { return AdcCommand::escape(str, false); };
+
+private:
+ friend class ClientManager;
+
+ enum States {
+ STATE_PROTOCOL,
+ STATE_IDENTIFY,
+ STATE_VERIFY,
+ STATE_NORMAL
+ } state;
+
+ AdcHub(const string& aHubURL);
+
+ AdcHub(const AdcHub&);
+ AdcHub& operator=(const AdcHub&);
+ virtual ~AdcHub() throw();
+ User::NickMap nickMap;
+ User::CIDMap cidMap;
+ User::Ptr hub;
+ StringMap lastInfoMap;
+ CriticalSection cs;
+
+ string salt;
+
+ static const string CLIENT_PROTOCOL;
+
+ virtual string checkNick(const string& nick);
+ virtual string getHubURL();
+
+ void clearUsers();
+
+ virtual void on(Connecting) throw() { fire(ClientListener::Connecting(), this); }
+ virtual void on(Connected) throw();
+ virtual void on(Line, const string& aLine) throw();
+ virtual void on(Failed, const string& aLine) throw();
+};
+
+/**
+ * @file
+ * $Id: AdcHub.h,v 1.2 2005/08/21 14:03:43 olof Exp $
+ */
diff --git a/dcpp/BZUtils.cpp b/dcpp/BZUtils.cpp
new file mode 100644
index 0000000..2694fec
--- /dev/null
+++ b/dcpp/BZUtils.cpp
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2001-2005 Jacek Sieka, arnetheduck on gmail point 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 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.
+ */
+
+#include "stdinc.h"
+#include "DCPlusPlus.h"
+
+#include "BZUtils.h"
+#include "Exception.h"
+#include "ResourceManager.h"
+
+BZFilter::BZFilter() {
+ memset(&zs, 0, sizeof(zs));
+
+ if(BZ2_bzCompressInit(&zs, 9, 0, 30) != BZ_OK) {
+ throw Exception(STRING(COMPRESSION_ERROR));
+ }
+}
+
+BZFilter::~BZFilter() {
+ dcdebug("BZFilter end, %u/%u = %.04f\n", zs.total_out_lo32, zs.total_in_lo32, (float)zs.total_out_lo32 / max((float)zs.total_in_lo32, (float)1));
+ BZ2_bzCompressEnd(&zs);
+}
+
+bool BZFilter::operator()(const void* in, size_t& insize, void* out, size_t& outsize) {
+ if(outsize == 0)
+ return 0;
+
+ zs.avail_in = insize;
+ zs.next_in = (char*)in;
+ zs.avail_out = outsize;
+ zs.next_out = (char*)out;
+
+ if(insize == 0) {
+ int err = ::BZ2_bzCompress(&zs, BZ_FINISH);
+ if(err != BZ_FINISH_OK && err != BZ_STREAM_END)
+ throw Exception(STRING(COMPRESSION_ERROR));
+
+ outsize = outsize - zs.avail_out;
+ insize = insize - zs.avail_in;
+ return err == BZ_FINISH_OK;
+ } else {
+ int err = ::BZ2_bzCompress(&zs, BZ_RUN);
+ if(err != BZ_RUN_OK)
+ throw Exception(STRING(COMPRESSION_ERROR));
+
+ outsize = outsize - zs.avail_out;
+ insize = insize - zs.avail_in;
+ return true;
+ }
+}
+
+UnBZFilter::UnBZFilter() {
+ memset(&zs, 0, sizeof(zs));
+
+ if(BZ2_bzDecompressInit(&zs, 0, 0) != BZ_OK)
+ throw Exception(STRING(DECOMPRESSION_ERROR));
+
+}
+
+UnBZFilter::~UnBZFilter() {
+ dcdebug("UnBZFilter end, %u/%u = %.04f\n", zs.total_out_lo32, zs.total_in_lo32, (float)zs.total_out_lo32 / max((float)zs.total_in_lo32, (float)1));
+ BZ2_bzDecompressEnd(&zs);
+}
+
+bool UnBZFilter::operator()(const void* in, size_t& insize, void* out, size_t& outsize) {
+ if(outsize == 0)
+ return 0;
+
+ zs.avail_in = insize;
+ zs.next_in = (char*)in;
+ zs.avail_out = outsize;
+ zs.next_out = (char*)out;
+
+ int err = ::BZ2_bzDecompress(&zs);
+
+ // No more input data, and inflate didn't think it has reached the end...
+ if(insize == 0 && zs.avail_out != 0 && err != BZ_STREAM_END)
+ throw Exception(STRING(DECOMPRESSION_ERROR));
+
+ if(err != BZ_OK && err != BZ_STREAM_END)
+ throw Exception(STRING(DECOMPRESSION_ERROR));
+
+ outsize = outsize - zs.avail_out;
+ insize = insize - zs.avail_in;
+ return err == BZ_OK;
+}
+
+/**
+ * @file
+ * $Id: BZUtils.cpp,v 1.2 2005/08/21 14:03:43 olof Exp $
+ */
diff --git a/dcpp/BZUtils.h b/dcpp/BZUtils.h
new file mode 100644
index 0000000..b81bbbc
--- /dev/null
+++ b/dcpp/BZUtils.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2001-2005 Jacek Sieka, arnetheduck on gmail point 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 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.
+ */
+
+#ifndef _BZ_UTILS
+#define _BZ_UTILS
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+#ifdef _WIN32
+#include "../bzip2/bzlib.h"
+#else
+#include <bzlib.h>
+#endif
+
+class BZFilter {
+public:
+ BZFilter();
+ ~BZFilter();
+ /**
+ * Compress data.
+ * @param in Input data
+ * @param insize Input size (Set to 0 to indicate that no more data will follow)
+ * @param out Output buffer
+ * @param outsize Output size, set to compressed size on return.
+ * @return True if there's more processing to be done.
+ */
+ bool operator()(const void* in, size_t& insize, void* out, size_t& outsize);
+private:
+ bz_stream zs;
+};
+
+class UnBZFilter {
+public:
+ UnBZFilter();
+ ~UnBZFilter();
+ /**
+ * Decompress data.
+ * @param in Input data
+ * @param insize Input size (Set to 0 to indicate that no more data will follow)
+ * @param out Output buffer
+ * @param outsize Output size, set to decompressed size on return.
+ * @return True if there's more processing to be done
+ */
+ bool operator()(const void* in, size_t& insize, void* out, size_t& outsize);
+private:
+ bz_stream zs;
+};
+
+#endif // _BZ_UTILS
+
+/**
+* @file
+* $Id: BZUtils.h,v 1.2 2005/08/21 14:03:43 olof Exp $
+*/
diff --git a/dcpp/BitInputStream.h b/dcpp/BitInputStream.h
new file mode 100644
index 0000000..83d4219
--- /dev/null
+++ b/dcpp/BitInputStream.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2001-2005 Jacek Sieka, arnetheduck on gmail point 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 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.
+ */
+
+#if !defined(AFX_BITINPUTSTREAM_H__EAF695A9_6D5C_4791_88A2_3FA0D47697AF__INCLUDED_)
+#define AFX_BITINPUTSTREAM_H__EAF695A9_6D5C_4791_88A2_3FA0D47697AF__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+#include "Exception.h"
+#include "ResourceManager.h"
+
+STANDARD_EXCEPTION(BitStreamException);
+
+/**
+ * A clumsy bit streamer, assumes that there's enough data to complete the operations.
+ * No, doesn't operate on streams...=)
+ */
+class BitInputStream
+{
+public:
+ BitInputStream(const u_int8_t* aStream, size_t aStart, size_t aEnd) : bitPos(aStart*8), endPos(aEnd*8), is(aStream) { };
+ ~BitInputStream() { };
+
+ bool get() throw(BitStreamException) {
+ if(bitPos > endPos) {
+ throw BitStreamException(STRING(SEEK_BEYOND_END));
+ }
+ bool ret = (((u_int8_t)is[bitPos>>3]) >> (bitPos&0x07)) & 0x01;
+ bitPos++;
+ return ret;
+ }
+
+ void skipToByte() {
+ if(bitPos%8 != 0)
+ bitPos = (bitPos & (~7)) + 8;
+ }
+
+ void skip(int n) {
+ bitPos += n * 8;
+ return ;
+ }
+private:
+ BitInputStream(const BitInputStream&);
+ BitInputStream& operator=(const BitInputStream&);
+
+ size_t bitPos;
+ size_t endPos;
+ const u_int8_t* is;
+};
+
+#endif // !defined(AFX_BITINPUTSTREAM_H__EAF695A9_6D5C_4791_88A2_3FA0D47697AF__INCLUDED_)
+
+/**
+ * @file
+ * $Id: BitInputStream.h,v 1.2 2005/08/21 14:03:43 olof Exp $
+ */
diff --git a/dcpp/BitOutputStream.h b/dcpp/BitOutputStream.h
new file mode 100644
index 0000000..bd54301
--- /dev/null
+++ b/dcpp/BitOutputStream.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2001-2005 Jacek Sieka, arnetheduck on gmail point 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 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.
+ */
+
+#if !defined(AFX_BITOUTPUTSTREAM_H__EAF695A9_6D5C_4791_88A2_3FA0D47697AF__INCLUDED_)
+#define AFX_BITOUTPUTSTREAM_H__EAF695A9_6D5C_4791_88A2_3FA0D47697AF__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+class BitOutputStream
+{
+public:
+ BitOutputStream(string& aStream) : is(aStream), bitPos(0), next(0) { };
+ ~BitOutputStream() { };
+
+ void put(vector<u_int8_t>& b) {
+ for(vector<u_int8_t>::iterator i = b.begin(); i != b.end(); ++i) {
+ next |= (*i) << bitPos++;
+
+ if(bitPos > 7) {
+ bitPos-=8;
+ is += next;
+ next = 0;
+ }
+
+ }
+ }
+
+ void skipToByte() {
+ if(bitPos > 0) {
+ bitPos = 0;
+ is += next;
+ next = 0;
+ }
+ }
+
+private:
+ BitOutputStream& operator=(const BitOutputStream&);
+ string& is;
+ int bitPos;
+ u_int8_t next;
+};
+
+#endif // !defined(AFX_BITINPUTSTREAM_H__EAF695A9_6D5C_4791_88A2_3FA0D47697AF__INCLUDED_)
+
+/**
+ * @file
+ * $Id: BitOutputStream.h,v 1.2 2005/08/21 14:03:43 olof Exp $
+ */
diff --git a/dcpp/BloomFilter.h b/dcpp/BloomFilter.h
new file mode 100644
index 0000000..2f437a1
--- /dev/null
+++ b/dcpp/BloomFilter.h
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2001-2005 Jacek Sieka, arnetheduck on gmail point 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 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.
+ */
+
+#ifndef _BLOOM_FILTER
+#define _BLOOM_FILTER
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+#include "ZUtils.h"
+
+struct CRC32Hash {
+ size_t operator()(const void* buf, size_t len) { f(buf, len); return f.getValue(); }
+private:
+ CRC32Filter f;
+};
+
+template<size_t N, class HashFunc = CRC32Hash>
+class BloomFilter {
+public:
+ BloomFilter(size_t tableSize) { table.resize(tableSize); };
+ ~BloomFilter() { };
+
+ void add(const string& s) { xadd(s, N); }
+ bool match(const StringList& s) {
+ for(StringList::const_iterator i = s.begin(); i != s.end(); ++i) {
+ if(!match(*i))
+ return false;
+ }
+ return true;
+ }
+ bool match(const string& s) {
+ if(s.length() >= N) {
+ string::size_type l = s.length() - N;
+ for(string::size_type i = 0; i <= l; ++i) {
+ if(!table[getPos(s, i, N)]) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+ void clear() {
+ size_t s = table.size();
+ table.clear();
+ table.resize(s);
+ }
+#ifdef TESTER
+ void print_table_status() {
+ int tot = 0;
+ for (unsigned int i = 0; i < table.size(); ++i) if (table[i] == true) ++tot;
+
+ std::cout << "table status: " << tot << " of " << table.size()
+ << " filled, for an occupancy percentage of " << (100.*tot)/table.size()
+ << "%" << std::endl;
+ }
+#endif
+private:
+ void xadd(const string& s, size_t n) {
+ if(s.length() >= n) {
+ string::size_type l = s.length() - n;
+ for(string::size_type i = 0; i <= l; ++i) {
+ table[getPos(s, i, n)] = true;
+ }
+ }
+ }
+
+ /* Same functionality, but the old one did not want to compile for some reason. */
+ size_t getPos(const string& s, size_t i, size_t l) {
+ HashFunc hf;
+ return (hf(&s[i], l) % table.size());
+ }
+
+ vector<bool> table;
+};
+
+#endif // _BLOOM_FILTER
+
+/**
+ * @file
+ * $Id: BloomFilter.h,v 1.2 2005/08/21 14:03:43 olof Exp $
+ */
diff --git a/dcpp/BufferedSocket.cpp b/dcpp/BufferedSocket.cpp
new file mode 100644
index 0000000..e78cf59
--- /dev/null
+++ b/dcpp/BufferedSocket.cpp
@@ -0,0 +1,441 @@
+/*
+ * Copyright (C) 2001-2005 Jacek Sieka, arnetheduck on gmail point 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 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.
+ */
+
+#include "stdinc.h"
+#include "DCPlusPlus.h"
+
+#include "BufferedSocket.h"
+
+#include "ResourceManager.h"
+#include "TimerManager.h"
+#include "SettingsManager.h"
+
+#include "Streams.h"
+
+#define SMALL_BUFFER_SIZE 1024
+
+// Polling is used for tasks...should be fixed...
+#define POLL_TIMEOUT 250
+
+BufferedSocket::BufferedSocket(char aSeparator) throw(SocketException) :
+separator(aSeparator), port(0), mode(MODE_LINE),
+dataBytes(0), escaped(false), inbufSize(64*1024), curBuf(0), file(NULL) {
+
+ inbuf = new u_int8_t[inbufSize];
+
+ for(int i = 0; i < BUFFERS; i++) {
+ outbuf[i] = new u_int8_t[inbufSize];
+ outbufPos[i] = 0;
+ outbufSize[i] = inbufSize;
+ }
+
+ try {
+ start();
+ } catch(const ThreadException& e) {
+ delete[] inbuf;
+ for(int i = 0; i < BUFFERS; i++) {
+ delete[] outbuf[i];
+ }
+ throw SocketException(e.getError());
+ }
+}
+
+BufferedSocket::~BufferedSocket() throw() {
+ delete[] inbuf;
+ for(int i = 0; i < BUFFERS; i++) {
+ delete[] outbuf[i];
+ }
+}
+
+/**
+ * Send a chunk of a file
+ * @return True if file is finished, false if there's more data to send
+ */
+bool BufferedSocket::threadSendFile() {
+ dcassert(file != NULL);
+ try {
+ for(;;) {
+ {
+ Lock l(cs);
+ if(!tasks.empty())
+ return false;
+ }
+ size_t s = (BOOLSETTING(SMALL_SEND_BUFFER) ? SMALL_BUFFER_SIZE : inbufSize);
+ size_t actual = file->read(inbuf, s);
+ if(actual > 0) {
+ Socket::write((char*)inbuf, actual);
+ fire(BufferedSocketListener::BytesSent(), s, actual);
+ } else {
+ fire(BufferedSocketListener::TransmitDone());
+ return true;
+ }
+ }
+ } catch(const Exception& e) {
+ fail(e.getError());
+ return true;
+ }
+}
+
+bool BufferedSocket::fillBuffer(char* buf, int bufLen, u_int32_t timeout /* = 0 */) throw(SocketException) {
+ dcassert(buf != NULL);
+ dcassert(bufLen > 0);
+
+ int bytesIn = 0;
+ u_int32_t startTime = GET_TICK();
+
+ while(bytesIn < bufLen) {
+ while(!wait(POLL_TIMEOUT, WAIT_READ)) {
+ {
+ Lock l(cs);
+ if(!tasks.empty()) {
+ // We don't want to handle tasks here...
+ Socket::disconnect();
+ return false;
+ }
+ }
+ if((timeout != 0) && ((startTime + timeout) < GET_TICK())) {
+ // Connection timeout
+ throw SocketException(STRING(CONNECTION_TIMEOUT));
+ }
+ }
+ dcassert(bufLen > bytesIn);
+ int x = Socket::read(buf + bytesIn, bufLen - bytesIn);
+ if(x <= 0) {
+ // ???
+ throw SocketException(STRING(CONNECTION_CLOSED));
+ }
+ bytesIn += x;
+ }
+
+ return true;
+}
+
+void BufferedSocket::threadConnect() {
+ dcdebug("threadConnect()\n");
+
+ fire(BufferedSocketListener::Connecting());
+
+ u_int32_t startTime = GET_TICK();
+ string s;
+ short p;
+ {
+ Lock l(cs);
+
+ s=address;
+ p=port;
+ }
+
+ try {
+ setBlocking(false);
+ Socket::create();
+
+ if( !getNoproxy() && SETTING(CONNECTION_TYPE) == SettingsManager::CONNECTION_SOCKS5) {
+ if(!BOOLSETTING(SOCKS_RESOLVE)) {
+ s = resolve(s);
+ }
+ Socket::connect(SETTING(SOCKS_SERVER), (short)SETTING(SOCKS_PORT));
+ } else {
+ Socket::connect(s, p);
+ }
+
+ while(!wait(POLL_TIMEOUT, WAIT_CONNECT)) {
+ {
+ Lock l(cs);
+ if(!tasks.empty()) {
+ // We don't want to handle tasks here...
+ Socket::disconnect();
+ return;
+ }
+ }
+ if((startTime + 30000) < GET_TICK()) {
+ // Connection timeout
+ fail(STRING(CONNECTION_TIMEOUT));
+ return;
+ }
+ }
+
+ // Hm, let's see if we're socksified...
+ if( !getNoproxy() && SETTING(CONNECTION_TYPE) == SettingsManager::CONNECTION_SOCKS5) {
+ if(SETTING(SOCKS_USER).empty() && SETTING(SOCKS_PASSWORD).empty()) {
+ // No username and pw, easier...=)
+ char connStr[3];
+ connStr[0] = 5; // SOCKSv5
+ connStr[1] = 1; // 1 method
+ connStr[2] = 0; // Method 0: No auth...
+
+ Socket::write(connStr, 3);
+
+ if(!fillBuffer(connStr, 2, 30000))
+ return;
+
+ if(connStr[1] != 0) {
+ fail(STRING(SOCKS_NEEDS_AUTH));
+ return;
+ }
+ } else {
+ // We try the username and password auth type (no, we don't support gssapi)
+ u_int8_t ulen = (u_int8_t)(SETTING(SOCKS_USER).length() & 0xff);
+ u_int8_t plen = (u_int8_t)(SETTING(SOCKS_PASSWORD).length() & 0xff);
+ AutoArray<u_int8_t> connStr(3 + ulen + plen);
+
+ connStr[0] = 5; // SOCKSv5
+ connStr[1] = 1; // 1 method
+ connStr[2] = 2; // Method 2: Name/Password...
+ Socket::write((char*)(u_int8_t*)connStr, 3);
+ if(!fillBuffer((char*)(u_int8_t*)connStr, 2, 30000))
+ return;
+ if(connStr[1] != 2) {
+ fail(STRING(SOCKS_AUTH_UNSUPPORTED));
+ return;
+ }
+ // Now we send the username / pw...
+ connStr[0] = 1;
+ connStr[1] = ulen;
+ strncpy((char*)(u_int8_t*)connStr + 2, SETTING(SOCKS_USER).c_str(), ulen);
+ connStr[2 + ulen] = plen;
+ strncpy((char*)(u_int8_t*)connStr + 3 + ulen, SETTING(SOCKS_PASSWORD).c_str(), plen);
+ Socket::write((char*)(u_int8_t*)connStr, 3 + plen + ulen);
+
+ if(!fillBuffer((char*)(u_int8_t*)connStr, 2, 30000)) {
+ return;
+ }
+
+ if(connStr[1] != 0) {
+ fail(STRING(SOCKS_AUTH_FAILED));
+ return;
+ }
+
+ }
+
+ // Alrite, let's get on with it...
+ AutoArray<u_int8_t> connStr(10 + s.length());
+ int connLen;
+ connStr[0] = 5; // SOCKSv5
+ connStr[1] = 1; // Connect
+ connStr[2] = 0; // Reserved
+
+ if(BOOLSETTING(SOCKS_RESOLVE)) {
+ u_int8_t slen =(u_int8_t)(s.length() & 0xff);
+ connStr[3] = 3; // Address type: domain name
+ connStr[4] = slen;
+ strncpy((char*)(u_int8_t*)connStr + 5, s.c_str(), slen);
+ *((u_int16_t*)(&connStr[5 + slen])) = htons(p);
+ connLen = 7 + slen;
+ } else {
+ connStr[3] = 1; // Address type: IPv4;
+ *((long*)(&connStr[4])) = inet_addr(s.c_str());
+ *((u_int16_t*)(&connStr[8])) = htons(p);
+ connLen = 10;
+ }
+
+ Socket::write((char*)(u_int8_t*)connStr, connLen);
+ // We assume we'll get a ipv4 address back...therefore, 10 bytes...if not, things
+ // will break, but hey...noone's perfect (and I'm tired...)...
+ if(!fillBuffer((char*)(u_int8_t*)connStr, 10, 30000)) {
+ return;
+ }
+
+ if(connStr[0] != 5 || connStr[1] != 0) {
+ fail(STRING(SOCKS_FAILED));
+ return;
+ }
+
+ // Yihaa!
+ }
+
+ // We're connected! Clear the buffers...
+ for(int k = 0; k < BUFFERS; k++) {
+ outbufPos[k] = 0;
+ }
+ line.clear();
+ setBlocking(true);
+
+ fire(BufferedSocketListener::Connected());
+ } catch(const SocketException& e) {
+ if(!getNoproxy() && SETTING(CONNECTION_TYPE) == SettingsManager::CONNECTION_SOCKS5) {
+ fail("Socks5: " + e.getError());
+ } else {
+ fail(e.getError());
+ }
+ return;
+ }
+}
+
+void BufferedSocket::threadRead() {
+ try {
+ int i = read(inbuf, inbufSize);
+ if(i == -1) {
+ // EWOULDBLOCK, no data recived...
+ return;
+ } else if(i == 0) {
+ // This socket has been closed...
+ disconnect();
+ return;
+ }
+
+ int bufpos = 0;
+ string l;
+ while(i > 0) {
+ // Special to autodetect nmdc connections...
+ if(separator == 0) {
+ if(inbuf[0] == '$') {
+ separator = '|';
+ } else {
+ separator = '\n';
+ }
+ }
+ if(mode == MODE_LINE) {
+ string::size_type pos = 0;
+
+ l = string((char*)inbuf + bufpos, i);
+
+ if((pos = l.find(separator)) != string::npos) {
+ if(!line.empty()) {
+ fire(BufferedSocketListener::Line(), line + l.substr(0, pos));
+ line.clear();
+ } else {
+ fire(BufferedSocketListener::Line(), l.substr(0, pos));
+ }
+ i -= (pos + sizeof(separator));
+ bufpos += (pos + sizeof(separator));
+ } else {
+ line += l;
+ i = 0;
+ }
+ } else if(mode == MODE_DATA) {
+ if(dataBytes == -1) {
+ fire(BufferedSocketListener::Data(), inbuf+bufpos, i);
+ bufpos+=i;
+ i = 0;
+ } else {
+ int high = (int)min(dataBytes, (int64_t)i);
+ fire(BufferedSocketListener::Data(), inbuf+bufpos, high);
+ bufpos += high;
+ i-=high;
+
+ dataBytes -= high;
+ if(dataBytes == 0) {
+ mode = MODE_LINE;
+ fire(BufferedSocketListener::ModeChange());
+ }
+ }
+ }
+ }
+ } catch(const SocketException& e) {
+ dcdebug("BufferedSocket::threadRead caught: %s\n", e.getError().c_str());
+ // Ouch...
+ fail(e.getError());
+ return;
+ }
+}
+
+void BufferedSocket::write(const char* aBuf, size_t aLen) throw() {
+ {
+ Lock l(cs);
+ size_t newSize = outbufSize[curBuf];
+
+ while(newSize < (aLen + outbufPos[curBuf])) {
+ newSize *= 2;
+ }
+
+ if(newSize > outbufSize[curBuf]) {
+ // Need to grow...
+ dcdebug("Growing outbuf[%d] to %lu bytes\n", curBuf, newSize);
+ u_int8_t* newbuf = new u_int8_t[newSize];
+ memcpy(newbuf, outbuf[curBuf], outbufPos[curBuf]);
+ delete[] outbuf[curBuf];
+ outbuf[curBuf] = newbuf;
+ outbufSize[curBuf] = newSize;
+ }
+
+ memcpy(outbuf[curBuf] + outbufPos[curBuf], aBuf, aLen);
+ outbufPos[curBuf] += aLen;
+ addTask(SEND_DATA);
+ }
+}
+
+void BufferedSocket::threadSendData() {
+ int myBuf;
+
+ {
+ Lock l(cs);
+ myBuf = curBuf;
+ curBuf = (curBuf + 1) % BUFFERS;
+ }
+
+ if(outbufPos[myBuf] == 0)
+ return;
+
+ Socket::write((char*)outbuf[myBuf], outbufPos[myBuf]);
+ outbufPos[myBuf] = 0;
+}
+
+/**
+ * Main task dispatcher for the buffered socket abstraction.
+ * @todo Fix the polling...
+ */
+int BufferedSocket::run() {
+ bool sendingFile = false;
+
+ while(true) {
+ try {
+
+ while(isConnected() ? taskSem.wait(0) : taskSem.wait()) {
+ Tasks t;
+ {
+ Lock l(cs);
+ dcassert(tasks.size() > 0);
+ t = tasks.front();
+ tasks.erase(tasks.begin());
+ }
+
+ switch(t) {
+ case SHUTDOWN: threadShutDown(); return 0;
+ case DISCONNECT: if(isConnected()) fail(STRING(DISCONNECTED)); break;
+ case SEND_FILE: if(isConnected()) sendingFile = true; break;
+ case SEND_DATA: dcassert(!sendingFile); if(isConnected()) threadSendData(); break;
+ case CONNECT: sendingFile = false; threadConnect(); break;
+ case ACCEPTED: break;
+
+ default: dcassert("BufferedSocket::threadRun: Unknown command received" == NULL);
+ }
+ }
+
+ // Now check if there's any activity on the socket
+ if(isConnected()) {
+ int waitFor = wait(POLL_TIMEOUT, sendingFile ? WAIT_READ | WAIT_WRITE : WAIT_READ);
+ if(waitFor & WAIT_WRITE) {
+ dcassert(sendingFile);
+ if(threadSendFile())
+ sendingFile = false;
+ }
+ if(waitFor & WAIT_READ) {
+ threadRead();
+ }
+ }
+ } catch(const SocketException& e) {
+ fail(e.getError());
+ }
+ }
+ return 0;
+}
+
+/**
+ * @file
+ * $Id: BufferedSocket.cpp,v 1.2 2005/08/21 14:03:43 olof Exp $
+ */
diff --git a/dcpp/BufferedSocket.h b/dcpp/BufferedSocket.h
new file mode 100644
index 0000000..e10e724
--- /dev/null
+++ b/dcpp/BufferedSocket.h
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 2001-2005 Jacek Sieka, arnetheduck on gmail point 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 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.
+ */
+
+#if !defined(AFX_BUFFEREDSOCKET_H__0760BAF6_91F5_481F_BFF7_7CA192EE44CC__INCLUDED_)
+#define AFX_BUFFEREDSOCKET_H__0760BAF6_91F5_481F_BFF7_7CA192EE44CC__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+#include "Socket.h"
+#include "Semaphore.h"
+#include "Thread.h"
+#include "Speaker.h"
+
+class InputStream;
+
+class BufferedSocketListener {
+public:
+ template<int I> struct X { enum { TYPE = I }; };
+
+ typedef X<0> Connecting;
+ typedef X<1> Connected;
+ typedef X<2> Line;
+ typedef X<3> Data;
+ typedef X<4> BytesSent;
+ typedef X<5> ModeChange;
+ typedef X<6> TransmitDone;
+ typedef X<7> Failed;
+ typedef X<8> Shutdown;
+
+ virtual void on(Connecting) throw() { }
+ virtual void on(Connected) throw() { }
+ virtual void on(Line, const string&) throw() { }
+ virtual void on(Data, u_int8_t*, size_t) throw() { }
+ virtual void on(BytesSent, size_t, size_t) throw() { }
+ virtual void on(ModeChange) throw() { }
+ virtual void on(TransmitDone) throw() { }
+ virtual void on(Failed, const string&) throw() { }
+ virtual void on(Shutdown) throw() { }
+};
+
+class BufferedSocket : public Speaker<BufferedSocketListener>, public Socket, public Thread
+{
+public:
+ enum {
+ MODE_LINE,
+ MODE_DATA
+ };
+
+ enum Tasks {
+ CONNECT,
+ DISCONNECT,
+ SEND_DATA,
+ SEND_FILE,
+ SHUTDOWN,
+ ACCEPTED
+ };
+
+ /**
+ * BufferedSocket factory
+ * @param sep Line separator
+ * @param esc A preceding backslash escapes any character, including the separator
+ * @return An unconnected socket
+ */
+ static BufferedSocket* getSocket(char sep) throw(SocketException) {
+ return new BufferedSocket(sep);
+ };
+
+ static void putSocket(BufferedSocket* aSock) {
+ aSock->removeListeners();
+ aSock->Socket::disconnect();
+ aSock->shutdown();
+ };
+
+ virtual void shutdown() {
+ Lock l(cs);
+ addTask(SHUTDOWN);
+ }
+
+ virtual void accept(const ServerSocket& srv) throw(SocketException) {
+ Socket::accept(srv);
+ Lock l(cs);
+ addTask(ACCEPTED);
+ }
+
+ virtual void disconnect() throw() {
+ Lock l(cs);
+ addTask(DISCONNECT);
+ }
+
+ /**
+ * Sets data mode for aBytes bytes long. Must be called within an action method...
+ */
+ void setDataMode(int64_t aBytes = -1) {
+ mode = MODE_DATA;
+ dataBytes = aBytes;
+ }
+ /**
+ * Should be called when data mode.
+ */
+ void setLineMode() {
+ dcassert(mode == MODE_DATA);
+ dcassert(dataBytes == -1);
+ mode = MODE_LINE;
+ }
+ int getMode() { return mode; };
+
+ /**
+ * Connect to aAddress / aPort
+ * Note; this one doesn't actually throw, but it overrides one that does...
+ */
+ virtual void connect(const string& aAddress, short aPort) throw(SocketException) {
+ Lock l(cs);
+ mode = MODE_LINE;
+ address = aAddress;
+ port = aPort;
+ addTask(CONNECT);
+ }
+
+ void write(const string& aData) throw(SocketException) { write(aData.data(), aData.length()); };
+ virtual void write(const char* aBuf, size_t aLen) throw();
+
+ /**
+ * Send the file f over this socket.
+ */
+ void transmitFile(InputStream* f) throw() {
+ Lock l(cs);
+ file = f;
+ addTask(SEND_FILE);
+ }
+
+ GETSET(char, separator, Separator)
+private:
+ BufferedSocket(char aSeparator = 0x0a) throw(SocketException);
+
+ // Dummy...
+ BufferedSocket(const BufferedSocket&);
+ BufferedSocket& operator=(const BufferedSocket&);
+
+ virtual ~BufferedSocket() throw();
+
+ bool fillBuffer(char* buf, int bufLen, u_int32_t timeout = 0) throw(SocketException);
+
+ CriticalSection cs;
+
+ Semaphore taskSem;
+ vector<Tasks> tasks;
+ string address;
+ short port;
+ int mode;
+ int64_t dataBytes;
+
+ string line;
+ bool escaped;
+ u_int8_t* inbuf;
+ size_t inbufSize;
+ enum {BUFFERS = 2};
+ u_int8_t* outbuf[BUFFERS];
+ size_t outbufSize[BUFFERS];
+ size_t outbufPos[BUFFERS];
+ int curBuf;
+
+ InputStream* file;
+
+ virtual void create(int) throw(SocketException) { dcassert(0); }; // Sockets are created implicitly
+ virtual void bind(short) throw(SocketException) { dcassert(0); }; // Binding / UDP not supported...
+
+ virtual int run();
+
+ void threadConnect();
+ void threadRead();
+ bool threadSendFile();
+ void threadSendData();
+ void threadDisconnect();
+
+ void fail(const string& aError) {
+ Socket::disconnect();
+ fire(BufferedSocketListener::Failed(), aError);
+ }
+
+ /**
+ * Shut down the socket and delete itself...no variables must be referenced after
+ * calling threadShutDown, the thread function should exit as soon as possible
+ */
+ void threadShutDown() {
+ fire(BufferedSocketListener::Shutdown());
+ removeListeners();
+ delete this;
+ }
+
+ void addTask(Tasks task) {
+ tasks.push_back(task);
+ taskSem.signal();
+ }
+};
+
+#endif // !defined(AFX_BUFFEREDSOCKET_H__0760BAF6_91F5_481F_BFF7_7CA192EE44CC__INCLUDED_)
+
+/**
+ * @file
+ * $Id: BufferedSocket.h,v 1.2 2005/08/21 14:03:43 olof Exp $
+ */
diff --git a/dcpp/CID.h b/dcpp/CID.h
new file mode 100644
index 0000000..2b1b1ee
--- /dev/null
+++ b/dcpp/CID.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2001-2005 Jacek Sieka, arnetheduck on gmail point 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 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.
+ */
+
+#if !defined(AFX_CID_H__26AA222C_500B_4AD2_A5AA_A594E1A6D639__INCLUDED_)
+#define AFX_CID_H__26AA222C_500B_4AD2_A5AA_A594E1A6D639__INCLUDED_
+
+#include "Encoder.h"
+#include "Util.h"
+
+class CID {
+public:
+ enum { SIZE = sizeof(u_int64_t) };
+
+ struct Hash {
+ size_t operator()(const CID& c) const { return c.toHash(); }
+ };
+ CID() : cid(0) { }
+ explicit CID(u_int64_t c) : cid(c) { }
+ explicit CID(const string& base32) { Encoder::fromBase32(base32.c_str(),(u_int8_t*)&cid, sizeof(cid)); }
+ CID(const CID& rhs) : cid(rhs.cid) { }
+
+ operator u_int64_t() { return cid; }
+
+ CID& operator=(const CID& rhs) { cid = rhs.cid; return *this; }
+ bool operator==(const CID& rhs) const { return cid == rhs.cid; }
+ bool operator<(const CID& rhs) const { return cid < rhs.cid; }
+
+ string toBase32() const { return Encoder::toBase32((u_int8_t*)&cid, sizeof(cid)); }
+ string& toBase32(string& tmp) const { return Encoder::toBase32((u_int8_t*)&cid, sizeof(cid), tmp); }
+
+ size_t toHash() const { size_t* p = (size_t*)&cid; return *p ^ *(p+1); }
+ const u_int8_t* getData() const { return (u_int8_t*)&cid; }
+
+ bool isZero() const { return cid == 0; }
+
+ static CID generate() {
+ CID cid;
+ u_int32_t* c = (u_int32_t*)&cid.cid;
+ *(c++) = Util::rand();
+ *(c++) = Util::rand();
+ return cid;
+ }
+
+ static const u_int64_t zero = 0;
+private:
+ u_int64_t cid;
+
+};
+
+#endif
+
+/**
+* @file
+* $Id: CID.h,v 1.2 2005/08/21 14:03:43 olof Exp $
+*/
diff --git a/dcpp/Client.cpp b/dcpp/Client.cpp
new file mode 100644
index 0000000..188661c
--- /dev/null
+++ b/dcpp/Client.cpp
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2001-2005 Jacek Sieka, arnetheduck on gmail point 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 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.
+ */
+
+#include "stdinc.h"
+#include "DCPlusPlus.h"
+
+#include "Client.h"
+
+#include "BufferedSocket.h"
+
+#include "HubManager.h"
+
+Client::Counts Client::counts;
+
+Client::Client(const string& hubURL, char separator) :
+ socket(BufferedSocket::getSocket(separator)), reconnDelay(120), registered(false), port(0), countType(COUNT_UNCOUNTED)
+{
+ string file;
+ Util::decodeUrl(hubURL, address, port, file);
+ addressPort = hubURL;
+ socket->addListener(this);
+}
+
+Client::~Client() throw() {
+ socket->removeListener(this);
+
+ updateCounts(true);
+}
+
+void Client::reloadSettings() {
+ FavoriteHubEntry* hub = HubManager::getInstance()->getFavoriteHubEntry(getHubURL());
+ if(hub) {
+ setNick(checkNick(hub->getNick(true)));
+ setDescription(hub->getUserDescription());
+ setPassword(hub->getPassword());
+ } else {
+ setNick(checkNick(SETTING(NICK)));
+ }
+}
+
+void Client::connect() {
+ reloadSettings();
+ socket->connect(address, port);
+}
+
+void Client::updateCounts(bool aRemove) {
+ // We always remove the count and then add the correct one if requested...
+ if(countType == COUNT_NORMAL) {
+ Thread::safeDec(counts.normal);
+ } else if(countType == COUNT_REGISTERED) {
+ Thread::safeDec(counts.registered);
+ } else if(countType == COUNT_OP) {
+ Thread::safeDec(counts.op);
+ }
+ countType = COUNT_UNCOUNTED;
+
+ if(!aRemove) {
+ if(getOp()) {
+ Thread::safeInc(counts.op);
+ countType = COUNT_OP;
+ } else if(registered) {
+ Thread::safeInc(counts.registered);
+ countType = COUNT_REGISTERED;
+ } else {
+ Thread::safeInc(counts.normal);
+ countType = COUNT_NORMAL;
+ }
+ }
+}
+
+string Client::getLocalIp() const {
+ if(!SETTING(SERVER).empty()) {
+ return Socket::resolve(SETTING(SERVER));
+ }
+ if(getMe() && !getMe()->getIp().empty())
+ return getMe()->getIp();
+
+ if(socket == NULL)
+ return Util::getLocalIp();
+ string lip = socket->getLocalIp();
+ if(lip.empty())
+ return Util::getLocalIp();
+ return lip;
+}
+
+/**
+ * @file
+ * $Id: Client.cpp,v 1.2 2005/08/21 14:03:43 olof Exp $
+ */
diff --git a/dcpp/Client.h b/dcpp/Client.h
new file mode 100644
index 0000000..2536b93
--- /dev/null
+++ b/dcpp/Client.h
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2001-2005 Jacek Sieka, arnetheduck on gmail point 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 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.
+ */
+
+#ifndef _CLIENT_H
+#define _CLIENT_H
+
+#if _MSC_VER > 1000
+#pragma once
+#endif
+
+#include "User.h"
+#include "BufferedSocket.h"
+#include "SettingsManager.h"
+
+class Client;
+class AdcCommand;
+
+class ClientListener
+{
+public:
+ template<int I> struct X { enum { TYPE = I }; };
+
+ typedef X<0> Connecting;
+ typedef X<1> Connected;
+ typedef X<2> BadPassword;
+ typedef X<3> UserUpdated;
+ typedef X<4> UsersUpdated;
+ typedef X<5> UserRemoved;
+ typedef X<6> Redirect;
+ typedef X<7> Failed;
+ typedef X<8> GetPassword;
+ typedef X<9> HubUpdated;
+ typedef X<11> Message;
+ typedef X<12> PrivateMessage;
+ typedef X<13> UserCommand;
+ typedef X<14> HubFull;
+ typedef X<15> NickTaken;
+ typedef X<16> SearchFlood;
+ typedef X<17> NmdcSearch;
+ typedef X<18> AdcSearch;
+
+ virtual void on(Connecting, Client*) throw() { }
+ virtual void on(Connected, Client*) throw() { }
+ virtual void on(BadPassword, Client*) throw() { }
+ virtual void on(UserUpdated, Client*, const User::Ptr&) throw() { }
+ virtual void on(UsersUpdated, Client*, const User::List&) throw() { }
+ virtual void on(UserRemoved, Client*, const User::Ptr&) throw() { }
+ virtual void on(Redirect, Client*, const string&) throw() { }
+ virtual void on(Failed, Client*, const string&) throw() { }
+ virtual void on(GetPassword, Client*) throw() { }
+ virtual void on(HubUpdated, Client*) throw() { }
+ virtual void on(Message, Client*, const string&) throw() { }
+ virtual void on(PrivateMessage, Client*, const User::Ptr&, const string&) throw() { }
+ virtual void on(UserCommand, Client*, int, int, const string&, const string&) throw() { }
+ virtual void on(HubFull, Client*) throw() { }
+ virtual void on(NickTaken, Client*) throw() { }
+ virtual void on(SearchFlood, Client*, const string&) throw() { }
+ virtual void on(NmdcSearch, Client*, const string&, int, int64_t, int, const string&) throw() { }
+ virtual void on(AdcSearch, Client*, const AdcCommand&) throw() { }
+};
+
+/** Yes, this should probably be called a Hub */
+class Client : public Speaker<ClientListener>, public BufferedSocketListener {
+public:
+ typedef Client* Ptr;
+ typedef list<Ptr> List;
+ typedef List::iterator Iter;
+
+ Client(const string& hubURL, char separator);
+ virtual ~Client() throw();
+
+ virtual void connect(const User* user) = 0;
+ virtual void hubMessage(const string& aMessage) = 0;
+ virtual void privateMessage(const User* user, const string& aMessage) = 0;
+ virtual void send(const string& aMessage) = 0;
+ virtual void sendUserCmd(const string& aUserCmd) = 0;
+ virtual void search(int aSizeMode, int64_t aSize, int aFileType, const string& aString, const string& aToken) = 0;
+ virtual void password(const string& pwd) = 0;
+ virtual void info(bool alwaysSend) = 0;
+
+ virtual size_t getUserCount() const = 0;
+ virtual int64_t getAvailable() const = 0;
+ virtual const string& getName() const = 0;
+ virtual bool getOp() const = 0;
+
+ virtual User::NickMap& lockUserList() = 0;
+ virtual void unlockUserList() = 0;
+
+ const string& getAddress() const { return address; }
+ const string& getAddressPort() const { return addressPort; }
+ short getPort() const { return port; }
+
+ const string& getIp() const { return socket->getIp().empty() ? getAddress() : socket->getIp(); };
+ string getIpPort() const { return port == 411 ? getIp() : getIp() + ':' + Util::toString(port); };
+ string getLocalIp() const;
+
+ virtual void connect();
+ bool isConnected() const { return socket->isConnected(); }
+ void disconnect() { socket->disconnect(); }
+
+ void updated(User::Ptr& aUser) {
+ fire(ClientListener::UserUpdated(), this, aUser);
+ }
+
+ static string getCounts() {
+ char buf[128];
+ return string(buf, sprintf(buf, "%ld/%ld/%ld", counts.normal, counts.registered, counts.op));
+ }
+
+ const User::Ptr& getMe() const { return me; };
+ User::Ptr& getMe() { return me; }
+ void setMe(const User::Ptr& aMe) { me = aMe; }
+
+ const string& getDescription() const { return description.empty() ? SETTING(DESCRIPTION) : description; };
+ void setDescription(const string& aDesc) { description = aDesc; };
+
+ void scheduleDestruction() const { socket->shutdown(); }
+
+ virtual string escape(string const& str) const { return str; };
+ StringMap& escapeParams(StringMap& sm) {
+ for(StringMapIter i = sm.begin(); i != sm.end(); ++i) {
+ i->second = escape(i->second);
+ }
+ return sm;
+ }
+
+protected:
+ struct Counts {
+ Counts(long n = 0, long r = 0, long o = 0) : normal(n), registered(r), op(o) { };
+ volatile long normal;
+ volatile long registered;
+ volatile long op;
+ bool operator !=(const Counts& rhs) { return normal != rhs.normal || registered != rhs.registered || op != rhs.op; };
+ };
+
+ BufferedSocket* socket;
+
+ User::Ptr me;
+ static Counts counts;
+ Counts lastCounts;
+
+ void updateCounts(bool aRemove);
+
+ void setPort(short aPort) { port = aPort; }
+
+ // reload nick from settings, other details from hubmanager
+ void reloadSettings();
+
+ virtual string checkNick(const string& nick) = 0;
+ virtual string getHubURL() = 0;
+
+ GETSET(string, nick, Nick);
+ GETSET(string, defpassword, Password);
+ GETSET(u_int32_t, reconnDelay, ReconnDelay);
+ GETSET(bool, registered, Registered);
+private:
+
+ enum CountType {
+ COUNT_UNCOUNTED,
+ COUNT_NORMAL,
+ COUNT_REGISTERED,
+ COUNT_OP
+ };
+
+ Client(const Client&);
+ Client& operator=(const Client&);
+
+ string description;
+
+ string address;
+ string addressPort;
+ u_int16_t port;
+
+ CountType countType;
+
+ // BufferedSocketListener
+ virtual void on(BufferedSocketListener::Shutdown) throw() {
+ removeListeners();
+ delete this;
+ }
+};
+
+#endif // _CLIENT_H
+/**
+ * @file
+ * $Id: Client.h,v 1.2 2005/08/21 14:03:43 olof Exp $
+ */
diff --git a/dcpp/ClientManager.cpp b/dcpp/ClientManager.cpp
new file mode 100644
index 0000000..8221fd7
--- /dev/null
+++ b/dcpp/ClientManager.cpp
@@ -0,0 +1,360 @@
+/*
+ * Copyright (C) 2001-2005 Jacek Sieka, arnetheduck on gmail point 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 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.
+ */
+
+#include "stdinc.h"
+#include "DCPlusPlus.h"
+
+#include "ClientManager.h"
+
+#include "ShareManager.h"
+#include "SearchManager.h"
+#include "CryptoManager.h"
+#include "ConnectionManager.h"
+#include "HubManager.h"
+
+#include "AdcHub.h"
+#include "NmdcHub.h"
+
+Client* ClientManager::getClient(const string& aHubURL) {
+ Client* c;
+ if(Util::strnicmp("adc://", aHubURL.c_str(), 6) == 0) {
+ c = new AdcHub(aHubURL);
+ } else {
+ c = new NmdcHub(aHubURL);
+ }
+
+ {
+ Lock l(cs);
+ clients.push_back(c);
+ }
+
+ c->addListener(this);
+ return c;
+}
+
+void ClientManager::putClient(Client* aClient) {
+ aClient->disconnect();
+ fire(ClientManagerListener::ClientDisconnected(), aClient);
+ aClient->removeListeners();
+
+ {
+ Lock l(cs);
+
+ // Either I'm stupid or the msvc7 optimizer is doing something _very_ strange here...
+ // STL-port -D_STL_DEBUG complains that .begin() and .end() don't have the same owner (!)
+ // dcassert(find(clients.begin(), clients.end(), aClient) != clients.end());
+ // clients.erase(find(clients.begin(), clients.end(), aClient));
+
+ for(Client::Iter i = clients.begin(); i != clients.end(); ++i) {
+ if(*i == aClient) {
+ clients.erase(i);
+ break;
+ }
+ }
+ }
+ aClient->scheduleDestruction();
+}
+
+void ClientManager::infoUpdated() {
+ Lock l(cs);
+ for(Client::Iter i = clients.begin(); i != clients.end(); ++i) {
+ if((*i)->isConnected()) {
+ (*i)->info(false);
+ }
+ }
+}
+
+void ClientManager::on(NmdcSearch, Client* aClient, const string& aSeeker, int aSearchType, int64_t aSize,
+ int aFileType, const string& aString) throw()
+{
+ Speaker<ClientManagerListener>::fire(ClientManagerListener::IncomingSearch(), aString);
+
+ bool isPassive = (aSeeker.compare(0, 4, "Hub:") == 0);
+
+ // We don't wan't to answer passive searches if we're in passive mode...
+ if(isPassive && SETTING(CONNECTION_TYPE) != SettingsManager::CONNECTION_ACTIVE) {
+ return;
+ }
+
+ SearchResult::List l;
+ ShareManager::getInstance()->search(l, aString, aSearchType, aSize, aFileType, aClient, isPassive ? 5 : 10);
+// dcdebug("Found %d items (%s)\n", l.size(), aString.c_str());
+ if(l.size() > 0) {
+ if(isPassive) {
+ string name = aSeeker.substr(4);
+ // Good, we have a passive seeker, those are easier...
+ string str;
+ for(SearchResult::Iter i = l.begin(); i != l.end(); ++i) {
+ SearchResult* sr = *i;
+ str += sr->toSR();
+ str[str.length()-1] = 5;
+ str += name;
+ str += '|';
+
+ sr->decRef();
+ }
+
+ if(str.size() > 0)
+ aClient->send(str);
+
+ } else {
+ try {
+ string ip, file;
+ u_int16_t port = 0;
+ Util::decodeUrl(aSeeker, ip, port, file);
+ ip = Socket::resolve(ip);
+ if(port == 0) port = 412;
+ for(SearchResult::Iter i = l.begin(); i != l.end(); ++i) {
+ SearchResult* sr = *i;
+ s.writeTo(ip, port, sr->toSR());
+ sr->decRef();
+ }
+ } catch(const SocketException& /* e */) {
+ dcdebug("Search caught error\n");
+ }
+ }
+ }
+}
+
+void ClientManager::on(AdcSearch, Client*, const AdcCommand& adc) throw() {
+ SearchManager::getInstance()->respond(adc);
+}
+
+User::Ptr ClientManager::getUser(const string& aNick, const string& aHint /* = Util::emptyString */) {
+ Lock l(cs);
+ dcassert(aNick.size() > 0);
+ UserPair p = users.equal_range(aNick);
+
+ if(p.first == p.second) {
+ User::Ptr& u = users.insert(make_pair(aNick, new User(aNick)))->second;
+ u->setLastHubAddress(aHint);
+ return u;
+ }
+
+ UserIter i;
+ if(aHint.empty()) {
+ // No hint, first, try finding an online user...
+ for(i = p.first; i != p.second; ++i) {
+ if(i->second->isOnline()) {
+ return i->second;
+ }
+ }
+ // Blah...return the first one...doesn't matter now...
+ return p.first->second;
+ }
+
+ // Since we have a hint, make sure we use it...
+ for(i = p.first; i != p.second; ++i) {
+ if(i->second->getLastHubAddress() == aHint) {
+ return i->second;
+ }
+ }
+ // Since old dc++'s didn't return port in $SR's we'll check for port-less hints as well
+ string::size_type k = aHint.find(':');
+ if(k != string::npos) {
+ string hint = aHint.substr(0, k);
+ for(i = p.first; i != p.second; ++i) {
+ if(i->second->getLastHubAddress() == hint) {
+ return i->second;
+ }
+ }
+ }
+
+ // Try to find an online user, higher probablility that it's one of these...
+ for(i = p.first; i != p.second; ++i) {
+ if(i->second->isOnline()) {
+ return i->second;
+ }
+ }
+
+ return users.insert(make_pair(aNick, new User(aNick)))->second;
+}
+
+User::Ptr ClientManager::getUser(const string& aNick, Client* aClient, bool putOnline /* = true */) {
+ Lock l(cs);
+ dcassert(aNick.size() > 0);
+ dcassert(aClient != NULL);
+ dcassert(find(clients.begin(), clients.end(), aClient) != clients.end());
+
+ UserPair p = users.equal_range(aNick);
+ UserIter i;
+
+ // Check for a user already online
+ for(i = p.first; i != p.second; ++i) {
+ if(i->second->isClient(aClient)) {
+ return i->second;
+ }
+ }
+
+ // Check for an offline user that was on that hub that we can put online again
+ for(i = p.first; i != p.second; ++i) {
+ if( (!i->second->isOnline()) &&
+ ((i->second->getLastHubAddress() == aClient->getAddressPort()) || (i->second->getLastHubAddress() == aClient->getIpPort())) )
+ {
+ if(putOnline) {
+ i->second->setClient(aClient);
+ fire(ClientManagerListener::UserUpdated(), i->second);
+ }
+ return i->second;
+ }
+ }
+
+ // Check for any offline user
+ for(i = p.first; i != p.second; ++i) {
+ if( (!i->second->isOnline()) ) {
+ if(putOnline) {
+ i->second->setClient(aClient);
+ fire(ClientManagerListener::UserUpdated(), i->second);
+ }
+ return i->second;
+ }
+ }
+
+ // Create a new user
+ i = users.insert(make_pair(aNick, new User(aNick)));
+ if(putOnline) {
+ i->second->setClient(aClient);
+ fire(ClientManagerListener::UserUpdated(), i->second);
+ }
+ return i->second;
+}
+
+void ClientManager::search(int aSizeMode, int64_t aSize, int aFileType, const string& aString, const string& aToken) {
+ Lock l(cs);
+
+ for(Client::Iter i = clients.begin(); i != clients.end(); ++i) {
+ if((*i)->isConnected()) {
+ (*i)->search(aSizeMode, aSize, aFileType, aString, aToken);
+ }
+ }
+}
+
+void ClientManager::search(StringList& who, int aSizeMode, int64_t aSize, int aFileType, const string& aString, const string& aToken) {
+ Lock l(cs);
+
+ for(StringIter it = who.begin(); it != who.end(); ++it) {
+ string& client = *it;
+ for(Client::Iter j = clients.begin(); j != clients.end(); ++j) {
+ Client* c = *j;
+ if(c->isConnected() && c->getIpPort() == client) {
+ c->search(aSizeMode, aSize, aFileType, aString, aToken);
+ }
+ }
+ }
+}
+
+void ClientManager::putUserOffline(User::Ptr& aUser, bool quitHub /*= false*/) {
+ {
+ Lock l(cs);
+ aUser->setIp(Util::emptyString);
+ aUser->unsetFlag(User::PASSIVE);
+ aUser->unsetFlag(User::OP);
+ aUser->unsetFlag(User::DCPLUSPLUS);
+ if(quitHub)
+ aUser->setFlag(User::QUIT_HUB);
+ aUser->setClient(NULL);
+ }
+ fire(ClientManagerListener::UserUpdated(), aUser);
+}
+
+User::Ptr ClientManager::getUser(const CID& cid, bool createUser) {
+ Lock l(cs);
+ dcassert(!cid.isZero());
+ AdcIter i = adcUsers.find(cid);
+ if(i != adcUsers.end())
+ return i->second;
+
+ return createUser ? adcUsers.insert(make_pair(cid, new User(cid)))->second : User::Ptr();
+}
+
+User::Ptr ClientManager::getUser(const CID& cid, Client* aClient, bool putOnline /* = true */) {
+ Lock l(cs);
+ dcassert(!cid.isZero());
+ dcassert(aClient != NULL && find(clients.begin(), clients.end(), aClient) != clients.end());
+
+ AdcPair p = adcUsers.equal_range(cid);
+ for(AdcIter i = p.first; i != p.second; ++i) {
+ User::Ptr& u = i->second;
+ if(u->isClient(aClient))
+ return u;
+ }
+
+ if(putOnline) {
+ User::Ptr up;
+ for(AdcIter i = p.first; i != p.second; ++i) {
+ if(!i->second->isOnline()) {
+ up = i->second;
+ }
+ }
+ if(!up)
+ up = adcUsers.insert(make_pair(cid, new User(cid)))->second;
+
+ up->setClient(aClient);
+ fire(ClientManagerListener::UserUpdated(), up);
+ return up;
+ }
+
+ // Create a new user
+ return adcUsers.insert(make_pair(cid, new User(cid)))->second;
+}
+
+void ClientManager::on(TimerManagerListener::Minute, u_int32_t /* aTick */) throw() {
+ Lock l(cs);
+
+ // Collect some garbage...
+ UserIter i = users.begin();
+ while(i != users.end()) {
+ if(i->second->unique()) {
+ users.erase(i++);
+ } else {
+ ++i;
+ }
+ }
+ AdcIter k = adcUsers.begin();
+ while(k != adcUsers.end()) {
+ if(k->second->unique()) {
+ adcUsers.erase(k++);
+ } else {
+ ++k;
+ }
+ }
+
+ for(Client::Iter j = clients.begin(); j != clients.end(); ++j) {
+ (*j)->info(false);
+ }
+}
+
+void ClientManager::on(Failed, Client* client, const string&) throw() {
+ HubManager::getInstance()->removeUserCommand(client->getAddressPort());
+ fire(ClientManagerListener::ClientDisconnected(), client);
+}
+
+void ClientManager::on(UserCommand, Client* client, int aType, int ctx, const string& name, const string& command) throw() {
+ if(BOOLSETTING(HUB_USER_COMMANDS)) {
+ if(aType == ::UserCommand::TYPE_CLEAR) {
+ HubManager::getInstance()->removeHubUserCommands(ctx, client->getAddressPort());
+ } else {
+ HubManager::getInstance()->addUserCommand(aType, ctx, ::UserCommand::FLAG_NOSAVE, name, command, client->getAddressPort());
+ }
+ }
+}
+
+/**
+ * @file
+ * $Id: ClientManager.cpp,v 1.2 2005/08/21 14:03:43 olof Exp $
+ */
diff --git a/dcpp/ClientManager.h b/dcpp/ClientManager.h
new file mode 100644
index 0000000..74af818
--- /dev/null
+++ b/dcpp/ClientManager.h
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2001-2005 Jacek Sieka, arnetheduck on gmail point 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 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.
+ */
+
+#if !defined(AFX_CLIENTMANAGER_H__8EF173E1_F7DC_40B5_B2F3_F92297701034__INCLUDED_)
+#define AFX_CLIENTMANAGER_H__8EF173E1_F7DC_40B5_B2F3_F92297701034__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+#include "TimerManager.h"
+
+#include "Client.h"
+#include "Singleton.h"
+
+#include "ClientManagerListener.h"
+
+class ClientManager : public Speaker<ClientManagerListener>,
+ private ClientListener, public Singleton<ClientManager>,
+ private TimerManagerListener
+{
+public:
+ Client* getClient(const string& aHubURL);
+ void putClient(Client* aClient);
+
+ size_t getUserCount() {
+ Lock l(cs);
+
+ size_t c = 0;
+ for(Client::Iter i = clients.begin(); i != clients.end(); ++i) {
+ c+=(*i)->getUserCount();
+ }
+ return c;
+ }
+
+ int64_t getAvailable() {
+ Lock l(cs);
+
+ int64_t c = 0;
+ for(Client::Iter i = clients.begin(); i != clients.end(); ++i) {
+ c+=(*i)->getAvailable();
+ }
+ return c;
+ }
+
+ bool isConnected(const string& aAddress, short port) {
+ Lock l(cs);
+
+ for(Client::Iter i = clients.begin(); i != clients.end(); ++i) {
+ if(((*i)->getAddress() == aAddress || (*i)->getIp() == aAddress) && (*i)->getPort() == port) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ void search(int aSizeMode, int64_t aSize, int aFileType, const string& aString, const string& aToken);
+ void search(StringList& who, int aSizeMode, int64_t aSize, int aFileType, const string& aString, const string& aToken);
+ void infoUpdated();
+
+ User::Ptr getUser(const CID& cid, bool createUser);
+ User::Ptr getUser(const CID& cid, Client* aClient, bool putOnline = true);
+ User::Ptr getUser(const string& aNick, const string& aHint = Util::emptyString);
+ User::Ptr getUser(const string& aNick, Client* aClient, bool putOnline = true);
+
+ bool isOnline(const string& aNick) {
+ Lock l(cs);
+ UserPair i = users.equal_range(aNick);
+ for(UserIter j = i.first; j != i.second; ++j) {
+ if(j->second->isOnline())
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * A user went offline. Must be called whenever a user quits a hub.
+ * @param quitHub The user went offline because (s)he disconnected from the hub.
+ */
+ void putUserOffline(User::Ptr& aUser, bool quitHub = false);
+
+ void lock() throw() { cs.enter(); }
+ void unlock() throw() { cs.leave(); }
+
+ Client::List& getClients() { return clients; }
+
+ void removeClientListener(ClientListener* listener) {
+ Lock l(cs);
+ Client::Iter endIt = clients.end();
+ for(Client::Iter it = clients.begin(); it != endIt; ++it) {
+ Client* client = *it;
+ client->removeListener(listener);
+ }
+ }
+
+private:
+ typedef HASH_MULTIMAP_X(string, User::Ptr, noCaseStringHash, noCaseStringEq, noCaseStringLess) UserMap;
+ typedef UserMap::iterator UserIter;
+ typedef pair<UserIter, UserIter> UserPair;
+
+ typedef HASH_MULTIMAP_X(CID, User::Ptr, CID::Hash, equal_to<CID>, less<CID>) AdcMap;
+ typedef AdcMap::iterator AdcIter;
+ typedef pair<AdcIter, AdcIter> AdcPair;
+
+ Client::List clients;
+ CriticalSection cs;
+
+ UserMap users;
+ AdcMap adcUsers;
+
+ Socket s;
+
+ friend class Singleton<ClientManager>;
+ ClientManager() {
+ TimerManager::getInstance()->addListener(this);
+ };
+
+ virtual ~ClientManager() throw() { TimerManager::getInstance()->removeListener(this); };
+
+ // ClientListener
+ virtual void on(Connected, Client* c) throw() { fire(ClientManagerListener::ClientConnected(), c); }
+ virtual void on(UsersUpdated, Client* c, const User::List&) throw() { fire(ClientManagerListener::ClientUpdated(), c); }
+ virtual void on(Failed, Client*, const string&) throw();
+ virtual void on(HubUpdated, Client* c) throw() { fire(ClientManagerListener::ClientUpdated(), c); }
+ virtual void on(UserCommand, Client*, int, int, const string&, const string&) throw();
+ virtual void on(NmdcSearch, Client* aClient, const string& aSeeker, int aSearchType, int64_t aSize,
+ int aFileType, const string& aString) throw();
+ virtual void on(AdcSearch, Client* c, const AdcCommand& adc) throw();
+ // TimerManagerListener
+ virtual void on(TimerManagerListener::Minute, u_int32_t aTick) throw();
+};
+
+#endif // !defined(AFX_CLIENTMANAGER_H__8EF173E1_F7DC_40B5_B2F3_F92297701034__INCLUDED_)
+
+/**
+ * @file
+ * $Id: ClientManager.h,v 1.2 2005/08/21 14:03:43 olof Exp $
+ */
+
diff --git a/dcpp/ClientManagerListener.h b/dcpp/ClientManagerListener.h
new file mode 100644
index 0000000..55b7fd7
--- /dev/null
+++ b/dcpp/ClientManagerListener.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2001-2005 Jacek Sieka, arnetheduck on gmail point 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 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.
+ */
+
+#if !defined(AFX_CLIENTMANAGERLISTENER_H__8EF173E1_F7DC_40B5_B2F3_F92297701034__INCLUDED_)
+#define AFX_CLIENTMANAGERLISTENER_H__8EF173E1_F7DC_40B5_B2F3_F92297701034__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+class ClientManagerListener {
+public:
+ template<int I> struct X { enum { TYPE = I }; };
+
+ typedef X<0> UserUpdated;
+ typedef X<1> IncomingSearch;
+ typedef X<2> ClientConnected;
+ typedef X<3> ClientUpdated;
+ typedef X<4> ClientDisconnected;
+
+ virtual void on(UserUpdated, const User::Ptr&) throw() { }
+ virtual void on(IncomingSearch, const string&) throw() { }
+ virtual void on(ClientConnected, Client*) throw() { }
+ virtual void on(ClientUpdated, Client*) throw() { }
+ virtual void on(ClientDisconnected, Client*) throw() { }
+};
+
+#endif
+
+/**
+ * @file
+ * $Id: ClientManagerListener.h,v 1.2 2005/08/21 14:03:43 olof Exp $
+ */
diff --git a/dcpp/ConnectionManager.cpp b/dcpp/ConnectionManager.cpp
new file mode 100644
index 0000000..2fd9e8d
--- /dev/null
+++ b/dcpp/ConnectionManager.cpp
@@ -0,0 +1,695 @@
+/*
+ * Copyright (C) 2001-2005 Jacek Sieka, arnetheduck on gmail point 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 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.
+ */
+
+#include "stdinc.h"
+#include "DCPlusPlus.h"
+
+#include "ConnectionManager.h"
+
+#include "ResourceManager.h"
+#include "DownloadManager.h"
+#include "UploadManager.h"
+#include "CryptoManager.h"
+#include "ClientManager.h"
+#include "QueueManager.h"
+
+#include "UserConnection.h"
+
+ConnectionManager::ConnectionManager() : port(0), floodCounter(0), shuttingDown(false) {
+ TimerManager::getInstance()->addListener(this);
+ socket.addListener(this);
+
+ features.push_back(UserConnection::FEATURE_MINISLOTS);
+ features.push_back(UserConnection::FEATURE_XML_BZLIST);
+ features.push_back(UserConnection::FEATURE_ADCGET);
+ features.push_back(UserConnection::FEATURE_TTHL);
+ features.push_back(UserConnection::FEATURE_TTHF);
+
+ adcFeatures.push_back("+BASE");
+}
+
+/**
+ * Request a connection for downloading.
+ * DownloadManager::addConnection will be called as soon as the connection is ready
+ * for downloading.
+ * @param aUser The user to connect to.
+ */
+void ConnectionManager::getDownloadConnection(const User::Ptr& aUser) {
+ dcassert((bool)aUser);
+ {
+ Lock l(cs);
+ ConnectionQueueItem::Iter i = find(downloads.begin(), downloads.end(), aUser);
+ if(i == downloads.end()) {
+ getCQI(aUser, true);
+ } else {
+ ConnectionQueueItem* cqi = *i;
+ if(cqi->getState() == ConnectionQueueItem::IDLE) {
+ if(find(pendingAdd.begin(), pendingAdd.end(), aUser) == pendingAdd.end())
+ pendingAdd.push_back(aUser);
+ return;
+ }
+ }
+ }
+}
+
+ConnectionQueueItem* ConnectionManager::getCQI(const User::Ptr& aUser, bool download) {
+ ConnectionQueueItem* cqi = new ConnectionQueueItem(aUser, download);
+ if(download) {
+ dcassert(find(downloads.begin(), downloads.end(), aUser) == downloads.end());
+ downloads.push_back(cqi);
+
+ } else {
+ dcassert(find(uploads.begin(), uploads.end(), aUser) == uploads.end());
+ uploads.push_back(cqi);
+ }
+
+ fire(ConnectionManagerListener::Added(), cqi);
+
+ return cqi;
+}
+
+void ConnectionManager::putCQI(ConnectionQueueItem* cqi) {
+ fire(ConnectionManagerListener::Removed(), cqi);
+ if(cqi->getDownload()) {
+ dcassert(find(downloads.begin(), downloads.end(), cqi) != downloads.end());
+ downloads.erase(find(downloads.begin(), downloads.end(), cqi));
+ } else {
+ dcassert(find(uploads.begin(), uploads.end(), cqi) != uploads.end());
+ uploads.erase(find(uploads.begin(), uploads.end(), cqi));
+ }
+ delete cqi;
+}
+
+void ConnectionManager::putDownloadConnection(UserConnection* aSource, bool reuse /* = false */, bool ntd /* = false */) {
+ ConnectionQueueItem* cqi = aSource->getCQI();
+ dcassert(cqi);
+
+ if(reuse) {
+ dcdebug("ConnectionManager::putDownloadConnection Pooling reusable connection %p to %s\n", aSource, aSource->getUser()->getNick().c_str());
+ // Pool it for later usage...
+ aSource->addListener(this);
+ {
+ Lock l(cs);
+ cqi->setState(ConnectionQueueItem::IDLE);
+ }
+ } else {
+ // Disassociate the two...
+ aSource->setCQI(NULL);
+
+ bool hasDown = QueueManager::getInstance()->hasDownload(aSource->getUser());
+
+ {
+ Lock l(cs);
+ cqi->setConnection(NULL);
+ if(hasDown) {
+ cqi->setLastAttempt(GET_TICK());
+ cqi->setState(ConnectionQueueItem::WAITING);
+ } else {
+ putCQI(cqi);
+ }
+ }
+
+ if(ntd) {
+ aSource->unsetFlag(UserConnection::FLAG_DOWNLOAD);
+ addUploadConnection(aSource);
+ } else {
+ putConnection(aSource);
+ }
+ }
+}
+
+void ConnectionManager::putUploadConnection(UserConnection* aSource, bool ntd) {
+ ConnectionQueueItem* cqi = aSource->getCQI();
+ aSource->setCQI(NULL);
+
+ if(ntd) {
+ // We should pass it to the download manager...
+ aSource->unsetFlag(UserConnection::FLAG_UPLOAD);
+ aSource->setFlag(UserConnection::FLAG_DOWNLOAD);
+ addDownloadConnection(aSource, false);
+ } else {
+ putConnection(aSource);
+ }
+
+ {
+ Lock l(cs);
+ putCQI(cqi);
+ }
+}
+
+UserConnection* ConnectionManager::getConnection(bool aNmdc) throw(SocketException) {
+ UserConnection* uc = new UserConnection();
+ uc->addListener(this);
+ {
+ Lock l(cs);
+ userConnections.push_back(uc);
+ }
+ if(aNmdc)
+ uc->setFlag(UserConnection::FLAG_NMDC);
+ return uc;
+}
+
+void ConnectionManager::putConnection(UserConnection* aConn) {
+ aConn->removeListeners();
+ aConn->disconnect();
+ {
+ Lock l(cs);
+
+ dcassert(find(userConnections.begin(), userConnections.end(), aConn) != userConnections.end());
+ userConnections.erase(find(userConnections.begin(), userConnections.end(), aConn));
+ pendingDelete.push_back(aConn);
+ }
+}
+
+void ConnectionManager::on(TimerManagerListener::Second, u_int32_t aTick) throw() {
+ User::List passiveUsers;
+ ConnectionQueueItem::List removed;
+ UserConnection::List added;
+ UserConnection::List penDel;
+
+ bool tooMany = ((SETTING(DOWNLOAD_SLOTS) != 0) && DownloadManager::getInstance()->getDownloadCount() >= (size_t)SETTING(DOWNLOAD_SLOTS));
+ bool tooFast = ((SETTING(MAX_DOWNLOAD_SPEED) != 0 && DownloadManager::getInstance()->getAverageSpeed() >= (SETTING(MAX_DOWNLOAD_SPEED)*1024)));
+
+ {
+ Lock l(cs);
+
+ int attempts = 0;
+
+ for(ConnectionQueueItem::Iter i = downloads.begin(); i != downloads.end(); ++i) {
+ ConnectionQueueItem* cqi = *i;
+
+ if(cqi->getState() == ConnectionQueueItem::ACTIVE) {
+ // Do nothing
+ } else if(cqi->getState() == ConnectionQueueItem::IDLE) {
+ User::Iter it = find(pendingAdd.begin(), pendingAdd.end(), cqi->getUser());
+ if(it != pendingAdd.end()) {
+ dcassert(cqi->getConnection());
+ dcassert(cqi->getConnection()->getCQI() == cqi);
+ cqi->setState(ConnectionQueueItem::ACTIVE);
+ cqi->getConnection()->removeListener(this);
+ added.push_back(cqi->getConnection());
+
+ pendingAdd.erase(it);
+ }
+ } else {
+
+ if(!cqi->getUser()->isOnline()) {
+ // Not online anymore...remove it from the pending...
+ removed.push_back(cqi);
+ continue;
+ }
+
+ if(cqi->getUser()->isSet(User::PASSIVE) && (SETTING(CONNECTION_TYPE) != SettingsManager::CONNECTION_ACTIVE)) {
+ passiveUsers.push_back(cqi->getUser());
+ removed.push_back(cqi);
+ continue;
+ }
+
+ if( ((cqi->getLastAttempt() + 60*1000) < aTick) && (attempts < 2) ) {
+ cqi->setLastAttempt(aTick);
+
+ if(!QueueManager::getInstance()->hasDownload(cqi->getUser())) {
+ removed.push_back(cqi);
+ continue;
+ }
+
+ // Always start high-priority downloads unless we have 3 more than maxdownslots already...
+ bool startDown = !tooMany && !tooFast;
+
+ if(!startDown) {
+ bool extraFull = (SETTING(DOWNLOAD_SLOTS) != 0) && (DownloadManager::getInstance()->getDownloadCount() >= (size_t)(SETTING(DOWNLOAD_SLOTS)+3));
+ startDown = !extraFull && QueueManager::getInstance()->hasDownload(cqi->getUser(), QueueItem::HIGHEST);
+ }
+
+ if(cqi->getState() == ConnectionQueueItem::WAITING) {
+ if(startDown) {
+ cqi->setState(ConnectionQueueItem::CONNECTING);
+ cqi->getUser()->connect();
+ fire(ConnectionManagerListener::StatusChanged(), cqi);
+ attempts++;
+ } else {
+ cqi->setState(ConnectionQueueItem::NO_DOWNLOAD_SLOTS);
+ fire(ConnectionManagerListener::Failed(), cqi, STRING(ALL_DOWNLOAD_SLOTS_TAKEN));
+ }
+ } else if(cqi->getState() == ConnectionQueueItem::NO_DOWNLOAD_SLOTS && startDown) {
+ cqi->setState(ConnectionQueueItem::WAITING);
+ }
+ } else if(((cqi->getLastAttempt() + 50*1000) < aTick) && (cqi->getState() == ConnectionQueueItem::CONNECTING)) {
+ fire(ConnectionManagerListener::Failed(), cqi, STRING(CONNECTION_TIMEOUT));
+ cqi->setState(ConnectionQueueItem::WAITING);
+ }
+ }
+ }
+
+ pendingAdd.clear();
+
+ for(ConnectionQueueItem::Iter m = removed.begin(); m != removed.end(); ++m) {
+ putCQI(*m);
+ }
+
+ penDel = pendingDelete;
+ pendingDelete.clear();
+
+ }
+
+ for_each(penDel.begin(), penDel.end(), DeleteFunction<UserConnection*>());
+
+ for(User::Iter ui = passiveUsers.begin(); ui != passiveUsers.end(); ++ui) {
+ QueueManager::getInstance()->removeSources(*ui, QueueItem::Source::FLAG_PASSIVE);
+ }
+
+ for(UserConnection::Iter i = added.begin(); i != added.end(); ++i) {
+ DownloadManager::getInstance()->addConnection(*i);
+ }
+}
+
+void ConnectionManager::on(TimerManagerListener::Minute, u_int32_t aTick) throw() {
+ Lock l(cs);
+
+ for(UserConnection::Iter j = userConnections.begin(); j != userConnections.end(); ++j) {
+ if(((*j)->getLastActivity() + 180*1000) < aTick) {
+ (*j)->disconnect();
+ }
+ }
+}
+
+static const u_int32_t FLOOD_TRIGGER = 20000;
+static const u_int32_t FLOOD_ADD = 2000;
+
+/**
+ * Someone's connecting, accept the connection and wait for identification...
+ * It's always the other fellow that starts sending if he made the connection.
+ */
+void ConnectionManager::on(ServerSocketListener::IncomingConnection) throw() {
+ UserConnection* uc = NULL;
+ u_int32_t now = GET_TICK();
+
+ if(now > floodCounter) {
+ floodCounter = now + FLOOD_ADD;
+ } else {
+ if(now + FLOOD_TRIGGER < floodCounter) {
+ Socket s;
+ try {
+ s.accept(socket);
+ } catch(const SocketException&) {
+ // ...
+ }
+ dcdebug("Connection flood detected!\n");
+ return;
+ } else {
+ floodCounter += FLOOD_ADD;
+ }
+ }
+
+ try {
+ uc = getConnection(false);
+ uc->setFlag(UserConnection::FLAG_INCOMING);
+ uc->setState(UserConnection::STATE_SUPNICK);
+ uc->setLastActivity(GET_TICK());
+ uc->accept(socket);
+ } catch(const SocketException& e) {
+ dcdebug("ConnectionManager::OnIncomingConnection caught: %s\n", e.getError().c_str());
+ if(uc)
+ putConnection(uc);
+ }
+}
+
+void ConnectionManager::nmdcConnect(const string& aServer, short aPort, const string& aNick) {
+ if(shuttingDown)
+ return;
+
+ UserConnection* uc = NULL;
+ try {
+ uc = getConnection(true);
+ uc->setNick(aNick);
+ uc->setState(UserConnection::STATE_CONNECT);
+ uc->setFlag(UserConnection::FLAG_NMDC);
+ uc->connect(aServer, aPort);
+ } catch(const SocketException&) {
+ if(uc)
+ putConnection(uc);
+ }
+}
+
+void ConnectionManager::adcConnect(const string& aServer, short aPort, const string& aToken) {
+ if(shuttingDown)
+ return;
+
+ UserConnection* uc = NULL;
+ try {
+ uc = getConnection(false);
+ uc->setToken(aToken);
+ uc->setState(UserConnection::STATE_CONNECT);
+ uc->connect(aServer, aPort);
+ } catch(const SocketException&) {
+ if(uc)
+ putConnection(uc);
+ }
+}
+
+void ConnectionManager::on(AdcCommand::SUP, UserConnection* aSource, const AdcCommand&) throw() {
+ if(aSource->getState() != UserConnection::STATE_SUPNICK) {
+ // Already got this once, ignore...
+ dcdebug("CM::onMyNick %p sent nick twice\n", aSource);
+ return;
+ }
+
+ if(aSource->isSet(UserConnection::FLAG_INCOMING)) {
+ aSource->sup(adcFeatures);
+ aSource->inf(false);
+ } else {
+ aSource->inf(true);
+ }
+ aSource->setState(UserConnection::STATE_INF);
+}
+
+void ConnectionManager::on(AdcCommand::NTD, UserConnection*, const AdcCommand&) throw() {
+
+}
+
+void ConnectionManager::on(AdcCommand::STA, UserConnection*, const AdcCommand&) throw() {
+
+}
+
+void ConnectionManager::on(UserConnectionListener::Connected, UserConnection* aSource) throw() {
+ dcassert(aSource->getState() == UserConnection::STATE_CONNECT);
+ if(aSource->isSet(UserConnection::FLAG_NMDC)) {
+ aSource->myNick(aSource->getNick());
+ aSource->lock(CryptoManager::getInstance()->getLock(), CryptoManager::getInstance()->getPk());
+ } else {
+ aSource->sup(adcFeatures);
+ }
+ aSource->setState(UserConnection::STATE_SUPNICK);
+}
+
+/**
+ * Nick received. If it's a downloader, fine, otherwise it must be an uploader.
+ */
+void ConnectionManager::on(UserConnectionListener::MyNick, UserConnection* aSource, const string& aNick) throw() {
+
+ if(aSource->getState() != UserConnection::STATE_SUPNICK) {
+ // Already got this once, ignore...
+ dcdebug("CM::onMyNick %p sent nick twice\n", aSource);
+ return;
+ }
+
+ dcassert(aNick.size() > 0);
+ dcdebug("ConnectionManager::onMyNick %p, %s\n", aSource, aNick.c_str());
+ dcassert(!aSource->getUser());
+
+ // First, we try looking in the pending downloads...hopefully it's one of them...
+ {
+ Lock l(cs);
+ for(ConnectionQueueItem::Iter i = downloads.begin(); i != downloads.end(); ++i) {
+ ConnectionQueueItem* cqi = *i;
+ if((cqi->getState() == ConnectionQueueItem::CONNECTING || cqi->getState() == ConnectionQueueItem::WAITING) && cqi->getUser()->getNick() == aNick) {
+ aSource->setUser(cqi->getUser());
+ // Indicate that we're interested in this file...
+ aSource->setFlag(UserConnection::FLAG_DOWNLOAD);
+ break;
+ }
+ }
+ }
+
+ if(!aSource->getUser()) {
+ // Make sure we know who it is, i e that he/she is connected...
+ if(!ClientManager::getInstance()->isOnline(aNick)) {
+ dcdebug("CM::onMyNick Incoming connection from unknown user %s\n", aNick.c_str());
+ putConnection(aSource);
+ return;
+ }
+
+ aSource->setUser(ClientManager::getInstance()->getUser(aNick));
+ // We don't need this connection for downloading...make it an upload connection instead...
+ aSource->setFlag(UserConnection::FLAG_UPLOAD);
+ }
+
+ if( aSource->isSet(UserConnection::FLAG_INCOMING) ) {
+ aSource->myNick(aSource->getUser()->getClientNick());
+ aSource->lock(CryptoManager::getInstance()->getLock(), CryptoManager::getInstance()->getPk());
+ }
+
+ aSource->setState(UserConnection::STATE_LOCK);
+}
+
+void ConnectionManager::on(UserConnectionListener::CLock, UserConnection* aSource, const string& aLock, const string& aPk) throw() {
+ if(aSource->getState() != UserConnection::STATE_LOCK) {
+ dcdebug("CM::onLock %p received lock twice, ignoring\n", aSource);
+ return;
+ }
+
+ if( CryptoManager::getInstance()->isExtended(aLock) ) {
+ // Alright, we have an extended protocol, set a user flag for this user and refresh his info...
+ if( (aPk.find("DCPLUSPLUS") != string::npos) && aSource->getUser() && !aSource->getUser()->isSet(User::DCPLUSPLUS)) {
+ aSource->getUser()->setFlag(User::DCPLUSPLUS);
+ User::updated(aSource->getUser());
+ }
+ StringList defFeatures = features;
+ if(BOOLSETTING(COMPRESS_TRANSFERS)) {
+ defFeatures.push_back(UserConnection::FEATURE_GET_ZBLOCK);
+ defFeatures.push_back(UserConnection::FEATURE_ZLIB_GET);
+ }
+
+ aSource->supports(defFeatures);
+ }
+
+ aSource->setState(UserConnection::STATE_DIRECTION);
+ aSource->direction(aSource->getDirectionString(), aSource->getNumber());
+ aSource->key(CryptoManager::getInstance()->makeKey(aLock));
+}
+
+void ConnectionManager::on(UserConnectionListener::Direction, UserConnection* aSource, const string& dir, const string& num) throw() {
+ if(aSource->getState() != UserConnection::STATE_DIRECTION) {
+ dcdebug("CM::onDirection %p received direction twice, ignoring\n", aSource);
+ return;
+ }
+
+ dcassert(aSource->isSet(UserConnection::FLAG_DOWNLOAD) ^ aSource->isSet(UserConnection::FLAG_UPLOAD));
+ if(dir == "Upload") {
+ // Fine, the other fellow want's to send us data...make sure we really want that...
+ if(aSource->isSet(UserConnection::FLAG_UPLOAD)) {
+ // Huh? Strange...disconnect...
+ putConnection(aSource);
+ return;
+ }
+ } else {
+ if(aSource->isSet(UserConnection::FLAG_DOWNLOAD)) {
+ int number = Util::toInt(num);
+ // Damn, both want to download...the one with the highest number wins...
+ if(aSource->getNumber() < number) {
+ // Damn! We lost!
+ aSource->unsetFlag(UserConnection::FLAG_DOWNLOAD);
+ aSource->setFlag(UserConnection::FLAG_UPLOAD);
+ } else if(aSource->getNumber() == number) {
+ putConnection(aSource);
+ return;
+ }
+ }
+ }
+
+ dcassert(aSource->isSet(UserConnection::FLAG_DOWNLOAD) ^ aSource->isSet(UserConnection::FLAG_UPLOAD));
+
+ aSource->setState(UserConnection::STATE_KEY);
+}
+
+void ConnectionManager::addDownloadConnection(UserConnection* uc, bool sendNTD) {
+
+ dcassert(uc->isSet(UserConnection::FLAG_DOWNLOAD));
+
+ uc->removeListener(this);
+
+ bool addConn = false;
+ {
+ Lock l(cs);
+
+ ConnectionQueueItem::Iter i = find(downloads.begin(), downloads.end(), uc->getUser());
+ if(i != downloads.end()) {
+ ConnectionQueueItem* cqi = *i;
+ if(cqi->getState() == ConnectionQueueItem::WAITING || cqi->getState() == ConnectionQueueItem::CONNECTING) {
+ // Associate the two...
+ dcassert(uc->getCQI() == NULL);
+ uc->setCQI(cqi);
+ dcassert(cqi->getConnection() == NULL);
+ cqi->setConnection(uc);
+ cqi->setState(ConnectionQueueItem::ACTIVE);
+
+ fire(ConnectionManagerListener::Connected(), cqi);
+
+ dcdebug("ConnectionManager::addDownloadConnection, leaving to downloadmanager\n");
+ addConn = true;
+ }
+ }
+ }
+
+ if(addConn) {
+ DownloadManager::getInstance()->addConnection(uc);
+ } else if(sendNTD) {
+ uc->ntd();
+ uc->unsetFlag(UserConnection::FLAG_DOWNLOAD);
+ uc->setFlag(UserConnection::FLAG_UPLOAD);
+ addUploadConnection(uc);
+ } else {
+ putConnection(uc);
+ }
+}
+
+void ConnectionManager::addUploadConnection(UserConnection* uc) {
+ dcassert(uc->isSet(UserConnection::FLAG_UPLOAD));
+
+ uc->removeListener(this);
+
+ bool addConn = false;
+ {
+ Lock l(cs);
+
+ ConnectionQueueItem::Iter i = find(uploads.begin(), uploads.end(), uc->getUser());
+ if(i == uploads.end()) {
+ ConnectionQueueItem* cqi = getCQI(uc->getUser(), false);
+
+ uc->setCQI(cqi);
+ cqi->setConnection(uc);
+ cqi->setState(ConnectionQueueItem::ACTIVE);
+
+ fire(ConnectionManagerListener::Connected(), cqi);
+
+ dcdebug("ConnectionManager::addUploadConnection, leaving to uploadmanager\n");
+ addConn = true;
+ }
+ }
+
+ if(addConn) {
+ UploadManager::getInstance()->addConnection(uc);
+ } else {
+ putConnection(uc);
+ }
+}
+
+void ConnectionManager::on(UserConnectionListener::Key, UserConnection* aSource, const string&/* aKey*/) throw() {
+ if(aSource->getState() != UserConnection::STATE_KEY) {
+ dcdebug("CM::onKey Bad state, ignoring");
+ return;
+ }
+
+ dcassert(aSource->getUser());
+
+ if(aSource->isSet(UserConnection::FLAG_DOWNLOAD)) {
+ addDownloadConnection(aSource, false);
+ } else {
+ addUploadConnection(aSource);
+ }
+}
+
+void ConnectionManager::on(AdcCommand::INF, UserConnection* aSource, const AdcCommand& cmd) throw() {
+ if(aSource->getState() != UserConnection::STATE_INF) {
+ // Already got this once, ignore...
+ aSource->sta(AdcCommand::SEV_FATAL, AdcCommand::ERROR_PROTOCOL_GENERIC, "Expecting INF");
+ dcdebug("CM::onMyNick %p sent nick twice\n", aSource);
+ return;
+ }
+
+ aSource->setUser(ClientManager::getInstance()->getUser(cmd.getFrom(), false));
+
+ if(!aSource->getUser()) {
+ dcdebug("CM::onINF: User not found");
+ aSource->sta(AdcCommand::SEV_FATAL, AdcCommand::ERROR_GENERIC, "User not found");
+ putConnection(aSource);
+ return;
+ }
+
+ if(aSource->isSet(UserConnection::FLAG_INCOMING)) {
+ aSource->setFlag(UserConnection::FLAG_DOWNLOAD);
+ addDownloadConnection(aSource, true);
+ } else {
+ aSource->setFlag(UserConnection::FLAG_UPLOAD);
+ addUploadConnection(aSource);
+ }
+}
+
+void ConnectionManager::on(UserConnectionListener::Failed, UserConnection* aSource, const string& /*aError*/) throw() {
+ if(aSource->isSet(UserConnection::FLAG_DOWNLOAD) && aSource->getCQI()) {
+ {
+ Lock l(cs);
+
+ ConnectionQueueItem* cqi = aSource->getCQI();
+ dcassert(cqi->getState() == ConnectionQueueItem::IDLE);
+ cqi->setState(ConnectionQueueItem::WAITING);
+ cqi->setLastAttempt(GET_TICK());
+ cqi->setConnection(NULL);
+ aSource->setCQI(NULL);
+ }
+ }
+ putConnection(aSource);
+}
+
+void ConnectionManager::removeConnection(const User::Ptr& aUser, int isDownload) {
+ Lock l(cs);
+ for(UserConnection::Iter i = userConnections.begin(); i != userConnections.end(); ++i) {
+ UserConnection* uc = *i;
+ if(uc->getUser() == aUser && uc->isSet(isDownload ? UserConnection::FLAG_DOWNLOAD : UserConnection::FLAG_UPLOAD)) {
+ uc->disconnect();
+ break;
+ }
+ }
+}
+
+void ConnectionManager::shutdown() {
+ shuttingDown = true;
+ socket.removeListener(this);
+ socket.disconnect();
+ {
+ Lock l(cs);
+ for(UserConnection::Iter j = userConnections.begin(); j != userConnections.end(); ++j) {
+ (*j)->disconnect();
+ }
+ }
+ // Wait until all connections have died out...
+ while(true) {
+ {
+ Lock l(cs);
+ if(userConnections.empty()) {
+ break;
+ }
+ }
+ Thread::sleep(50);
+ }
+}
+
+// UserConnectionListener
+void ConnectionManager::on(UserConnectionListener::Supports, UserConnection* conn, const StringList& feat) throw() {
+ for(StringList::const_iterator i = feat.begin(); i != feat.end(); ++i) {
+ if(*i == UserConnection::FEATURE_GET_ZBLOCK)
+ conn->setFlag(UserConnection::FLAG_SUPPORTS_GETZBLOCK);
+ else if(*i == UserConnection::FEATURE_MINISLOTS)
+ conn->setFlag(UserConnection::FLAG_SUPPORTS_MINISLOTS);
+ else if(*i == UserConnection::FEATURE_XML_BZLIST)
+ conn->setFlag(UserConnection::FLAG_SUPPORTS_XML_BZLIST);
+ else if(*i == UserConnection::FEATURE_ADCGET)
+ conn->setFlag(UserConnection::FLAG_SUPPORTS_ADCGET);
+ else if(*i == UserConnection::FEATURE_ZLIB_GET)
+ conn->setFlag(UserConnection::FLAG_SUPPORTS_ZLIB_GET);
+ else if(*i == UserConnection::FEATURE_TTHL)
+ conn->setFlag(UserConnection::FLAG_SUPPORTS_TTHL);
+ else if(*i == UserConnection::FEATURE_TTHF)
+ conn->setFlag(UserConnection::FLAG_SUPPORTS_TTHF);
+ }
+}
+
+/**
+ * @file
+ * $Id: ConnectionManager.cpp,v 1.2 2005/08/21 14:03:43 olof Exp $
+ */
diff --git a/dcpp/ConnectionManager.h b/dcpp/ConnectionManager.h
new file mode 100644
index 0000000..f964f3c
--- /dev/null
+++ b/dcpp/ConnectionManager.h
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2001-2005 Jacek Sieka, arnetheduck on gmail point 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 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.
+ */
+
+#if !defined(AFX_ConnectionManager_H__675A2F66_AFE6_4A15_8386_6B6FD579D5FF__INCLUDED_)
+#define AFX_ConnectionManager_H__675A2F66_AFE6_4A15_8386_6B6FD579D5FF__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+#include "TimerManager.h"
+
+#include "ServerSocket.h"
+#include "UserConnection.h"
+#include "User.h"
+#include "CriticalSection.h"
+#include "Singleton.h"
+#include "Util.h"
+
+#include "ConnectionManagerListener.h"
+
+class ConnectionQueueItem {
+public:
+ typedef ConnectionQueueItem* Ptr;
+ typedef vector<Ptr> List;
+ typedef List::iterator Iter;
+
+ enum State {
+ CONNECTING, // Recently sent request to connect
+ WAITING, // Waiting to send request to connect
+ NO_DOWNLOAD_SLOTS, // Bot needed right now
+ IDLE, // In the download pool
+ ACTIVE // In one up/downmanager
+ };
+
+ ConnectionQueueItem(const User::Ptr& aUser, bool aDownload) : state(WAITING), connection(NULL), lastAttempt(0), download(aDownload), user(aUser) { };
+
+ User::Ptr& getUser() { return user; };
+
+ GETSET(State, state, State);
+ GETSET(UserConnection*, connection, Connection);
+ GETSET(u_int32_t, lastAttempt, LastAttempt);
+ GETSET(bool, download, Download);
+private:
+ ConnectionQueueItem(const ConnectionQueueItem&);
+ ConnectionQueueItem& operator=(const ConnectionQueueItem&);
+
+ User::Ptr user;
+};
+// Comparing with a user...
+inline bool operator==(ConnectionQueueItem::Ptr ptr, const User::Ptr& aUser) { return ptr->getUser() == aUser; }
+
+class ConnectionManager : public Speaker<ConnectionManagerListener>,
+ public UserConnectionListener, ServerSocketListener, TimerManagerListener,
+ public Singleton<ConnectionManager>
+{
+public:
+ void nmdcConnect(const string& aServer, short aPort, const string& aNick);
+ void adcConnect(const string& aServer, short aPort, const string& aToken);
+ void getDownloadConnection(const User::Ptr& aUser);
+ void putDownloadConnection(UserConnection* aSource, bool reuse = false, bool ntd = false);
+ void putUploadConnection(UserConnection* aSource, bool ntd);
+
+ void removeConnection(const User::Ptr& aUser, int isDownload);
+ void shutdown();
+ /**
+ * Set this ConnectionManager to listen at a different port.
+ */
+ void setPort(short aPort) throw(SocketException) {
+ port = aPort;
+ socket.waitForConnections(aPort);
+ }
+ void disconnect() throw() {
+ socket.disconnect();
+ }
+ unsigned short getPort() {
+ return port;
+ }
+
+ // Ugly trick to use windows messages...
+ ServerSocket& getServerSocket() {
+ return socket;
+ }
+
+private:
+ CriticalSection cs;
+ short port;
+
+ /** All ConnectionQueueItems */
+ ConnectionQueueItem::List downloads;
+ ConnectionQueueItem::List uploads;
+
+ User::List pendingAdd;
+ UserConnection::List pendingDelete;
+ /** All active connections */
+ UserConnection::List userConnections;
+
+ ServerSocket socket;
+ StringList features;
+ StringList adcFeatures;
+
+ u_int32_t floodCounter;
+
+ bool shuttingDown;
+
+ friend class Singleton<ConnectionManager>;
+ ConnectionManager();
+
+ virtual ~ConnectionManager() throw() { shutdown(); };
+
+ UserConnection* getConnection(bool aNmdc) throw(SocketException);
+ void putConnection(UserConnection* aConn);
+
+ void addUploadConnection(UserConnection* uc);
+ void addDownloadConnection(UserConnection* uc, bool sendNTD);
+
+ ConnectionQueueItem* getCQI(const User::Ptr& aUser, bool download);
+ void putCQI(ConnectionQueueItem* cqi);
+
+ // ServerSocketListener
+ virtual void on(ServerSocketListener::IncomingConnection) throw();
+
+ // UserConnectionListener
+ virtual void on(Connected, UserConnection*) throw();
+ virtual void on(Failed, UserConnection*, const string&) throw();
+ virtual void on(CLock, UserConnection*, const string&, const string&) throw();
+ virtual void on(Key, UserConnection*, const string&) throw();
+ virtual void on(Direction, UserConnection*, const string&, const string&) throw();
+ virtual void on(MyNick, UserConnection*, const string&) throw();
+ virtual void on(Supports, UserConnection*, const StringList&) throw();
+
+ virtual void on(AdcCommand::SUP, UserConnection*, const AdcCommand&) throw();
+ virtual void on(AdcCommand::INF, UserConnection*, const AdcCommand&) throw();
+ virtual void on(AdcCommand::NTD, UserConnection*, const AdcCommand&) throw();
+ virtual void on(AdcCommand::STA, UserConnection*, const AdcCommand&) throw();
+
+ // TimerManagerListener
+ virtual void on(TimerManagerListener::Second, u_int32_t aTick) throw();
+ virtual void on(TimerManagerListener::Minute, u_int32_t aTick) throw();
+
+};
+
+#endif // !defined(AFX_ConnectionManager_H__675A2F66_AFE6_4A15_8386_6B6FD579D5FF__INCLUDED_)
+
+/**
+ * @file
+ * $Id: ConnectionManager.h,v 1.2 2005/08/21 14:03:43 olof Exp $
+ */
diff --git a/dcpp/ConnectionManagerListener.h b/dcpp/ConnectionManagerListener.h
new file mode 100644
index 0000000..1c3b319
--- /dev/null
+++ b/dcpp/ConnectionManagerListener.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2001-2005 Jacek Sieka, arnetheduck on gmail point 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 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.
+ */
+
+#if !defined(AFX_CONNECTIONMANAGERLISTENER_H__E8F009DF_D216_4F8F_8C81_07D2FA0BFB7F__INCLUDED_)
+#define AFX_CONNECTIONMANAGERLISTENER_H__E8F009DF_D216_4F8F_8C81_07D2FA0BFB7F__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+class ConnectionQueueItem;
+
+class ConnectionManagerListener {
+public:
+ template<int I> struct X { enum { TYPE = I }; };
+
+ typedef X<0> Added;
+ typedef X<1> Connected;
+ typedef X<2> Removed;
+ typedef X<3> Failed;
+ typedef X<4> StatusChanged;
+
+ virtual void on(Added, ConnectionQueueItem*) throw() { };
+ virtual void on(Connected, ConnectionQueueItem*) throw() { };
+ virtual void on(Removed, ConnectionQueueItem*) throw() { };
+ virtual void on(Failed, ConnectionQueueItem*, const string&) throw() { };
+ virtual void on(StatusChanged, ConnectionQueueItem*) throw() { };
+};
+
+#endif
+
+/**
+* @file
+* $Id: ConnectionManagerListener.h,v 1.2 2005/08/21 14:03:43 olof Exp $
+*/
diff --git a/dcpp/CriticalSection.h b/dcpp/CriticalSection.h
new file mode 100644
index 0000000..e2a9629
--- /dev/null
+++ b/dcpp/CriticalSection.h
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2001-2005 Jacek Sieka, arnetheduck on gmail point 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 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.
+ */
+
+#ifndef CRITICALSECTION_H
+#define CRITICALSECTION_H
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+#include "Thread.h"
+
+class CriticalSection
+{
+#ifdef _WIN32
+public:
+ void enter() throw() {
+ EnterCriticalSection(&cs);
+ dcdrun(counter++);
+ }
+ void leave() throw() {
+ dcassert(--counter >= 0);
+ LeaveCriticalSection(&cs);
+ }
+ CriticalSection() throw() {
+ dcdrun(counter = 0;);
+ InitializeCriticalSection(&cs);
+ }
+ ~CriticalSection() throw() {
+ dcassert(counter==0);
+ DeleteCriticalSection(&cs);
+ }
+private:
+ dcdrun(long counter;);
+ CRITICAL_SECTION cs;
+#else
+public:
+ CriticalSection() throw() {
+ static bool inited = false;
+ static pthread_mutex_t recmtx = {0};
+ if(!inited) {
+ pthread_mutexattr_t attr;
+ pthread_mutexattr_init(&attr);
+ pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
+ pthread_mutex_init(&recmtx, &attr);
+ pthread_mutexattr_destroy(&attr);
+ inited = true;
+ }
+ mtx = recmtx;
+ };
+ ~CriticalSection() throw() { pthread_mutex_destroy(&mtx); };
+ void enter() throw() { pthread_mutex_lock(&mtx); };
+ void leave() throw() { pthread_mutex_unlock(&mtx); };
+ pthread_mutex_t& getMutex() { return mtx; };
+private:
+ pthread_mutex_t mtx;
+#endif
+ CriticalSection(const CriticalSection&);
+ CriticalSection& operator=(const CriticalSection&);
+};
+
+/**
+ * A fast, non-recursive and unfair implementation of the Critical Section.
+ * It is meant to be used in situations where the risk for lock conflict is very low,
+ * i e locks that are held for a very short time. The lock is _not_ recursive, i e if
+ * the same thread will try to grab the lock it'll hang in a never-ending loop. The lock
+ * is not fair, i e the first to try to enter a locked lock is not guaranteed to be the
+ * first to get it when it's freed...
+ */
+class FastCriticalSection {
+public:
+#ifdef _WIN32
+ FastCriticalSection() : state(0) { };
+
+ void enter() {
+ while(Thread::safeExchange(state, 1) == 1) {
+ Thread::yield();
+ }
+ }
+ void leave() {
+ Thread::safeDec(state);
+ }
+private:
+ volatile long state;
+
+#else
+ // We have to use a pthread (nonrecursive) mutex, didn't find any test_and_set on linux...
+ FastCriticalSection() {
+ static pthread_mutex_t fastmtx = PTHREAD_MUTEX_INITIALIZER;
+ mtx = fastmtx;
+ };
+ ~FastCriticalSection() { pthread_mutex_destroy(&mtx); };
+ void enter() { pthread_mutex_lock(&mtx); };
+ void leave() { pthread_mutex_unlock(&mtx); };
+private:
+ pthread_mutex_t mtx;
+#endif
+};
+
+template<class T>
+class LockBase {
+public:
+ LockBase(T& aCs) throw() : cs(aCs) { cs.enter(); };
+ ~LockBase() throw() { cs.leave(); };
+private:
+ LockBase& operator=(const LockBase&);
+ T& cs;
+};
+typedef LockBase<CriticalSection> Lock;
+typedef LockBase<FastCriticalSection> FastLock;
+
+template<class T = CriticalSection>
+class RWLock
+{
+public:
+ RWLock() throw() : cs(), readers(0) { }
+ ~RWLock() throw() { dcassert(readers==0); }
+
+ void enterRead() throw() {
+ Lock l(cs);
+ readers++;
+ dcassert(readers < 100);
+ }
+
+ void leaveRead() throw() {
+ Thread::safeDec(readers);
+ dcassert(readers >= 0);
+ }
+ void enterWrite() throw() {
+ cs.enter();
+ while(readers > 0) {
+ cs.leave();
+ Thread::yield();
+ cs.enter();
+ }
+ }
+ void leaveWrite() {
+ cs.leave();
+ }
+private:
+ T cs;
+ volatile long readers;
+};
+
+template<class T = CriticalSection>
+class RLock {
+public:
+ RLock(RWLock<T>& aRwl) throw() : rwl(aRwl) { rwl.enterRead(); };
+ ~RLock() throw() { rwl.leaveRead(); };
+private:
+ RLock& operator=(const RLock&);
+ RWLock<T>& rwl;
+};
+
+template<class T = CriticalSection>
+class WLock {
+public:
+ WLock(RWLock<T>& aRwl) throw() : rwl(aRwl) { rwl.enterWrite(); };
+ ~WLock() throw() { rwl.leaveWrite(); };
+private:
+ WLock& operator=(const WLock&);
+ RWLock<T>& rwl;
+};
+
+#endif // CRITICALSECTION_H
+
+/**
+ * @file
+ * $Id: CriticalSection.h,v 1.6 2005/10/20 19:50:45 olof Exp $
+ */
diff --git a/dcpp/CryptoManager.cpp b/dcpp/CryptoManager.cpp
new file mode 100644
index 0000000..de6b592
--- /dev/null
+++ b/dcpp/CryptoManager.cpp
@@ -0,0 +1,405 @@
+/*
+ * Copyright (C) 2001-2005 Jacek Sieka, arnetheduck on gmail point 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 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.
+ */
+
+#include "stdinc.h"
+#include "DCPlusPlus.h"
+
+#include "CryptoManager.h"
+
+#include "BitInputStream.h"
+#include "BitOutputStream.h"
+#include "ResourceManager.h"
+
+#ifdef _WIN32
+#include "../bzip2/bzlib.h"
+#else
+#include <bzlib.h>
+#endif
+
+void CryptoManager::decodeBZ2(const u_int8_t* is, size_t sz, string& os) throw (CryptoException) {
+ bz_stream bs = { 0 };
+
+ if(BZ2_bzDecompressInit(&bs, 0, 0) != BZ_OK)
+ throw(CryptoException(STRING(DECOMPRESSION_ERROR)));
+
+ // We assume that the files aren't compressed more than 2:1...if they are it'll work anyway,
+ // but we'll have to do multiple passes...
+ size_t bufsize = 2*sz;
+ AutoArray<char> buf(bufsize);
+
+ bs.avail_in = sz;
+ bs.avail_out = bufsize;
+ bs.next_in = (char*)(const_cast<u_int8_t*>(is));
+ bs.next_out = buf;
+
+ int err;
+
+ os.clear();
+
+ while((err = BZ2_bzDecompress(&bs)) == BZ_OK) {
+ if (bs.avail_in == 0 && bs.avail_out > 0) { // error: BZ_UNEXPECTED_EOF
+ BZ2_bzDecompressEnd(&bs);
+ throw CryptoException(STRING(DECOMPRESSION_ERROR));
+ }
+ os.append(buf, bufsize-bs.avail_out);
+ bs.avail_out = bufsize;
+ bs.next_out = buf;
+ }
+
+ if(err == BZ_STREAM_END)
+ os.append(buf, bufsize-bs.avail_out);
+
+ BZ2_bzDecompressEnd(&bs);
+
+ if(err < 0) {
+ // This was a real error
+ throw CryptoException(STRING(DECOMPRESSION_ERROR));
+ }
+}
+
+string CryptoManager::keySubst(const u_int8_t* aKey, size_t len, size_t n) {
+ AutoArray<u_int8_t> temp(len + n * 10);
+
+ size_t j=0;
+
+ for(size_t i = 0; i<len; i++) {
+ if(isExtra(aKey[i])) {
+ temp[j++] = '/'; temp[j++] = '%'; temp[j++] = 'D';
+ temp[j++] = 'C'; temp[j++] = 'N';
+ switch(aKey[i]) {
+ case 0: temp[j++] = '0'; temp[j++] = '0'; temp[j++] = '0'; break;
+ case 5: temp[j++] = '0'; temp[j++] = '0'; temp[j++] = '5'; break;
+ case 36: temp[j++] = '0'; temp[j++] = '3'; temp[j++] = '6'; break;
+ case 96: temp[j++] = '0'; temp[j++] = '9'; temp[j++] = '6'; break;
+ case 124: temp[j++] = '1'; temp[j++] = '2'; temp[j++] = '4'; break;
+ case 126: temp[j++] = '1'; temp[j++] = '2'; temp[j++] = '6'; break;
+ }
+ temp[j++] = '%'; temp[j++] = '/';
+ } else {
+ temp[j++] = aKey[i];
+ }
+ }
+ return string((char*)(u_int8_t*)temp, j);
+}
+
+string CryptoManager::makeKey(const string& aLock) {
+ if(aLock.size() < 3)
+ return Util::emptyString;
+
+ AutoArray<u_int8_t> temp(aLock.length());
+ u_int8_t v1;
+ size_t extra=0;
+
+ v1 = (u_int8_t)(aLock[0]^5);
+ v1 = (u_int8_t)(((v1 >> 4) | (v1 << 4)) & 0xff);
+ temp[0] = v1;
+
+ string::size_type i;
+
+ for(i = 1; i<aLock.length(); i++) {
+ v1 = (u_int8_t)(aLock[i]^aLock[i-1]);
+ v1 = (u_int8_t)(((v1 >> 4) | (v1 << 4))&0xff);
+ temp[i] = v1;
+ if(isExtra(temp[i]))
+ extra++;
+ }
+
+ temp[0] = (u_int8_t)(temp[0] ^ temp[aLock.length()-1]);
+
+ if(isExtra(temp[0])) {
+ extra++;
+ }
+
+ return keySubst(temp, aLock.length(), extra);
+}
+
+void CryptoManager::decodeHuffman(const u_int8_t* is, string& os, const size_t len) throw(CryptoException) {
+// BitInputStream bis;
+ int pos = 0;
+
+ if(len < 11 || is[pos] != 'H' || is[pos+1] != 'E' || !((is[pos+2] == '3') || (is[pos+2] == '0'))) {
+ throw CryptoException(STRING(DECOMPRESSION_ERROR));
+ }
+ pos+=5;
+
+ int size;
+ size = *(int*)&is[pos];
+
+ pos+=4;
+
+ dcdebug("Size: %d\n", size);
+
+ unsigned short treeSize;
+ treeSize = *(unsigned short*)&is[pos];
+
+ pos+=2;
+
+ if(len < (size_t)(11 + treeSize * 2))
+ throw CryptoException(STRING(DECOMPRESSION_ERROR));
+ Leaf** leaves = new Leaf*[treeSize];
+
+ int i;
+ for(i=0; i<treeSize; i++) {
+ int chr = is[pos++];
+ int bits = is[pos++];
+ leaves[i] = new Leaf(chr, bits);
+ }
+
+ BitInputStream bis(is, pos, len);
+
+ DecNode* root = new DecNode();
+
+ for(i=0; i<treeSize; i++) {
+ DecNode* node = root;
+ for(int j=0; j<leaves[i]->len; j++) {
+ try {
+ if(bis.get()) {
+ if(node->right == NULL)
+ node->right = new DecNode();
+
+ node = node->right;
+ } else {
+ if(node->left == NULL)
+ node->left = new DecNode();
+
+ node = node->left;
+ }
+ } catch(const BitStreamException&) {
+ throw CryptoException(STRING(DECOMPRESSION_ERROR));
+ }
+ }
+ node->chr = leaves[i]->chr;
+ }
+
+ bis.skipToByte();
+
+ // We know the size, so no need to use strange STL stuff...
+ AutoArray<char> buf(size+1);
+
+ pos = 0;
+ for(i=0; i<size; i++) {
+ DecNode* node = root;
+ while(node->chr == -1) {
+ try {
+ if(bis.get()) {
+ node = node->right;
+ } else {
+ node = node->left;
+ }
+ } catch(const BitStreamException&) {
+ throw CryptoException(STRING(DECOMPRESSION_ERROR));
+ }
+
+ if(node == NULL) {
+ for(i=0; i<treeSize; i++) {
+ delete leaves[i];
+ }
+
+ delete[] leaves;
+ delete root;
+
+ dcdebug("Bad node found!!!\n");
+ throw CryptoException(STRING(DECOMPRESSION_ERROR));
+ }
+ }
+ buf[pos++] = (u_int8_t)node->chr;
+ }
+ buf[pos] = 0;
+ os.assign(buf, size);
+
+ for(i=0; i<treeSize; i++) {
+ delete leaves[i];
+ }
+
+ delete[] leaves;
+ delete root;
+}
+
+/**
+ * Counts the occurances of each characters, and adds the total number of
+ * different characters to the end of the array.
+ */
+int CryptoManager::countChars(const string& aString, int* c, u_int8_t& csum) {
+ int chars = 0;
+ const u_int8_t* a = (const u_int8_t*)aString.data();
+ string::size_type len = aString.length();
+ for(string::size_type i=0; i<len; i++) {
+
+ if(c[a[i]] == 0)
+ chars++;
+
+ c[a[i]]++;
+ csum^=a[i];
+ }
+ return chars;
+}
+
+void CryptoManager::walkTree(list<Node*>& aTree) {
+ while(aTree.size() > 1) {
+ // Merge the first two nodes
+ Node* node = new Node(aTree.front(), *(++aTree.begin()));
+ aTree.pop_front();
+ aTree.pop_front();
+
+ bool done = false;
+ for(list<Node*>::iterator i=aTree.begin(); i != aTree.end(); ++i) {
+ if(*node <= *(*i)) {
+ aTree.insert(i, node);
+ done = true;
+ break;
+ }
+ }
+
+ if(!done)
+ aTree.push_back(node);
+
+ }
+}
+
+/**
+ * @todo Make more effective in terms of memory allocations and copies...
+ */
+void CryptoManager::recurseLookup(vector<u_int8_t>* table, Node* node, vector<u_int8_t>& u_int8_ts) {
+ if(node->chr != -1) {
+ table[node->chr] = u_int8_ts;
+ return;
+ }
+
+ vector<u_int8_t> left = u_int8_ts;
+ vector<u_int8_t> right = u_int8_ts;
+
+ left.push_back(0);
+ right.push_back(1);
+
+ recurseLookup(table, node->left, left);
+ recurseLookup(table, node->right, right);
+}
+
+/**
+ * Builds a table over the characters available (for fast lookup).
+ * Stores each character as a set of u_int8_ts with values {0, 1}.
+ */
+void CryptoManager::buildLookup(vector<u_int8_t>* table, Node* aRoot) {
+ vector<u_int8_t> left;
+ vector<u_int8_t> right;
+
+ left.push_back(0);
+ right.push_back(1);
+
+ recurseLookup(table, aRoot->left, left);
+ recurseLookup(table, aRoot->right, right);
+}
+
+
+struct greaterNode {
+ bool operator() (const Node* a, const Node* b) const {
+ return *a < *b;
+ };
+};
+
+/**
+ * Encodes a set of data with DC's version of huffman encoding..
+ * @todo Use real streams maybe? or something else than string (operator[] contains a compare, slow...)
+ */
+void CryptoManager::encodeHuffman(const string& is, string& os) {
+
+ // We might as well expect this much data as huffman encoding doesn't go very far...
+ os.reserve(is.size());
+ if(is.length() == 0) {
+ os.append("HE3\x0d");
+
+ // Nada...
+ os.append(7, '\0');
+ return;
+ }
+ // First, we count all characters
+ u_int8_t csum = 0;
+ int count[256];
+ memset(count, 0, sizeof(count));
+ int chars = countChars(is, count, csum);
+
+ // Next, we create a set of nodes and add it to a list, removing all characters that never occur.
+
+ list<Node*> nodes;
+
+ int i;
+ for(i=0; i<256; i++) {
+ if(count[i] > 0) {
+ nodes.push_back(new Node(i, count[i]));
+ }
+ }
+
+ nodes.sort(greaterNode());
+#ifdef _DEBUG
+ for(list<Node*>::iterator it = nodes.begin(); it != nodes.end(); ++it) dcdebug("%.02x:%d, ", (*it)->chr, (*it)->weight);
+ dcdebug("\n");
+#endif
+
+ walkTree(nodes);
+ dcassert(nodes.size() == 1);
+
+ Node* root = nodes.front();
+ vector<u_int8_t> lookup[256];
+
+ // Build a lookup table for fast character lookups
+ buildLookup(lookup, root);
+ delete root;
+
+ // Reserve some memory to avoid all those copies when appending...
+ os.reserve(is.size() * 3 / 4);
+
+ os.append("HE3\x0d");
+
+ // Checksum
+ os.append(1, csum);
+ string::size_type sz = is.size();
+ os.append((char*)&sz, 4);
+
+ // Character count
+ os.append((char*)&chars, 2);
+
+ // The characters and their bitlengths
+ for(i=0; i<256; i++) {
+ if(count[i] > 0) {
+ os.append(1, (u_int8_t)i);
+ os.append(1, (u_int8_t)lookup[i].size());
+ }
+ }
+
+ BitOutputStream bos(os);
+ // The tree itself, ie the bits of each character
+ for(i=0; i<256; i++) {
+ if(count[i] > 0) {
+ bos.put(lookup[i]);
+ }
+ }
+
+ dcdebug("u_int8_ts: %lu\n", os.size());
+ bos.skipToByte();
+
+ for(string::size_type j=0; j<is.size(); j++) {
+ dcassert(lookup[(u_int8_t)is[j]].size() != 0);
+ bos.put(lookup[(u_int8_t)is[j]]);
+ }
+ bos.skipToByte();
+}
+
+/**
+ * @file
+ * $Id: CryptoManager.cpp,v 1.2 2005/08/21 14:03:43 olof Exp $
+ */
diff --git a/dcpp/CryptoManager.h b/dcpp/CryptoManager.h
new file mode 100644
index 0000000..adea380
--- /dev/null
+++ b/dcpp/CryptoManager.h
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2001-2005 Jacek Sieka, arnetheduck on gmail point 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 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.
+ */
+
+#if !defined(AFX_CRYPTO_H__28F66860_0AD5_44AD_989C_BA4326C42F46__INCLUDED_)
+#define AFX_CRYPTO_H__28F66860_0AD5_44AD_989C_BA4326C42F46__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+#include "SettingsManager.h"
+
+#include "Exception.h"
+#include "Singleton.h"
+#include "FastAlloc.h"
+#include "version.h"
+
+STANDARD_EXCEPTION(CryptoException);
+
+class Node : public FastAlloc<Node> {
+public:
+ // What's this? The only way (I've found out) to avoid a Internal Compiler Error! If this class is moved into
+ // CryptoManager along with the greater specialization, it generates a ICE on the greater class. The typedefs
+ // had to be removed in order to avoid template instatiation.
+// typedef Node* Ptr;
+// typedef list<Ptr> List;
+// typedef List::iterator Iter;
+ int chr;
+ int weight;
+
+ Node* left;
+ Node* right;
+
+ Node(int aChr, int aWeight) : chr(aChr), weight(aWeight), left(NULL), right(NULL) { };
+ Node(Node* aLeft, Node* aRight) : chr(-1), weight(aLeft->weight + aRight->weight), left(aLeft), right(aRight) { };
+ ~Node() {
+ delete left;
+ delete right;
+ }
+ bool operator <(const Node& rhs) const {
+ return weight<rhs.weight;
+ }
+ bool operator >(const Node& rhs) const {
+ return weight>rhs.weight;
+ }
+ bool operator <=(const Node& rhs) const {
+ return weight<=rhs.weight;
+ }
+ bool operator >=(const Node& rhs) const {
+ return weight>rhs.weight;
+ }
+};
+
+class File;
+class FileException;
+
+class CryptoManager : public Singleton<CryptoManager>
+{
+public:
+ string makeKey(const string& aLock);
+ const string& getLock() { return lock; };
+ const string& getPk() { return pk; };
+ bool isExtended(const string& aLock) { return strncmp(aLock.c_str(), "EXTENDEDPROTOCOL", 16) == 0; };
+
+ void decodeHuffman(const u_int8_t* /*is*/, string& /*os*/, const size_t /*len*/) throw(CryptoException);
+ void encodeHuffman(const string& is, string& os);
+ void decodeBZ2(const u_int8_t* is, size_t sz, string& os) throw(CryptoException);
+private:
+
+ friend class Singleton<CryptoManager>;
+
+ CryptoManager() : lock("EXTENDEDPROTOCOLABCABCABCABCABCABC"), pk("DCPLUSPLUS" VERSIONSTRING "ABCABC") { };
+ virtual ~CryptoManager() { };
+
+ class Leaf : public FastAlloc<Leaf> {
+ public:
+ int chr;
+ int len;
+ Leaf(int aChr, int aLen) : chr(aChr), len(aLen) { };
+ Leaf() : chr(-1), len(-1) { };
+ };
+
+ class DecNode : public FastAlloc<DecNode> {
+ public:
+ int chr;
+ DecNode* left;
+ DecNode* right;
+ DecNode(int aChr) : chr(aChr), left(NULL), right(NULL) { };
+ DecNode(DecNode* aLeft, DecNode* aRight) : chr(-1), left(aLeft), right(aRight) { };
+ DecNode() : chr(-1), left(NULL), right(NULL) { };
+ ~DecNode() {
+ delete left;
+ delete right;
+ }
+ };
+
+ const string lock;
+ const string pk;
+
+ int countChars(const string& aString, int* c, u_int8_t& csum);
+ void walkTree(list<Node*>& aTree);
+ void recurseLookup(vector<u_int8_t>* b, Node* node, vector<u_int8_t>& bytes);
+ void buildLookup(vector<u_int8_t>* b, Node* root);
+
+ string keySubst(const u_int8_t* aKey, size_t len, size_t n);
+ bool isExtra(u_int8_t b) {
+ return (b == 0 || b==5 || b==124 || b==96 || b==126 || b==36);
+ }
+};
+
+#endif // !defined(AFX_CRYPTO_H__28F66860_0AD5_44AD_989C_BA4326C42F46__INCLUDED_)
+
+/**
+ * @file
+ * $Id: CryptoManager.h,v 1.2 2005/08/21 14:03:43 olof Exp $
+ */
diff --git a/dcpp/DCPlusPlus.cpp b/dcpp/DCPlusPlus.cpp
new file mode 100644
index 0000000..d7a874d
--- /dev/null
+++ b/dcpp/DCPlusPlus.cpp
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2001-2005 Jacek Sieka, arnetheduck on gmail point 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 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.
+ */
+
+#include "stdinc.h"
+#include "DCPlusPlus.h"
+
+#include "ConnectionManager.h"
+#include "DownloadManager.h"
+#include "UploadManager.h"
+#include "CryptoManager.h"
+#include "ShareManager.h"
+#include "SearchManager.h"
+#include "QueueManager.h"
+#include "ClientManager.h"
+#include "HashManager.h"
+#include "LogManager.h"
+#include "HubManager.h"
+#include "SettingsManager.h"
+#include "FinishedManager.h"
+#include "ADLSearch.h"
+
+#include "StringTokenizer.h"
+
+void startup(void (*f)(void*, const string&), void* p) {
+ // "Dedicated to the near-memory of Nev. Let's start remembering people while they're still alive."
+ // Nev's great contribution to dc++
+ while(1) break;
+
+ Util::initialize();
+
+ ResourceManager::newInstance();
+ SettingsManager::newInstance();
+
+ LogManager::newInstance();
+ TimerManager::newInstance();
+ HashManager::newInstance();
+ CryptoManager::newInstance();
+ SearchManager::newInstance();
+ ClientManager::newInstance();
+ ConnectionManager::newInstance();
+ DownloadManager::newInstance();
+ UploadManager::newInstance();
+ ShareManager::newInstance();
+ HubManager::newInstance();
+ QueueManager::newInstance();
+ FinishedManager::newInstance();
+ ADLSearchManager::newInstance();
+
+ SettingsManager::getInstance()->load();
+
+ if(!SETTING(LANGUAGE_FILE).empty()) {
+ ResourceManager::getInstance()->loadLanguage(SETTING(LANGUAGE_FILE));
+ }
+
+ HubManager::getInstance()->load();
+ int i;
+ for(i = 0; i < SettingsManager::SPEED_LAST; i++) {
+ if(SETTING(CONNECTION) == SettingsManager::connectionSpeeds[i])
+ break;
+ }
+ if(i == SettingsManager::SPEED_LAST) {
+ SettingsManager::getInstance()->set(SettingsManager::CONNECTION, SettingsManager::connectionSpeeds[0]);
+ }
+
+ if(f != NULL)
+ (*f)(p, STRING(HASH_DATABASE));
+ HashManager::getInstance()->startup();
+ if(f != NULL)
+ (*f)(p, STRING(SHARED_FILES));
+ ShareManager::getInstance()->refresh(true, false, true);
+ if(f != NULL)
+ (*f)(p, STRING(DOWNLOAD_QUEUE));
+ QueueManager::getInstance()->loadQueue();
+
+}
+
+void shutdown() {
+ ConnectionManager::getInstance()->shutdown();
+ HashManager::getInstance()->shutdown();
+
+ TimerManager::getInstance()->removeListeners();
+ SettingsManager::getInstance()->save();
+
+ ADLSearchManager::deleteInstance();
+ FinishedManager::deleteInstance();
+ ShareManager::deleteInstance();
+ CryptoManager::deleteInstance();
+ DownloadManager::deleteInstance();
+ UploadManager::deleteInstance();
+ QueueManager::deleteInstance();
+ ConnectionManager::deleteInstance();
+ SearchManager::deleteInstance();
+ ClientManager::deleteInstance();
+ HubManager::deleteInstance();
+ HashManager::deleteInstance();
+ LogManager::deleteInstance();
+ SettingsManager::deleteInstance();
+ TimerManager::deleteInstance();
+ ResourceManager::deleteInstance();
+}
+
+/**
+ * @file
+ * $Id: DCPlusPlus.cpp,v 1.2 2005/08/21 14:03:43 olof Exp $
+ */
+
diff --git a/dcpp/DCPlusPlus.h b/dcpp/DCPlusPlus.h
new file mode 100644
index 0000000..3b8c421
--- /dev/null
+++ b/dcpp/DCPlusPlus.h
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2001-2005 Jacek Sieka, arnetheduck on gmail point 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 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.
+ */
+
+#ifndef DCPLUSPLUS_H
+#define DCPLUSPLUS_H
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+#ifdef _DEBUG
+
+inline void CDECL debugTrace(const char* format, ...)
+{
+ va_list args;
+ va_start(args, format);
+
+#ifdef _WIN32
+ char buf[512];
+
+ _vsnprintf(buf, sizeof(buf), format, args);
+ OutputDebugStringA(buf);
+#else // _WIN32
+ vprintf(format, args);
+#endif // _WIN32
+ va_end(args);
+}
+
+#define dcdebug debugTrace
+#ifdef _WIN32
+#define dcassert(exp) \
+do { if (!(exp)) { \
+ dcdebug("Assertion hit in %s(%d): " #exp "\n", __FILE__, __LINE__); \
+ if(1 == _CrtDbgReport(_CRT_ASSERT, __FILE__, __LINE__, NULL, #exp)) \
+_CrtDbgBreak(); } } while(false)
+#define dcasserta(exp) dcassert(0)
+#else
+#include <assert.h>
+#define dcasserta(exp) assert(exp)
+#define dcassert(exp) assert(exp)
+#endif
+#define dcdrun(exp) exp
+#else //_DEBUG
+#ifdef _WIN32
+#define dcasserta(exp) __assume(exp)
+#else
+#define dcasserta(exp)
+#endif // _WIN32
+#define dcdebug if (false) printf
+#define dcassert(exp)
+#define dcdrun(exp)
+#endif //_DEBUG
+
+// Make sure we're using the templates from algorithm...
+#ifdef min
+#undef min
+#endif
+#ifdef max
+#undef max
+#endif
+
+typedef vector<string> StringList;
+typedef StringList::iterator StringIter;
+typedef StringList::const_iterator StringIterC;
+
+typedef pair<string, string> StringPair;
+typedef vector<StringPair> StringPairList;
+typedef StringPairList::iterator StringPairIter;
+
+typedef HASH_MAP<string, string> StringMap;
+typedef StringMap::iterator StringMapIter;
+
+typedef vector<wstring> WStringList;
+typedef WStringList::iterator WStringIter;
+typedef WStringList::const_iterator WStringIterC;
+
+typedef pair<wstring, wstring> WStringPair;
+typedef vector<WStringPair> WStringPairList;
+typedef WStringPairList::iterator WStringPairIter;
+
+typedef HASH_MAP<wstring, wstring> WStringMap;
+typedef WStringMap::iterator WStringMapIter;
+
+#ifdef UNICODE
+
+typedef wstring tstring;
+typedef WStringList TStringList;
+typedef WStringIter TStringIter;
+
+typedef WStringPair TStringPair;
+typedef WStringPairIter TStringPairIter;
+typedef WStringPairList TStringPairList;
+
+typedef WStringMap TStringMap;
+typedef WStringMapIter TStringMapIter;
+#else
+typedef string tstring;
+typedef StringList TStringList;
+typedef StringIter TStringIter;
+
+typedef StringPair TStringPair;
+typedef StringPairIter TStringPairIter;
+typedef StringPairList TStringPairList;
+
+typedef StringMap TStringMap;
+typedef StringMapIter TStringMapIter;
+#endif
+
+extern void startup(void (*f)(void*, const string&), void* p);
+extern void shutdown();
+
+#endif // DCPLUSPLUS_H
+
+/**
+ * @file
+ * $Id: DCPlusPlus.h,v 1.2 2005/08/21 14:03:43 olof Exp $
+ */
diff --git a/dcpp/DirectoryListing.cpp b/dcpp/DirectoryListing.cpp
new file mode 100644
index 0000000..7f55b42
--- /dev/null
+++ b/dcpp/DirectoryListing.cpp
@@ -0,0 +1,336 @@
+/*
+ * Copyright (C) 2001-2005 Jacek Sieka, arnetheduck on gmail point 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 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.
+ */
+
+#include "stdinc.h"
+#include "DCPlusPlus.h"
+
+#include "DirectoryListing.h"
+
+#include "QueueManager.h"
+#include "SearchManager.h"
+
+#include "StringTokenizer.h"
+#include "SimpleXML.h"
+#include "FilteredFile.h"
+#include "BZUtils.h"
+#include "CryptoManager.h"
+
+#ifdef ff
+#undef ff
+#endif
+
+void DirectoryListing::loadFile(const string& name) {
+ string txt;
+
+ // For now, we detect type by ending...
+ string ext = Util::getFileExt(name);
+ if(Util::stricmp(ext, ".DcLst") == 0) {
+ size_t len = (size_t)::File::getSize(name);
+ if(len == (size_t)-1)
+ return;
+ AutoArray<u_int8_t> buf(len);
+ ::File(name, ::File::READ, ::File::OPEN).read(buf, len);
+ CryptoManager::getInstance()->decodeHuffman(buf, txt, len);
+ load(txt);
+ } else if(Util::stricmp(ext, ".bz2") == 0) {
+ ::File ff(name, ::File::READ, ::File::OPEN);
+ FilteredInputStream<UnBZFilter, false> f(&ff);
+ const size_t BUF_SIZE = 64*1024;
+ char buf[BUF_SIZE];
+ size_t len;
+ for(;;) {
+ size_t n = BUF_SIZE;
+ len = f.read(buf, n);
+ txt.append(buf, len);
+ if(len < BUF_SIZE)
+ break;
+ }
+
+ loadXML(txt, false);
+ }
+}
+
+void DirectoryListing::load(const string& in) {
+ StringTokenizer<string> t(in, '\n');
+
+ StringList& tokens = t.getTokens();
+ string::size_type indent = 0;
+
+ root->setComplete(true);
+
+ Directory* cur = root;
+ string fullPath;
+
+ for(StringIter i = tokens.begin(); i != tokens.end(); ++i)
+ {
+ string& tok = *i;
+ string::size_type j = tok.find_first_not_of('\t');
+ if(j == string::npos) {
+ break;
+ }
+
+ while(j < indent) {
+ // Wind up directory structure
+ cur = cur->getParent();
+ dcassert(cur != NULL);
+ indent--;
+ string::size_type l = fullPath.find_last_of('\\');
+ if(l != string::npos) {
+ fullPath.erase(fullPath.begin() + l, fullPath.end());
+ }
+ }
+
+ string::size_type k = tok.find('|', j);
+ if(k != string::npos) {
+ // this must be a file...
+ cur->files.push_back(new File(cur, tok.substr(j, k-j), Util::toInt64(tok.substr(k+1))));
+ } else {
+ // A directory
+ string name = tok.substr(j, tok.length()-j-1);
+ fullPath += '\\';
+ fullPath += name;
+
+ Directory::Iter di = ::find(cur->directories.begin(), cur->directories.end(), name);
+ if(di != cur->directories.end()) {
+ cur = *di;
+ } else {
+ Directory* d = new Directory(cur, name, false, true);
+ cur->directories.push_back(d);
+ cur = d;
+ }
+ indent++;
+ }
+ }
+}
+
+class ListLoader : public SimpleXMLReader::CallBack {
+public:
+ ListLoader(DirectoryListing::Directory* root, bool aUpdating) : cur(root), base("/"), inListing(false), updating(aUpdating) {
+ };
+
+ virtual ~ListLoader() { }
+
+ virtual void startTag(const string& name, StringPairList& attribs, bool simple);
+ virtual void endTag(const string& name, const string& data);
+
+ const string& getBase() const { return base; }
+private:
+ DirectoryListing::Directory* cur;
+
+ StringMap params;
+ string base;
+ bool inListing;
+ bool updating;
+};
+
+string DirectoryListing::loadXML(const string& xml, bool updating) {
+ setUtf8(true);
+
+ ListLoader ll(getRoot(), updating);
+ SimpleXMLReader(&ll).fromXML(xml);
+ return ll.getBase();
+}
+
+static const string sFileListing = "FileListing";
+static const string sBase = "Base";
+static const string sDirectory = "Directory";
+static const string sIncomplete = "Incomplete";
+static const string sFile = "File";
+static const string sName = "Name";
+static const string sSize = "Size";
+static const string sTTH = "TTH";
+
+void ListLoader::startTag(const string& name, StringPairList& attribs, bool simple) {
+ if(inListing) {
+ if(name == sFile) {
+ const string& n = getAttrib(attribs, sName, 0);
+ if(n.empty())
+ return;
+ const string& s = getAttrib(attribs, sSize, 1);
+ if(s.empty())
+ return;
+ const string& h = getAttrib(attribs, sTTH, 2);
+ DirectoryListing::File* f = h.empty() ? new DirectoryListing::File(cur, n, Util::toInt64(s)) : new DirectoryListing::File(cur, n, Util::toInt64(s), h);
+ cur->files.push_back(f);
+ } else if(name == sDirectory) {
+ const string& n = getAttrib(attribs, sName, 0);
+ if(n.empty()) {
+ throw SimpleXMLException("Directory missing name attribute");
+ }
+ bool incomp = getAttrib(attribs, sIncomplete, 1) == "1";
+ DirectoryListing::Directory* d = NULL;
+ if(updating) {
+ for(DirectoryListing::Directory::Iter i = cur->directories.begin(); i != cur->directories.end(); ++i) {
+ if((*i)->getName() == n) {
+ d = *i;
+ if(!d->getComplete())
+ d->setComplete(!incomp);
+ break;
+ }
+ }
+ }
+ if(d == NULL) {
+ d = new DirectoryListing::Directory(cur, n, false, !incomp);
+ cur->directories.push_back(d);
+ }
+ cur = d;
+
+ if(simple) {
+ // To handle <Directory Name="..." />
+ endTag(name, Util::emptyString);
+ }
+ }
+ } else if(name == sFileListing) {
+ const string& b = getAttrib(attribs, sBase, 2);
+ if(b.size() >= 1 && b[0] == '/' && b[b.size()-1] == '/') {
+ base = b;
+ }
+ StringList sl = StringTokenizer<string>(base.substr(1), '/').getTokens();
+ for(StringIter i = sl.begin(); i != sl.end(); ++i) {
+ DirectoryListing::Directory* d = NULL;
+ for(DirectoryListing::Directory::Iter j = cur->directories.begin(); j != cur->directories.end(); ++j) {
+ if((*j)->getName() == *i) {
+ d = *j;
+ break;
+ }
+ }
+ if(d == NULL) {
+ d = new DirectoryListing::Directory(cur, *i, false, false);
+ cur->directories.push_back(d);
+ }
+ cur = d;
+ }
+ cur->setComplete(true);
+ inListing = true;
+
+ if(simple) {
+ // To handle <Directory Name="..." />
+ endTag(name, Util::emptyString);
+ }
+ }
+}
+
+void ListLoader::endTag(const string& name, const string&) {
+ if(inListing) {
+ if(name == sDirectory) {
+ cur = cur->getParent();
+ } else if(name == sFileListing) {
+ // cur should be root now...
+ inListing = false;
+ }
+ }
+}
+
+string DirectoryListing::getPath(Directory* d) {
+ if(d == root)
+ return "";
+
+ string dir;
+ dir.reserve(128);
+ dir.append(d->getName());
+ dir.append(1, '\\');
+
+ Directory* cur = d->getParent();
+ while(cur!=root) {
+ dir.insert(0, cur->getName() + '\\');
+ cur = cur->getParent();
+ }
+ return dir;
+}
+
+static inline const string& escaper(const string& n, string& tmp, bool utf8) {
+ return utf8 ? n : (tmp.clear(), Text::acpToUtf8(n, tmp));
+}
+
+void DirectoryListing::download(Directory* aDir, const string& aTarget, bool highPrio) {
+ string tmp;
+ string target = (aDir == getRoot()) ? aTarget : aTarget + escaper(aDir->getName(), tmp, getUtf8()) + PATH_SEPARATOR;
+ // First, recurse over the directories
+ Directory::List& lst = aDir->directories;
+ sort(lst.begin(), lst.end(), Directory::DirSort());
+ for(Directory::Iter j = lst.begin(); j != lst.end(); ++j) {
+ download(*j, target, highPrio);
+ }
+ // Then add the files
+ File::List& l = aDir->files;
+ sort(l.begin(), l.end(), File::FileSort());
+ for(File::Iter i = aDir->files.begin(); i != aDir->files.end(); ++i) {
+ File* file = *i;
+ try {
+ download(file, target + escaper(file->getName(), tmp, getUtf8()), false, highPrio);
+ } catch(const QueueException&) {
+ // Catch it here to allow parts of directories to be added...
+ } catch(const FileException&) {
+ //..
+ }
+ }
+}
+
+void DirectoryListing::download(const string& aDir, const string& aTarget, bool highPrio) {
+ dcassert(aDir.size() > 2);
+ dcassert(aDir[aDir.size() - 1] == PATH_SEPARATOR);
+ Directory* d = find(aDir, getRoot());
+ if(d != NULL)
+ download(d, aTarget, highPrio);
+}
+
+void DirectoryListing::download(File* aFile, const string& aTarget, bool view, bool highPrio) {
+ int flags = (getUtf8() ? QueueItem::FLAG_SOURCE_UTF8 : 0) |
+ (view ? (QueueItem::FLAG_TEXT | QueueItem::FLAG_CLIENT_VIEW) : QueueItem::FLAG_RESUME);
+
+ QueueManager::getInstance()->add(getPath(aFile) + aFile->getName(), aFile->getSize(), user, aTarget,
+ aFile->getTTH(), flags, highPrio || view ? QueueItem::HIGHEST : QueueItem::DEFAULT);
+}
+
+DirectoryListing::Directory* DirectoryListing::find(const string& aName, Directory* current) {
+ string::size_type end = aName.find('\\');
+ dcassert(end != string::npos);
+ string name = aName.substr(0, end);
+
+ Directory::Iter i = ::find(current->directories.begin(), current->directories.end(), name);
+ if(i != current->directories.end()) {
+ if(end == (aName.size() - 1))
+ return *i;
+ else
+ return find(aName.substr(end + 1), *i);
+ }
+ return NULL;
+}
+
+int64_t DirectoryListing::Directory::getTotalSize(bool adl) {
+ int64_t x = getSize();
+ for(Iter i = directories.begin(); i != directories.end(); ++i) {
+ if(!(adl && (*i)->getAdls()))
+ x += (*i)->getTotalSize(adls);
+ }
+ return x;
+}
+
+size_t DirectoryListing::Directory::getTotalFileCount(bool adl) {
+ size_t x = getFileCount();
+ for(Iter i = directories.begin(); i != directories.end(); ++i) {
+ if(!(adl && (*i)->getAdls()))
+ x += (*i)->getTotalFileCount(adls);
+ }
+ return x;
+}
+
+/**
+ * @file
+ * $Id: DirectoryListing.cpp,v 1.4 2006/01/05 22:03:26 mickeg Exp $
+ */
diff --git a/dcpp/DirectoryListing.h b/dcpp/DirectoryListing.h
new file mode 100644
index 0000000..f49c2cc
--- /dev/null
+++ b/dcpp/DirectoryListing.h
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2001-2005 Jacek Sieka, arnetheduck on gmail point 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 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.
+ */
+
+#if !defined(AFX_DIRECTORYLISTING_H__D2AF61C5_DEDE_42E0_8257_71D5AB567D39__INCLUDED_)
+#define AFX_DIRECTORYLISTING_H__D2AF61C5_DEDE_42E0_8257_71D5AB567D39__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+#include "User.h"
+#include "FastAlloc.h"
+
+#include "MerkleTree.h"
+
+class ListLoader;
+
+class DirectoryListing
+{
+public:
+ class Directory;
+
+ class File : public FastAlloc<File> {
+ public:
+ typedef File* Ptr;
+ struct FileSort {
+ bool operator()(const Ptr& a, const Ptr& b) const {
+ return Util::stricmp(a->getName().c_str(), b->getName().c_str()) < 0;
+ }
+ };
+ typedef vector<Ptr> List;
+ typedef List::iterator Iter;
+
+ File(Directory* aDir, const string& aName, int64_t aSize, const string& aTTH) throw() :
+ name(aName), size(aSize), parent(aDir), tthRoot(new TTHValue(aTTH)), adls(false)
+ {
+ };
+ File(Directory* aDir, const string& aName, int64_t aSize) throw() :
+ name(aName), size(aSize), parent(aDir), tthRoot(NULL), adls(false)
+ {
+ };
+
+ File(const File& rhs, bool _adls = false) : name(rhs.name), size(rhs.size), parent(rhs.parent), tthRoot(rhs.tthRoot == NULL ? NULL : new TTHValue(*rhs.tthRoot)), adls(_adls)
+ {
+ }
+
+ File& operator=(const File& rhs) {
+ name = rhs.name; size = rhs.size; parent = rhs.parent; tthRoot = rhs.tthRoot ? new TTHValue(*rhs.tthRoot) : NULL;
+ return *this;
+ }
+
+ ~File() {
+ delete tthRoot;
+ }
+
+ GETSET(string, name, Name);
+ GETSET(int64_t, size, Size);
+ GETSET(Directory*, parent, Parent);
+ GETSET(TTHValue*, tthRoot, TTH);
+ GETSET(bool, adls, Adls);
+ };
+
+ class Directory : public FastAlloc<Directory> {
+ public:
+ typedef Directory* Ptr;
+ struct DirSort {
+ bool operator()(const Ptr& a, const Ptr& b) const {
+ return Util::stricmp(a->getName().c_str(), b->getName().c_str()) < 0;
+ }
+ };
+ typedef vector<Ptr> List;
+ typedef List::iterator Iter;
+
+ List directories;
+ File::List files;
+
+ Directory(Directory* aParent, const string& aName, bool _adls, bool aComplete)
+ : name(aName), parent(aParent), adls(_adls), complete(aComplete) { };
+
+ virtual ~Directory() {
+ for_each(directories.begin(), directories.end(), DeleteFunction<Directory*>());
+ for_each(files.begin(), files.end(), DeleteFunction<File*>());
+ }
+
+ size_t getTotalFileCount(bool adls = false);
+ int64_t getTotalSize(bool adls = false);
+
+ size_t getFileCount() { return files.size(); };
+
+ int64_t getSize() {
+ int64_t x = 0;
+ for(File::Iter i = files.begin(); i != files.end(); ++i) {
+ x+=(*i)->getSize();
+ }
+ return x;
+ }
+
+ GETSET(string, name, Name);
+ GETSET(Directory*, parent, Parent);
+ GETSET(bool, adls, Adls);
+ GETSET(bool, complete, Complete);
+
+ private:
+ Directory(const Directory&);
+ Directory& operator=(const Directory&);
+ };
+
+ class AdlDirectory : public Directory {
+ public:
+ AdlDirectory(const string& aFullPath, Directory* aParent, const string& aName) : Directory(aParent, aName, true, true), fullPath(aFullPath) { };
+
+ GETSET(string, fullPath, FullPath);
+ };
+
+ DirectoryListing(const User::Ptr& aUser) : user(aUser), utf8(false), root(new Directory(NULL, Util::emptyString, false, false)) {
+ };
+
+ ~DirectoryListing() {
+ delete root;
+ };
+
+ void loadFile(const string& name);
+
+ void load(const string& i);
+ string loadXML(const string& xml, bool updating);
+
+ void download(const string& aDir, const string& aTarget, bool highPrio);
+ void download(Directory* aDir, const string& aTarget, bool highPrio);
+ void download(File* aFile, const string& aTarget, bool view, bool highPrio);
+
+ string getPath(Directory* d);
+ string getPath(File* f) { return getPath(f->getParent()); };
+
+ int64_t getTotalSize(bool adls = false) { return root->getTotalSize(adls); };
+ size_t getTotalFileCount(bool adls = false) { return root->getTotalFileCount(adls); };
+
+ Directory* getRoot() { return root; };
+
+ GETSET(User::Ptr, user, User);
+ GETSET(bool, utf8, Utf8);
+
+private:
+ friend class ListLoader;
+
+ DirectoryListing(const DirectoryListing&);
+ DirectoryListing& operator=(const DirectoryListing&);
+
+ Directory* root;
+
+ Directory* find(const string& aName, Directory* current);
+
+};
+
+inline bool operator==(DirectoryListing::Directory::Ptr a, const string& b) { return Util::stricmp(a->getName(), b) == 0; }
+inline bool operator==(DirectoryListing::File::Ptr a, const string& b) { return Util::stricmp(a->getName(), b) == 0; }
+
+#endif // !defined(AFX_DIRECTORYLISTING_H__D2AF61C5_DEDE_42E0_8257_71D5AB567D39__INCLUDED_)
+
+/**
+ * @file
+ * $Id: DirectoryListing.h,v 1.2 2005/08/21 14:03:43 olof Exp $
+ */
diff --git a/dcpp/DownloadManager.cpp b/dcpp/DownloadManager.cpp
new file mode 100644
index 0000000..1d3a3b7
--- /dev/null
+++ b/dcpp/DownloadManager.cpp
@@ -0,0 +1,891 @@
+/*
+ * Copyright (C) 2001-2005 Jacek Sieka, arnetheduck on gmail point 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 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.
+ */
+
+#include "stdinc.h"
+#include "DCPlusPlus.h"
+
+#include "DownloadManager.h"
+
+#include "ResourceManager.h"
+#include "ConnectionManager.h"
+#include "QueueManager.h"
+#include "CryptoManager.h"
+#include "HashManager.h"
+
+#include "LogManager.h"
+#include "SFVReader.h"
+#include "User.h"
+#include "File.h"
+#include "FilteredFile.h"
+#include "MerkleCheckOutputStream.h"
+
+#include <limits>
+
+// some strange mac definition
+#ifdef ff
+#undef ff
+#endif
+
+static const string DOWNLOAD_AREA = "Downloads";
+const string Download::ANTI_FRAG_EXT = ".antifrag";
+
+Download::Download() throw() : file(NULL),
+crcCalc(NULL), tth(NULL), treeValid(false) {
+}
+
+Download::Download(QueueItem* qi) throw() : source(qi->getCurrent()->getPath()),
+ target(qi->getTarget()), tempTarget(qi->getTempTarget()), file(NULL),
+ crcCalc(NULL), tth(qi->getTTH()), treeValid(false) {
+
+ setSize(qi->getSize());
+ if(qi->isSet(QueueItem::FLAG_USER_LIST))
+ setFlag(Download::FLAG_USER_LIST);
+ if(qi->isSet(QueueItem::FLAG_RESUME))
+ setFlag(Download::FLAG_RESUME);
+ if(qi->getCurrent()->isSet(QueueItem::Source::FLAG_UTF8))
+ setFlag(Download::FLAG_UTF8);
+}
+
+AdcCommand Download::getCommand(bool zlib, bool tthf) {
+ AdcCommand cmd(AdcCommand::CMD_GET);
+ if(isSet(FLAG_TREE_DOWNLOAD)) {
+ cmd.addParam("tthl");
+ } else if(isSet(FLAG_PARTIAL_LIST)) {
+ cmd.addParam("list");
+ } else {
+ cmd.addParam("file");
+ }
+ if(tthf && getTTH() != NULL) {
+ cmd.addParam("TTH/" + getTTH()->toBase32());
+ } else {
+ cmd.addParam(Util::toAdcFile(getSource()));
+ }
+ cmd.addParam(Util::toString(getPos()));
+ cmd.addParam(Util::toString(getSize() - getPos()));
+
+ if(zlib && getSize() != -1 && BOOLSETTING(COMPRESS_TRANSFERS)) {
+ cmd.addParam("ZL1");
+ }
+
+ return cmd;
+}
+
+void DownloadManager::on(TimerManagerListener::Second, u_int32_t /*aTick*/) throw() {
+ Lock l(cs);
+
+ Download::List tickList;
+ // Tick each ongoing download
+ for(Download::Iter i = downloads.begin(); i != downloads.end(); ++i) {
+ if((*i)->getTotal() > 0) {
+ tickList.push_back(*i);
+ }
+ }
+
+ if(tickList.size() > 0)
+ fire(DownloadManagerListener::Tick(), tickList);
+}
+
+void DownloadManager::FileMover::moveFile(const string& source, const string& target) {
+ Lock l(cs);
+ files.push_back(make_pair(source, target));
+ if(!active) {
+ active = true;
+ start();
+ }
+}
+
+int DownloadManager::FileMover::run() {
+ for(;;) {
+ FilePair next;
+ {
+ Lock l(cs);
+ if(files.empty()) {
+ active = false;
+ return 0;
+ }
+ next = files.back();
+ files.pop_back();
+ }
+ try {
+ File::renameFile(next.first, next.second);
+ } catch(const FileException&) {
+ // Too bad...
+ }
+ }
+}
+
+void DownloadManager::removeConnection(UserConnection::Ptr aConn, bool reuse /* = false */, bool ntd /* = false */) {
+ dcassert(aConn->getDownload() == NULL);
+ aConn->removeListener(this);
+ ConnectionManager::getInstance()->putDownloadConnection(aConn, reuse, ntd);
+}
+
+class TreeOutputStream : public OutputStream {
+public:
+ TreeOutputStream(TigerTree& aTree) : tree(aTree), bufPos(0) {
+ }
+
+ virtual size_t write(const void* xbuf, size_t len) throw(Exception) {
+ size_t pos = 0;
+ u_int8_t* b = (u_int8_t*)xbuf;
+ while(pos < len) {
+ size_t left = len - pos;
+ if(bufPos == 0 && left >= TigerTree::HASH_SIZE) {
+ tree.getLeaves().push_back(TTHValue(b + pos));
+ pos += TigerTree::HASH_SIZE;
+ } else {
+ size_t bytes = min(TigerTree::HASH_SIZE - bufPos, left);
+ memcpy(buf + bufPos, b + pos, bytes);
+ bufPos += bytes;
+ pos += bytes;
+ if(bufPos == TigerTree::HASH_SIZE) {
+ tree.getLeaves().push_back(TTHValue(buf));
+ bufPos = 0;
+ }
+ }
+ }
+ return len;
+ }
+
+ virtual size_t flush() throw(Exception) {
+ return 0;
+ }
+private:
+ TigerTree& tree;
+ u_int8_t buf[TigerTree::HASH_SIZE];
+ size_t bufPos;
+};
+
+void DownloadManager::checkDownloads(UserConnection* aConn) {
+ dcassert(aConn->getDownload() == NULL);
+
+ bool slotsFull = (SETTING(DOWNLOAD_SLOTS) != 0) && (getDownloadCount() >= (size_t)SETTING(DOWNLOAD_SLOTS));
+ bool speedFull = (SETTING(MAX_DOWNLOAD_SPEED) != 0) && (getAverageSpeed() >= (SETTING(MAX_DOWNLOAD_SPEED)*1024));
+
+ if( slotsFull || speedFull ) {
+ bool extraFull = (SETTING(DOWNLOAD_SLOTS) != 0) && (getDownloadCount() >= (size_t)(SETTING(DOWNLOAD_SLOTS)+3));
+ if(extraFull || !QueueManager::getInstance()->hasDownload(aConn->getUser(), QueueItem::HIGHEST)) {
+ removeConnection(aConn);
+ return;
+ }
+ }
+
+ Download* d = QueueManager::getInstance()->getDownload(aConn->getUser(), aConn->isSet(UserConnection::FLAG_SUPPORTS_TTHL));
+
+ if(d == NULL) {
+ removeConnection(aConn, true);
+ return;
+ }
+
+ {
+ Lock l(cs);
+ downloads.push_back(d);
+ }
+
+ d->setUserConnection(aConn);
+ aConn->setDownload(d);
+
+ aConn->setState(UserConnection::STATE_FILELENGTH);
+
+ if(d->isSet(Download::FLAG_RESUME)) {
+ dcassert(d->getSize() != -1);
+
+ const string& target = (d->getTempTarget().empty() ? d->getTarget() : d->getTempTarget());
+ int64_t start = File::getSize(target);
+
+ // Only use antifrag if we don't have a previous non-antifrag part
+ if( BOOLSETTING(ANTI_FRAG) && (start == -1) && (d->getSize() != -1) ) {
+ int64_t aSize = File::getSize(target + Download::ANTI_FRAG_EXT);
+
+ if(aSize == d->getSize())
+ start = d->getPos();
+ else
+ start = 0;
+
+ d->setFlag(Download::FLAG_ANTI_FRAG);
+ }
+
+ if(BOOLSETTING(ADVANCED_RESUME) && d->getTreeValid() && start > 0) {
+ d->setStartPos(getResumePos(d->getDownloadTarget(), d->getTigerTree(), start));
+ } else {
+ int rollback = SETTING(ROLLBACK);
+ if(rollback > start) {
+ d->setStartPos(0);
+ } else {
+ d->setStartPos(start - rollback);
+ d->setFlag(Download::FLAG_ROLLBACK);
+ }
+ }
+
+ } else {
+ d->setStartPos(0);
+ }
+
+ if(d->isSet(Download::FLAG_USER_LIST)) {
+ if(!aConn->isSet(UserConnection::FLAG_NMDC) || aConn->isSet(UserConnection::FLAG_SUPPORTS_XML_BZLIST)) {
+ d->setSource("files.xml.bz2");
+ if(!aConn->isSet(UserConnection::FLAG_NMDC) || aConn->isSet(UserConnection::FLAG_SUPPORTS_ADCGET))
+ d->setFlag(Download::FLAG_UTF8);
+ }
+ }
+
+ // File ok for adcget in nmdc-conns
+ bool adcOk = d->isSet(Download::FLAG_UTF8) || (aConn->isSet(UserConnection::FLAG_SUPPORTS_TTHF) && d->getTTH() != NULL);
+
+ if(!aConn->isSet(UserConnection::FLAG_NMDC) || (aConn->isSet(UserConnection::FLAG_SUPPORTS_ADCGET) && adcOk)) {
+ aConn->send(d->getCommand(
+ aConn->isSet(UserConnection::FLAG_SUPPORTS_ZLIB_GET),
+ aConn->isSet(!aConn->isSet(UserConnection::FLAG_NMDC) || UserConnection::FLAG_SUPPORTS_TTHF)
+ ));
+ } else {
+ if(BOOLSETTING(COMPRESS_TRANSFERS) && aConn->isSet(UserConnection::FLAG_SUPPORTS_GETZBLOCK) && d->getSize() != -1 ) {
+ // This one, we'll download with a zblock download instead...
+ d->setFlag(Download::FLAG_ZDOWNLOAD);
+ aConn->getZBlock(d->getSource(), d->getPos(), d->getBytesLeft(), d->isSet(Download::FLAG_UTF8));
+ } else if(aConn->isSet(UserConnection::FLAG_SUPPORTS_XML_BZLIST) && d->isSet(Download::FLAG_UTF8)) {
+ aConn->uGetBlock(d->getSource(), d->getPos(), d->getBytesLeft());
+ } else {
+ aConn->get(d->getSource(), d->getPos());
+ }
+ }
+}
+
+class DummyOutputStream : public OutputStream {
+public:
+ virtual size_t write(const void*, size_t n) throw(Exception) { return n; }
+ virtual size_t flush() throw(Exception) { return 0; }
+};
+
+int64_t DownloadManager::getResumePos(const string& file, const TigerTree& tt, int64_t startPos) {
+ // Always discard data until the last block
+ startPos = startPos - (startPos % tt.getBlockSize());
+ if(startPos < tt.getBlockSize())
+ return 0;
+
+ DummyOutputStream dummy;
+
+ vector<u_int8_t> buf((size_t)min((int64_t)1024*1024, tt.getBlockSize()));
+
+ do {
+ int64_t blockPos = startPos - tt.getBlockSize();
+ MerkleCheckOutputStream<TigerTree, false> check(tt, &dummy, blockPos);
+
+ try {
+ File inFile(file, File::READ, File::OPEN);
+ inFile.setPos(blockPos);
+ int64_t bytesLeft = tt.getBlockSize();
+ while(bytesLeft > 0) {
+ size_t n = (size_t)min((int64_t)buf.size(), bytesLeft);
+ size_t nr = inFile.read(&buf[0], n);
+ check.write(&buf[0], nr);
+ bytesLeft -= nr;
+ if(bytesLeft > 0 && nr == 0) {
+ // Huh??
+ throw Exception();
+ }
+ }
+ check.flush();
+ break;
+ } catch(const Exception&) {
+ dcdebug("Removed bad block at " I64_FMT "\n", blockPos);
+ }
+ startPos = blockPos;
+ } while(startPos > 0);
+ return startPos;
+}
+
+void DownloadManager::on(UserConnectionListener::Sending, UserConnection* aSource, int64_t aBytes) throw() {
+ if(aSource->getState() != UserConnection::STATE_FILELENGTH) {
+ dcdebug("DM::onFileLength Bad state, ignoring\n");
+ return;
+ }
+
+ if(prepareFile(aSource, (aBytes == -1) ? -1 : aSource->getDownload()->getPos() + aBytes, aSource->getDownload()->isSet(Download::FLAG_ZDOWNLOAD))) {
+ aSource->setDataMode();
+ }
+}
+
+void DownloadManager::on(UserConnectionListener::FileLength, UserConnection* aSource, int64_t aFileLength) throw() {
+
+ if(aSource->getState() != UserConnection::STATE_FILELENGTH) {
+ dcdebug("DM::onFileLength Bad state, ignoring\n");
+ return;
+ }
+
+ if(prepareFile(aSource, aFileLength, aSource->getDownload()->isSet(Download::FLAG_ZDOWNLOAD))) {
+ aSource->setDataMode();
+ aSource->startSend();
+ }
+}
+
+void DownloadManager::on(AdcCommand::SND, UserConnection* aSource, const AdcCommand& cmd) throw() {
+ if(aSource->getState() != UserConnection::STATE_FILELENGTH) {
+ dcdebug("DM::onFileLength Bad state, ignoring\n");
+ return;
+ }
+
+ const string& type = cmd.getParam(0);
+ int64_t bytes = Util::toInt64(cmd.getParam(3));
+
+ if(!(type == "file" || (type == "tthl" && aSource->getDownload()->isSet(Download::FLAG_TREE_DOWNLOAD)) ||
+ (type == "list" && aSource->getDownload()->isSet(Download::FLAG_PARTIAL_LIST))) )
+ {
+ // Uhh??? We didn't ask for this?
+ aSource->disconnect();
+ return;
+ }
+
+ if(prepareFile(aSource, (bytes == -1) ? -1 : aSource->getDownload()->getPos() + bytes, cmd.hasFlag("ZL", 4))) {
+ aSource->setDataMode();
+ }
+}
+
+class RollbackException : public FileException {
+public:
+ RollbackException (const string& aError) : FileException(aError) { };
+};
+
+template<bool managed>
+class RollbackOutputStream : public OutputStream {
+public:
+ RollbackOutputStream(File* f, OutputStream* aStream, size_t bytes) : s(aStream), pos(0), bufSize(bytes), buf(new u_int8_t[bytes]) {
+ size_t n = bytes;
+ f->read(buf, n);
+ f->movePos(-((int64_t)bytes));
+ }
+ virtual ~RollbackOutputStream() throw() { delete[] buf; if(managed) delete s; };
+
+ virtual size_t flush() throw(FileException) {
+ return s->flush();
+ }
+
+ virtual size_t write(const void* b, size_t len) throw(FileException) {
+ if(buf != NULL) {
+ size_t n = min(len, bufSize - pos);
+
+ u_int8_t* wb = (u_int8_t*)b;
+ if(memcmp(buf + pos, wb, n) != 0) {
+ throw RollbackException(STRING(ROLLBACK_INCONSISTENCY));
+ }
+ pos += n;
+ if(pos == bufSize) {
+ delete buf;
+ buf = NULL;
+ }
+ }
+ return s->write(b, len);
+ }
+
+private:
+ OutputStream* s;
+ size_t pos;
+ size_t bufSize;
+ u_int8_t* buf;
+};
+
+
+bool DownloadManager::prepareFile(UserConnection* aSource, int64_t newSize, bool z) {
+ Download* d = aSource->getDownload();
+ dcassert(d != NULL);
+
+ if(newSize != -1) {
+ d->setSize(newSize);
+ }
+ if(d->getPos() >= d->getSize()) {
+ // Already finished?
+ aSource->setDownload(NULL);
+ removeDownload(d);
+ QueueManager::getInstance()->putDownload(d, true);
+ removeConnection(aSource);
+ return false;
+ }
+
+ dcassert(d->getSize() != -1);
+
+ if(d->isSet(Download::FLAG_PARTIAL_LIST)) {
+ d->setFile(new StringOutputStream(d->getPFS()));
+ } else if(d->isSet(Download::FLAG_TREE_DOWNLOAD)) {
+ d->setFile(new TreeOutputStream(d->getTigerTree()));
+ } else {
+ string target = d->getDownloadTarget();
+ File::ensureDirectory(target);
+ if(d->isSet(Download::FLAG_USER_LIST)) {
+ if(!aSource->isSet(UserConnection::FLAG_NMDC) || aSource->isSet(UserConnection::FLAG_SUPPORTS_XML_BZLIST)) {
+ target += ".xml.bz2";
+ } else {
+ target += ".DcLst";
+ }
+ }
+
+ File* file = NULL;
+ try {
+ // Let's check if we can find this file in a any .SFV...
+ int trunc = d->isSet(Download::FLAG_RESUME) ? 0 : File::TRUNCATE;
+ file = new File(target, File::RW, File::OPEN | File::CREATE | trunc);
+ if(d->isSet(Download::FLAG_ANTI_FRAG)) {
+ file->setSize(d->getSize());
+ }
+ file->setPos(d->getPos());
+ } catch(const FileException& e) {
+ delete file;
+ removeDownload(d);
+ fire(DownloadManagerListener::Failed(), d, STRING(COULD_NOT_OPEN_TARGET_FILE) + e.getError());
+ aSource->setDownload(NULL);
+ QueueManager::getInstance()->putDownload(d, false);
+ removeConnection(aSource);
+ return false;
+ } catch(const Exception& e) {
+ delete file;
+ removeDownload(d);
+ fire(DownloadManagerListener::Failed(), d, e.getError());
+ aSource->setDownload(NULL);
+ QueueManager::getInstance()->putDownload(d, false);
+ removeConnection(aSource);
+ return false;
+ }
+
+ d->setFile(file);
+
+
+ if(SETTING(BUFFER_SIZE) > 0 ) {
+ d->setFile(new BufferedOutputStream<true>(d->getFile()));
+ }
+
+ bool sfvcheck = BOOLSETTING(SFV_CHECK) && (d->getPos() == 0) && (SFVReader(d->getTarget()).hasCRC());
+
+ if(sfvcheck) {
+ d->setFlag(Download::FLAG_CALC_CRC32);
+ Download::CrcOS* crc = new Download::CrcOS(d->getFile());
+ d->setCrcCalc(crc);
+ d->setFile(crc);
+ }
+
+ /** @todo something when resuming... */
+ if(d->getTreeValid()) {
+ if((d->getPos() % d->getTigerTree().getBlockSize()) == 0) {
+ d->setFile(new MerkleCheckOutputStream<TigerTree, true>(d->getTigerTree(), d->getFile(), d->getPos()));
+ }
+ }
+ if(d->isSet(Download::FLAG_ROLLBACK)) {
+ d->setFile(new RollbackOutputStream<true>(file, d->getFile(), (size_t)min((int64_t)SETTING(ROLLBACK), d->getSize() - d->getPos())));
+ }
+
+ }
+
+ if(z) {
+ d->setFlag(Download::FLAG_ZDOWNLOAD);
+ d->setFile(new FilteredOutputStream<UnZFilter, true>(d->getFile()));
+ }
+
+ dcassert(d->getPos() != -1);
+ d->setStart(GET_TICK());
+ aSource->setState(UserConnection::STATE_DONE);
+
+ fire(DownloadManagerListener::Starting(), d);
+
+ return true;
+}
+
+void DownloadManager::on(UserConnectionListener::Data, UserConnection* aSource, const u_int8_t* aData, size_t aLen) throw() {
+ Download* d = aSource->getDownload();
+ dcassert(d != NULL);
+
+ try {
+ d->addPos(d->getFile()->write(aData, aLen), aLen);
+
+ if(d->getPos() > d->getSize()) {
+ throw Exception(STRING(TOO_MUCH_DATA));
+ } else if(d->getPos() == d->getSize()) {
+ handleEndData(aSource);
+ aSource->setLineMode();
+ }
+ } catch(const RollbackException& e) {
+ string target = d->getTarget();
+ QueueManager::getInstance()->removeSource(target, aSource->getUser(), QueueItem::Source::FLAG_ROLLBACK_INCONSISTENCY);
+ removeDownload(d);
+ fire(DownloadManagerListener::Failed(), d, e.getError());
+
+ d->resetPos();
+ aSource->setDownload(NULL);
+ QueueManager::getInstance()->putDownload(d, false);
+ removeConnection(aSource);
+ return;
+ } catch(const FileException& e) {
+ removeDownload(d);
+ fire(DownloadManagerListener::Failed(), d, e.getError());
+
+ d->resetPos();
+ aSource->setDownload(NULL);
+ QueueManager::getInstance()->putDownload(d, false);
+ removeConnection(aSource);
+ return;
+ } catch(const Exception& e) {
+ removeDownload(d);
+ fire(DownloadManagerListener::Failed(), d, e.getError());
+ // Nuke the bytes we have written, this is probably a compression error
+ d->resetPos();
+ aSource->setDownload(NULL);
+ QueueManager::getInstance()->putDownload(d, false);
+ removeConnection(aSource);
+ return;
+ }
+}
+
+/** Download finished! */
+void DownloadManager::handleEndData(UserConnection* aSource) {
+
+ dcassert(aSource->getState() == UserConnection::STATE_DONE);
+ Download* d = aSource->getDownload();
+ dcassert(d != NULL);
+
+ if(d->isSet(Download::FLAG_TREE_DOWNLOAD)) {
+ d->getFile()->flush();
+ delete d->getFile();
+ d->setFile(NULL);
+
+ int64_t bl = 1024;
+ while(bl * (int64_t)d->getTigerTree().getLeaves().size() < d->getTigerTree().getFileSize())
+ bl *= 2;
+ d->getTigerTree().setBlockSize(bl);
+ d->getTigerTree().calcRoot();
+
+ if(!(*d->getTTH() == d->getTigerTree().getRoot())) {
+ // This tree is for a different file, remove from queue...
+ removeDownload(d);
+ fire(DownloadManagerListener::Failed(), d, STRING(INVALID_TREE));
+
+ string target = d->getTarget();
+
+ aSource->setDownload(NULL);
+ QueueManager::getInstance()->putDownload(d, false);
+
+ QueueManager::getInstance()->removeSource(target, aSource->getUser(), QueueItem::Source::FLAG_BAD_TREE, false);
+ checkDownloads(aSource);
+ return;
+ }
+ d->setTreeValid(true);
+ } else {
+
+ // Hm, if the real crc == 0, we'll get a file reread extra, but what the heck...
+ u_int32_t crc = 0;
+
+ // First, finish writing the file (flushing the buffers and closing the file...)
+ try {
+ d->getFile()->flush();
+ if(d->getCrcCalc() != NULL)
+ crc = d->getCrcCalc()->getFilter().getValue();
+ delete d->getFile();
+ d->setFile(NULL);
+ d->setCrcCalc(NULL);
+
+ // Check if we're anti-fragging...
+ if(d->isSet(Download::FLAG_ANTI_FRAG)) {
+ // Ok, rename the file to what we expect it to be...
+ try {
+ const string& tgt = d->getTempTarget().empty() ? d->getTarget() : d->getTempTarget();
+ File::renameFile(d->getDownloadTarget(), tgt);
+ d->unsetFlag(Download::FLAG_ANTI_FRAG);
+ } catch(const FileException& e) {
+ dcdebug("AntiFrag: %s\n", e.getError().c_str());
+ // Now what?
+ }
+ }
+ } catch(const FileException& e) {
+ removeDownload(d);
+ fire(DownloadManagerListener::Failed(), d, e.getError());
+
+ aSource->setDownload(NULL);
+ QueueManager::getInstance()->putDownload(d, false);
+ removeConnection(aSource);
+ return;
+ }
+
+ dcassert(d->getPos() == d->getSize());
+ dcdebug("Download finished: %s, size " I64_FMT ", downloaded " I64_FMT "\n", d->getTarget().c_str(), d->getSize(), d->getTotal());
+
+ // Check if we have some crc:s...
+ if(BOOLSETTING(SFV_CHECK)) {
+ if(!checkSfv(aSource, d, crc))
+ return;
+ }
+
+ if(BOOLSETTING(LOG_DOWNLOADS) && (BOOLSETTING(LOG_FILELIST_TRANSFERS) || !d->isSet(Download::FLAG_USER_LIST)) && !d->isSet(Download::FLAG_TREE_DOWNLOAD)) {
+ logDownload(aSource, d);
+ }
+
+ // Check if we need to move the file
+ if( !d->getTempTarget().empty() && (Util::stricmp(d->getTarget().c_str(), d->getTempTarget().c_str()) != 0) ) {
+ moveFile(d->getTempTarget(), d->getTarget());
+ }
+ }
+
+ removeDownload(d);
+ fire(DownloadManagerListener::Complete(), d);
+
+ aSource->setDownload(NULL);
+ QueueManager::getInstance()->putDownload(d, true);
+ checkDownloads(aSource);
+}
+
+u_int32_t DownloadManager::calcCrc32(const string& file) throw(FileException) {
+ File ff(file, File::READ, File::OPEN);
+ CalcInputStream<CRC32Filter, false> f(&ff);
+
+ const size_t BUF_SIZE = 1024*1024;
+ AutoArray<u_int8_t> b(BUF_SIZE);
+ size_t n = BUF_SIZE;
+ while(f.read((u_int8_t*)b, n) > 0)
+ ; // Keep on looping...
+
+ return f.getFilter().getValue();
+}
+
+bool DownloadManager::checkSfv(UserConnection* aSource, Download* d, u_int32_t crc) {
+ SFVReader sfv(d->getTarget());
+ if(sfv.hasCRC()) {
+ bool crcMatch = (crc == sfv.getCRC());
+ if(!crcMatch && crc == 0) {
+ // Blah. We have to reread the file...
+ try {
+ crcMatch = (calcCrc32(d->getDownloadTarget()) == sfv.getCRC());
+ } catch(const FileException& ) {
+ // Couldn't read the file to get the CRC(!!!)
+ crcMatch = false;
+ }
+ }
+
+ if(!crcMatch) {
+ File::deleteFile(d->getDownloadTarget());
+ dcdebug("DownloadManager: CRC32 mismatch for %s\n", d->getTarget().c_str());
+ LogManager::getInstance()->message(STRING(SFV_INCONSISTENCY) + " (" + STRING(FILE) + ": " + d->getTarget() + ")");
+ removeDownload(d);
+ fire(DownloadManagerListener::Failed(), d, STRING(SFV_INCONSISTENCY));
+
+ string target = d->getTarget();
+
+ aSource->setDownload(NULL);
+ QueueManager::getInstance()->putDownload(d, false);
+
+ QueueManager::getInstance()->removeSource(target, aSource->getUser(), QueueItem::Source::FLAG_CRC_WARN, false);
+ checkDownloads(aSource);
+ return false;
+ }
+
+ d->setFlag(Download::FLAG_CRC32_OK);
+
+ dcdebug("DownloadManager: CRC32 match for %s\n", d->getTarget().c_str());
+ }
+ return true;
+}
+
+void DownloadManager::logDownload(UserConnection* aSource, Download* d) {
+ StringMap params;
+ params["target"] = d->getTarget();
+ params["user"] = aSource->getUser()->getNick();
+ params["userip"] = aSource->getRemoteIp();
+ params["hub"] = aSource->getUser()->getLastHubName();
+ params["hubip"] = aSource->getUser()->getLastHubAddress();
+ params["size"] = Util::toString(d->getSize());
+ params["sizeshort"] = Util::formatBytes(d->getSize());
+ params["chunksize"] = Util::toString(d->getTotal());
+ params["chunksizeshort"] = Util::formatBytes(d->getTotal());
+ params["actualsize"] = Util::toString(d->getActual());
+ params["actualsizeshort"] = Util::formatBytes(d->getActual());
+ params["speed"] = Util::formatBytes(d->getAverageSpeed()) + "/s";
+ params["time"] = Util::formatSeconds((GET_TICK() - d->getStart()) / 1000);
+ params["sfv"] = Util::toString(d->isSet(Download::FLAG_CRC32_OK) ? 1 : 0);
+ TTHValue *hash = d->getTTH();
+ if(hash != NULL) {
+ params["tth"] = d->getTTH()->toBase32();
+ }
+ LOG(LogManager::DOWNLOAD, params);
+}
+
+void DownloadManager::moveFile(const string& source, const string& target) {
+ try {
+ File::ensureDirectory(target);
+ if(File::getSize(source) > MOVER_LIMIT) {
+ mover.moveFile(source, target);
+ } else {
+ File::renameFile(source, target);
+ }
+ } catch(const FileException&) {
+ try {
+ if(!SETTING(DOWNLOAD_DIRECTORY).empty()) {
+ File::renameFile(source, SETTING(DOWNLOAD_DIRECTORY) + Util::getFileName(target));
+ } else {
+ File::renameFile(source, Util::getFilePath(source) + Util::getFileName(target));
+ }
+ } catch(const FileException&) {
+ try {
+ File::renameFile(source, Util::getFilePath(source) + Util::getFileName(target));
+ } catch(const FileException&) {
+ // Ignore...
+ }
+ }
+ }
+
+}
+
+void DownloadManager::on(UserConnectionListener::MaxedOut, UserConnection* aSource) throw() {
+ noSlots(aSource);
+}
+void DownloadManager::noSlots(UserConnection* aSource) {
+ if(aSource->getState() != UserConnection::STATE_FILELENGTH && aSource->getState() != UserConnection::STATE_TREE) {
+ dcdebug("DM::onMaxedOut Bad state, ignoring\n");
+ return;
+ }
+
+ Download* d = aSource->getDownload();
+ dcassert(d != NULL);
+
+ removeDownload(d);
+ fire(DownloadManagerListener::Failed(), d, STRING(NO_SLOTS_AVAILABLE));
+
+ aSource->setDownload(NULL);
+ QueueManager::getInstance()->putDownload(d, false);
+ removeConnection(aSource, false, !aSource->isSet(UserConnection::FLAG_NMDC));
+}
+
+void DownloadManager::on(UserConnectionListener::Failed, UserConnection* aSource, const string& aError) throw() {
+ Download* d = aSource->getDownload();
+
+ if(d == NULL) {
+ removeConnection(aSource);
+ return;
+ }
+
+ removeDownload(d);
+ fire(DownloadManagerListener::Failed(), d, aError);
+
+ string target = d->getTarget();
+ aSource->setDownload(NULL);
+ QueueManager::getInstance()->putDownload(d, false);
+ removeConnection(aSource);
+}
+
+void DownloadManager::removeDownload(Download* d) {
+ if(d->getFile()) {
+ if(d->getActual() > 0) {
+ try {
+ d->getFile()->flush();
+ } catch(const Exception&) {
+ }
+ }
+ delete d->getFile();
+ d->setFile(NULL);
+ d->setCrcCalc(NULL);
+
+ if(d->isSet(Download::FLAG_ANTI_FRAG)) {
+ // Ok, set the pos to whereever it was last writing and hope for the best...
+ d->unsetFlag(Download::FLAG_ANTI_FRAG);
+ }
+ }
+
+ {
+ Lock l(cs);
+ // Either I'm stupid or the msvc7 optimizer is doing something _very_ strange here...
+ // STL-port -D_STL_DEBUG complains that .begin() and .end() don't have the same owner (!),
+ // but only in release build
+
+ dcassert(find(downloads.begin(), downloads.end(), d) != downloads.end());
+
+ // downloads.erase(find(downloads.begin(), downloads.end(), d));
+
+ for(Download::Iter i = downloads.begin(); i != downloads.end(); ++i) {
+ if(*i == d) {
+ downloads.erase(i);
+ break;
+ }
+ }
+ }
+}
+
+void DownloadManager::abortDownload(const string& aTarget) {
+ Lock l(cs);
+ for(Download::Iter i = downloads.begin(); i != downloads.end(); ++i) {
+ Download* d = *i;
+ if(d->getTarget() == aTarget) {
+ dcassert(d->getUserConnection() != NULL);
+ d->getUserConnection()->disconnect();
+ break;
+ }
+ }
+}
+
+void DownloadManager::on(UserConnectionListener::FileNotAvailable, UserConnection* aSource) throw() {
+ fileNotAvailable(aSource);
+}
+
+/** @todo Handle errors better */
+void DownloadManager::on(AdcCommand::STA, UserConnection* aSource, const AdcCommand& cmd) throw() {
+ if(cmd.getParameters().size() < 2) {
+ aSource->disconnect();
+ return;
+ }
+
+ const string& err = cmd.getParameters()[0];
+ if(err.length() < 3) {
+ aSource->disconnect();
+ return;
+ }
+
+ switch(Util::toInt(err.substr(0, 1))) {
+ case AdcCommand::SEV_FATAL:
+ aSource->disconnect();
+ return;
+ case AdcCommand::SEV_RECOVERABLE:
+ switch(Util::toInt(err.substr(1))) {
+ case AdcCommand::ERROR_FILE_NOT_AVAILABLE:
+ fileNotAvailable(aSource);
+ return;
+ case AdcCommand::ERROR_SLOTS_FULL:
+ noSlots(aSource);
+ return;
+ }
+ }
+ aSource->disconnect();
+}
+
+void DownloadManager::fileNotAvailable(UserConnection* aSource) {
+ Download* d = aSource->getDownload();
+ dcassert(d != NULL);
+ dcdebug("File Not Available: %s\n", d->getTarget().c_str());
+
+ if(d->getFile()) {
+ delete d->getFile();
+ d->setFile(NULL);
+ d->setCrcCalc(NULL);
+ }
+
+ removeDownload(d);
+ fire(DownloadManagerListener::Failed(), d, d->getTargetFileName() + ": " + STRING(FILE_NOT_AVAILABLE));
+
+ aSource->setDownload(NULL);
+
+ QueueManager::getInstance()->removeSource(d->getTarget(), aSource->getUser(), d->isSet(Download::FLAG_TREE_DOWNLOAD) ? QueueItem::Source::FLAG_NO_TREE : QueueItem::Source::FLAG_FILE_NOT_AVAILABLE, false);
+
+ QueueManager::getInstance()->putDownload(d, false);
+ checkDownloads(aSource);
+}
+
+
+/**
+ * @file
+ * $Id: DownloadManager.cpp,v 1.2 2005/08/21 14:03:43 olof Exp $
+ */
diff --git a/dcpp/DownloadManager.h b/dcpp/DownloadManager.h
new file mode 100644
index 0000000..e0b635e
--- /dev/null
+++ b/dcpp/DownloadManager.h
@@ -0,0 +1,281 @@
+/*
+ * Copyright (C) 2001-2005 Jacek Sieka, arnetheduck on gmail point 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 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.
+ */
+
+#if !defined(AFX_DOWNLOADMANAGER_H__D6409156_58C2_44E9_B63C_B58C884E36A3__INCLUDED_)
+#define AFX_DOWNLOADMANAGER_H__D6409156_58C2_44E9_B63C_B58C884E36A3__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+#include "TimerManager.h"
+
+#include "UserConnection.h"
+#include "Singleton.h"
+#include "FilteredFile.h"
+#include "ZUtils.h"
+#include "MerkleTree.h"
+
+class QueueItem;
+class ConnectionQueueItem;
+
+/**
+ * Comes as an argument in the DownloadManagerListener functions.
+ * Use it to retrieve information about the ongoing transfer.
+ */
+class Download : public Transfer, public Flags {
+public:
+ static const string ANTI_FRAG_EXT;
+
+ typedef Download* Ptr;
+ typedef vector<Ptr> List;
+ typedef List::iterator Iter;
+
+ enum {
+ FLAG_USER_LIST = 0x01,
+ FLAG_RESUME = 0x02,
+ FLAG_ROLLBACK = 0x04,
+ FLAG_ZDOWNLOAD = 0x08,
+ FLAG_CALC_CRC32 = 0x10,
+ FLAG_CRC32_OK = 0x20,
+ FLAG_ANTI_FRAG = 0x40,
+ FLAG_UTF8 = 0x80,
+ FLAG_TREE_DOWNLOAD = 0x100,
+ FLAG_TREE_TRIED = 0x200,
+ FLAG_PARTIAL_LIST = 0x400
+ };
+
+ Download() throw();
+ Download(QueueItem* qi) throw();
+
+ virtual ~Download() { }
+
+ /**
+ * @remarks This function is only used from DownloadManager but its
+ * functionality could be useful in TransferView.
+ *
+ * @return Target filename without path.
+ */
+ string getTargetFileName() {
+ string::size_type i = getTarget().rfind('\\');
+ if(i != string::npos) {
+ return getTarget().substr(i + 1);
+ } else {
+ return getTarget();
+ }
+ }
+
+ /** @internal */
+ string getDownloadTarget() {
+ const string& tgt = (getTempTarget().empty() ? getTarget() : getTempTarget());
+ return isSet(FLAG_ANTI_FRAG) ? tgt + ANTI_FRAG_EXT : tgt;
+ }
+
+ /** @internal */
+ TigerTree& getTigerTree() { return tt; }
+ string& getPFS() { return pfs; }
+ /** @internal */
+ AdcCommand getCommand(bool zlib, bool tthf);
+
+ typedef CalcOutputStream<CRC32Filter, true> CrcOS;
+ GETSET(string, source, Source);
+ GETSET(string, target, Target);
+ GETSET(string, tempTarget, TempTarget);
+ GETSET(OutputStream*, file, File);
+ GETSET(CrcOS*, crcCalc, CrcCalc);
+ GETSET(TTHValue*, tth, TTH);
+ GETSET(bool, treeValid, TreeValid);
+
+private:
+ Download(const Download&);
+
+ Download& operator=(const Download&);
+
+ TigerTree tt;
+ string pfs;
+};
+
+
+/**
+ * Use this listener interface to get progress information for downloads.
+ *
+ * @remarks All methods are sending a pointer to a Download but the receiver
+ * (TransferView) is not using any of the methods in Download, only methods
+ * from its super class, Transfer. The listener functions should send Transfer
+ * objects instead.
+ *
+ * Changing this will will cause a problem with Download::List which is used
+ * in the on Tick function. One solution is reimplement on Tick to call once
+ * for every Downloads, sending one Download at a time. But maybe updating the
+ * GUI is not DownloadManagers problem at all???
+ */
+class DownloadManagerListener {
+public:
+ template<int I> struct X { enum { TYPE = I }; };
+
+ typedef X<0> Complete;
+ typedef X<1> Failed;
+ typedef X<2> Starting;
+ typedef X<3> Tick;
+
+ /**
+ * This is the first message sent before a download starts.
+ * No other messages will be sent before.
+ */
+ virtual void on(Starting, Download*) throw() { };
+
+ /**
+ * Sent once a second if something has actually been downloaded.
+ */
+ virtual void on(Tick, const Download::List&) throw() { };
+
+ /**
+ * This is the last message sent before a download is deleted.
+ * No more messages will be sent after it.
+ */
+ virtual void on(Complete, Download*) throw() { };
+
+ /**
+ * This indicates some sort of failure with a particular download.
+ * No more messages will be sent after it.
+ *
+ * @remarks Should send an error code instead of a string and let the GUI
+ * display an error string.
+ */
+ virtual void on(Failed, Download*, const string&) throw() { };
+};
+
+
+/**
+ * Singleton. Use its listener interface to update the download list
+ * in the user interface.
+ */
+class DownloadManager : public Speaker<DownloadManagerListener>,
+ private UserConnectionListener, private TimerManagerListener,
+ public Singleton<DownloadManager>
+{
+public:
+
+ /** @internal */
+ void addConnection(UserConnection::Ptr conn) {
+ conn->addListener(this);
+ checkDownloads(conn);
+ }
+
+ /** @internal */
+ void abortDownload(const string& aTarget);
+
+ /**
+ * @remarks This is only used in the tray icons. In MainFrame this is
+ * calculated instead so there seems to be a little duplication of code.
+ *
+ * @return Average download speed in Bytes/s
+ */
+ int getAverageSpeed() {
+ Lock l(cs);
+ int avg = 0;
+ for(Download::Iter i = downloads.begin(); i != downloads.end(); ++i) {
+ Download* d = *i;
+ avg += (int)d->getRunningAverage();
+ }
+ return avg;
+ }
+
+ /** @return Number of downloads. */
+ size_t getDownloadCount() {
+ Lock l(cs);
+ return downloads.size();
+ }
+
+private:
+ enum { MOVER_LIMIT = 10*1024*1024 };
+ class FileMover : public Thread {
+ public:
+ FileMover() : active(false) { };
+ virtual ~FileMover() { join(); };
+
+ void moveFile(const string& source, const string& target);
+ virtual int run();
+ private:
+ typedef pair<string, string> FilePair;
+ typedef vector<FilePair> FileList;
+ typedef FileList::iterator FileIter;
+
+ bool active;
+
+ FileList files;
+ CriticalSection cs;
+ } mover;
+
+ CriticalSection cs;
+ Download::List downloads;
+
+ bool checkRollback(Download* aDownload, const u_int8_t* aBuf, int aLen) throw(FileException);
+ void removeConnection(UserConnection::Ptr aConn, bool reuse = false, bool ntd = false);
+ void removeDownload(Download* aDown);
+ void fileNotAvailable(UserConnection* aSource);
+ void noSlots(UserConnection* aSource);
+
+ void moveFile(const string& source, const string&target);
+ void logDownload(UserConnection* aSource, Download* d);
+ u_int32_t calcCrc32(const string& file) throw(FileException);
+ bool checkSfv(UserConnection* aSource, Download* d, u_int32_t crc);
+ int64_t getResumePos(const string& file, const TigerTree& tt, int64_t startPos);
+
+ friend class Singleton<DownloadManager>;
+ DownloadManager() {
+ TimerManager::getInstance()->addListener(this);
+ };
+
+ virtual ~DownloadManager() throw() {
+ TimerManager::getInstance()->removeListener(this);
+ while(true) {
+ {
+ Lock l(cs);
+ if(downloads.empty())
+ break;
+ }
+ Thread::sleep(100);
+ }
+ };
+
+ void checkDownloads(UserConnection* aConn);
+ void handleEndData(UserConnection* aSource);
+
+ // UserConnectionListener
+ virtual void on(Data, UserConnection*, const u_int8_t*, size_t) throw();
+ virtual void on(Failed, UserConnection*, const string&) throw();
+ virtual void on(Sending, UserConnection*, int64_t) throw();
+ virtual void on(FileLength, UserConnection*, int64_t) throw();
+ virtual void on(MaxedOut, UserConnection*) throw();
+ virtual void on(FileNotAvailable, UserConnection*) throw();
+
+ virtual void on(AdcCommand::SND, UserConnection*, const AdcCommand&) throw();
+ virtual void on(AdcCommand::STA, UserConnection*, const AdcCommand&) throw();
+
+ bool prepareFile(UserConnection* aSource, int64_t newSize, bool z);
+ // TimerManagerListener
+ virtual void on(TimerManagerListener::Second, u_int32_t aTick) throw();
+};
+
+#endif // !defined(AFX_DOWNLOADMANAGER_H__D6409156_58C2_44E9_B63C_B58C884E36A3__INCLUDED_)
+
+/**
+ * @file
+ * $Id: DownloadManager.h,v 1.2 2005/08/21 14:03:43 olof Exp $
+ */
diff --git a/dcpp/Encoder.cpp b/dcpp/Encoder.cpp
new file mode 100644
index 0000000..e684ce5
--- /dev/null
+++ b/dcpp/Encoder.cpp
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2001-2005 Jacek Sieka, arnetheduck on gmail point 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 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.
+ */
+
+#include "stdinc.h"
+#include "DCPlusPlus.h"
+
+#include "Encoder.h"
+
+const int8_t Encoder::base32Table[] = {
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+ -1,-1,26,27,28,29,30,31,-1,-1,-1,-1,-1,-1,-1,-1,
+ -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,
+ 15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,
+ -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,
+ 15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+};
+
+const char Encoder::base32Alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
+
+string& Encoder::toBase32(const u_int8_t* src, size_t len, string& dst) {
+ // Code snagged from the bitzi bitcollider
+ size_t i, index;
+ u_int8_t word;
+ dst.reserve(((len * 8) / 5) + 1);
+
+ for(i = 0, index = 0; i < len;) {
+ /* Is the current word going to span a byte boundary? */
+ if (index > 3) {
+ word = (u_int8_t)(src[i] & (0xFF >> index));
+ index = (index + 5) % 8;
+ word <<= index;
+ if ((i + 1) < len)
+ word |= src[i + 1] >> (8 - index);
+
+ i++;
+ } else {
+ word = (u_int8_t)(src[i] >> (8 - (index + 5))) & 0x1F;
+ index = (index + 5) % 8;
+ if (index == 0)
+ i++;
+ }
+
+ dcassert(word < 32);
+ dst += base32Alphabet[word];
+ }
+ return dst;
+}
+
+void Encoder::fromBase32(const char* src, u_int8_t* dst, size_t len) {
+ size_t i, index, offset;
+
+ memset(dst, 0, len);
+ for(i = 0, index = 0, offset = 0; src[i]; i++) {
+ // Skip what we don't recognise
+ int8_t tmp = base32Table[(unsigned char)src[i]];
+
+ if(tmp == -1)
+ continue;
+
+ if (index <= 3) {
+ index = (index + 5) % 8;
+ if (index == 0) {
+ dst[offset] |= tmp;
+ offset++;
+ if(offset == len)
+ break;
+ } else {
+ dst[offset] |= tmp << (8 - index);
+ }
+ } else {
+ index = (index + 5) % 8;
+ dst[offset] |= (tmp >> index);
+ offset++;
+ if(offset == len)
+ break;
+ dst[offset] |= tmp << (8 - index);
+ }
+ }
+}
+
+/**
+ * @file
+ * $Id: Encoder.cpp,v 1.2 2005/08/21 14:03:43 olof Exp $
+ */
diff --git a/dcpp/Encoder.h b/dcpp/Encoder.h
new file mode 100644
index 0000000..0ffd22f
--- /dev/null
+++ b/dcpp/Encoder.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2001-2005 Jacek Sieka, arnetheduck on gmail point 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 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.
+ */
+
+#ifndef _ENCODER
+#define _ENCODER
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+class Encoder
+{
+public:
+ static string& toBase32(const u_int8_t* src, size_t len, string& tgt);
+ static string toBase32(const u_int8_t* src, size_t len) {
+ string tmp;
+ return toBase32(src, len, tmp);
+ }
+ static void fromBase32(const char* src, u_int8_t* dst, size_t len);
+private:
+ static const int8_t base32Table[];
+ static const char base32Alphabet[];
+};
+
+#endif // _ENCODER
+
+/**
+ * @file
+ * $Id: Encoder.h,v 1.2 2005/08/21 14:03:43 olof Exp $
+ */
diff --git a/dcpp/Exception.cpp b/dcpp/Exception.cpp
new file mode 100644
index 0000000..f3cd811
--- /dev/null
+++ b/dcpp/Exception.cpp
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2001-2003 Jacek Sieka, j_s@telia.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 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.
+ */
+
+#include "stdinc.h"
+#include "DCPlusPlus.h"
+
+#include "Exception.h"
+
+/**
+ * @file
+ * $Id: Exception.cpp,v 1.2 2005/08/21 14:03:43 olof Exp $
+ */
+
diff --git a/dcpp/Exception.h b/dcpp/Exception.h
new file mode 100644
index 0000000..d6d4538
--- /dev/null
+++ b/dcpp/Exception.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2001-2005 Jacek Sieka, arnetheduck on gmail point 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 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.
+ */
+
+#ifndef EXCEPTION_H
+#define EXCEPTION_H
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+class Exception
+{
+public:
+ Exception() { };
+ Exception(const string& aError) throw() : error(aError) { dcdrun(if(error.size()>0)) dcdebug("Thrown: %s\n", error.c_str()); };
+ virtual ~Exception() throw() { };
+ virtual const string& getError() const throw() { return error; };
+protected:
+ string error;
+};
+
+#ifdef _DEBUG
+
+#define STANDARD_EXCEPTION(name) class name : public Exception { \
+public:\
+ name() throw() : Exception(#name) { } \
+ name(const string& aError) throw() : Exception(#name ": " + aError) { } \
+ virtual ~name() throw() { } \
+}
+
+#else // _DEBUG
+
+#define STANDARD_EXCEPTION(name) class name : public Exception { \
+public:\
+ name() throw() : Exception() { } \
+ name(const string& aError) throw() : Exception(aError) { } \
+ virtual ~name() throw() { } \
+}
+#endif
+
+#endif // EXCEPTION_H
+
+/**
+ * @file
+ * $Id: Exception.h,v 1.2 2005/08/21 14:03:43 olof Exp $
+ */
diff --git a/dcpp/FastAlloc.h b/dcpp/FastAlloc.h
new file mode 100644
index 0000000..a727e50
--- /dev/null
+++ b/dcpp/FastAlloc.h
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2001-2005 Jacek Sieka, arnetheduck on gmail point 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 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.
+ */
+
+#ifndef FASTALLOC_H
+#define FASTALLOC_H
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+#include "CriticalSection.h"
+
+#ifndef _DEBUG
+struct FastAllocBase {
+ static FastCriticalSection cs;
+};
+
+/**
+ * Fast new/delete replacements for constant sized objects, that also give nice
+ * reference locality...
+ */
+template<class T>
+struct FastAlloc : public FastAllocBase {
+ // Custom new & delete that (hopefully) use the node allocator
+ static void* operator new(size_t s) {
+ if(s != sizeof(T))
+ return ::operator new(s);
+ return allocate();
+ }
+
+ // Avoid hiding placement new that's needed by the stl containers...
+ static void* operator new(size_t, void* m) {
+ return m;
+ }
+ // ...and the warning about missing placement delete...
+ static void operator delete(void*, void*) {
+ // ? We didn't allocate so...
+ }
+
+ static void operator delete(void* m, size_t s) {
+ if (s != sizeof(T)) {
+ ::operator delete(m);
+ } else if(m != NULL) {
+ deallocate((u_int8_t*)m);
+ }
+ }
+private:
+
+ static void* allocate() {
+ FastLock l(cs);
+ if(freeList == NULL) {
+ grow();
+ }
+ void* tmp = freeList;
+ freeList = *((void**)freeList);
+ return tmp;
+ }
+
+ static void deallocate(void* p) {
+ FastLock l(cs);
+ *(void**)p = freeList;
+ freeList = p;
+ }
+
+ static void* freeList;
+
+ static void grow() {
+ dcassert(sizeof(T) >= sizeof(void*));
+ // We want to grow by approximately 128kb at a time...
+ size_t items = ((128*1024 + sizeof(T) - 1)/sizeof(T));
+ freeList = new u_int8_t[sizeof(T)*items];
+ u_int8_t* tmp = (u_int8_t*)freeList;
+ for(size_t i = 0; i < items - 1; i++) {
+ *(void**)tmp = tmp + sizeof(T);
+ tmp += sizeof(T);
+ }
+ *(void**)tmp = NULL;
+ }
+};
+template<class T> void* FastAlloc<T>::freeList = NULL;
+#else
+template<class T> struct FastAlloc { };
+#endif
+
+#endif // FASTALLOC_H
+
+/**
+ * @file
+ * $Id: FastAlloc.h,v 1.2 2005/08/21 14:03:43 olof Exp $
+ */
diff --git a/dcpp/FavoriteUser.h b/dcpp/FavoriteUser.h
new file mode 100644
index 0000000..d1697cf
--- /dev/null
+++ b/dcpp/FavoriteUser.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2001-2005 Jacek Sieka, arnetheduck on gmail point 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 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.
+ */
+
+#if !defined(AFX_FAVORITEUSER_H__64E4A69E_BB58_425D_830C_ADD1760E29A4__INCLUDED_)
+#define AFX_FAVORITEUSER_H__64E4A69E_BB58_425D_830C_ADD1760E29A4__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+#include "FastAlloc.h"
+
+class FavoriteUser : public Flags, public FastAlloc<FavoriteUser>
+{
+public:
+ typedef FavoriteUser* Ptr;
+
+ enum Flags {
+ FLAG_GRANTSLOT = 1 << 0
+ };
+
+ GETSET(string, description, Description);
+ GETSET(u_int32_t, lastSeen, LastSeen);
+
+ FavoriteUser() : lastSeen(0) {}
+ ~FavoriteUser() {}
+};
+
+#endif // !defined(AFX_FAVORITEUSER_H__64E4A69E_BB58_425D_830C_ADD1760E29A4__INCLUDED_)
+
+/**
+ * @file
+ * $Id: FavoriteUser.h,v 1.2 2005/08/21 14:03:43 olof Exp $
+ */
diff --git a/dcpp/File.h b/dcpp/File.h
new file mode 100644
index 0000000..7464e5e
--- /dev/null
+++ b/dcpp/File.h
@@ -0,0 +1,426 @@
+/*
+ * Copyright (C) 2001-2005 Jacek Sieka, arnetheduck on gmail point 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 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.
+ */
+
+#ifndef FILE_H
+#define FILE_H
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+#include "SettingsManager.h"
+
+#include "Util.h"
+#include "Text.h"
+#include "Streams.h"
+
+#ifndef _WIN32
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#endif
+
+#ifdef _WIN32
+#include "../zlib/zlib.h"
+#else
+#include <zlib.h>
+#endif
+
+class File : public IOStream {
+public:
+ enum {
+ OPEN = 0x01,
+ CREATE = 0x02,
+ TRUNCATE = 0x04
+ };
+
+#ifdef _WIN32
+ enum {
+ READ = GENERIC_READ,
+ WRITE = GENERIC_WRITE,
+ RW = READ | WRITE
+ };
+
+ File(const string& aFileName, int access, int mode) throw(FileException) {
+ dcassert(access == WRITE || access == READ || access == (READ | WRITE));
+
+ int m = 0;
+ if(mode & OPEN) {
+ if(mode & CREATE) {
+ m = (mode & TRUNCATE) ? CREATE_ALWAYS : OPEN_ALWAYS;
+ } else {
+ m = (mode & TRUNCATE) ? TRUNCATE_EXISTING : OPEN_EXISTING;
+ }
+ } else {
+ if(mode & CREATE) {
+ m = (mode & TRUNCATE) ? CREATE_ALWAYS : CREATE_NEW;
+ } else {
+ dcassert(0);
+ }
+ }
+
+ h = ::CreateFile(Text::utf8ToWide(aFileName).c_str(), access, FILE_SHARE_READ, NULL, m, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
+
+ if(h == INVALID_HANDLE_VALUE) {
+ throw FileException(Util::translateError(GetLastError()));
+ }
+
+ }
+
+ u_int32_t getLastModified() {
+ FILETIME f = {0};
+ ::GetFileTime(h, NULL, NULL, &f);
+ return convertTime(&f);
+ }
+
+ static u_int32_t convertTime(FILETIME* f) {
+ SYSTEMTIME s = { 1970, 1, 0, 1, 0, 0, 0, 0 };
+ FILETIME f2 = {0};
+ if(::SystemTimeToFileTime(&s, &f2)) {
+ u_int64_t* a = (u_int64_t*)f;
+ u_int64_t* b = (u_int64_t*)&f2;
+ *a -= *b;
+ *a /= (1000LL*1000LL*1000LL/100LL); // 100ns > s
+ return (u_int32_t)*a;
+ }
+ return 0;
+ }
+
+ bool isOpen() { return h != INVALID_HANDLE_VALUE; };
+
+ virtual void close() throw() {
+ if(isOpen()) {
+ CloseHandle(h);
+ h = INVALID_HANDLE_VALUE;
+ }
+ }
+
+ virtual int64_t getSize() throw() {
+ DWORD x;
+ DWORD l = ::GetFileSize(h, &x);
+
+ if( (l == INVALID_FILE_SIZE) && (GetLastError() != NO_ERROR))
+ return -1;
+
+ return (int64_t)l | ((int64_t)x)<<32;
+ }
+
+ virtual void setSize(int64_t newSize) throw(FileException) {
+ int64_t pos = getPos();
+ setPos(newSize);
+ setEOF();
+ setPos(pos);
+ }
+
+ virtual int64_t getPos() throw() {
+ LONG x = 0;
+ DWORD l = ::SetFilePointer(h, 0, &x, FILE_CURRENT);
+
+ return (int64_t)l | ((int64_t)x)<<32;
+ }
+
+ virtual void setPos(int64_t pos) throw() {
+ LONG x = (LONG) (pos>>32);
+ ::SetFilePointer(h, (DWORD)(pos & 0xffffffff), &x, FILE_BEGIN);
+ }
+ virtual void setEndPos(int64_t pos) throw() {
+ LONG x = (LONG) (pos>>32);
+ ::SetFilePointer(h, (DWORD)(pos & 0xffffffff), &x, FILE_END);
+ }
+
+ virtual void movePos(int64_t pos) throw() {
+ LONG x = (LONG) (pos>>32);
+ ::SetFilePointer(h, (DWORD)(pos & 0xffffffff), &x, FILE_CURRENT);
+ }
+
+ virtual size_t read(void* buf, size_t& len) throw(Exception) {
+ DWORD x;
+ if(!::ReadFile(h, buf, (DWORD)len, &x, NULL)) {
+ throw(FileException(Util::translateError(GetLastError())));
+ }
+ len = x;
+ return x;
+ }
+
+ virtual size_t write(const void* buf, size_t len) throw(Exception) {
+ DWORD x;
+ if(!::WriteFile(h, buf, (DWORD)len, &x, NULL)) {
+ throw FileException(Util::translateError(GetLastError()));
+ }
+ dcassert(x == len);
+ return x;
+ }
+ virtual void setEOF() throw(FileException) {
+ dcassert(isOpen());
+ if(!SetEndOfFile(h)) {
+ throw FileException(Util::translateError(GetLastError()));
+ }
+ }
+
+ virtual size_t flush() throw(Exception) {
+ if(isOpen() && !FlushFileBuffers(h))
+ throw FileException(Util::translateError(GetLastError()));
+ return 0;
+ }
+
+ static void deleteFile(const string& aFileName) throw() { ::DeleteFile(Text::toT(aFileName).c_str()); }
+ static void renameFile(const string& source, const string& target) throw(FileException) {
+ if(!::MoveFile(Text::toT(source).c_str(), Text::toT(target).c_str())) {
+ // Can't move, try copy/delete...
+ if(!CopyFile(Text::toT(source).c_str(), Text::toT(target).c_str(), FALSE)) {
+ throw FileException(Util::translateError(GetLastError()));
+ }
+ deleteFile(source);
+ }
+ }
+ static void copyFile(const string& src, const string& target) throw(FileException) {
+ if(!::CopyFile(Text::toT(src).c_str(), Text::toT(target).c_str(), FALSE)) {
+ throw FileException(Util::translateError(GetLastError()));
+ }
+ }
+
+ static int64_t getSize(const string& aFileName) throw() {
+ WIN32_FIND_DATA fd;
+ HANDLE hFind;
+
+ hFind = FindFirstFile(Text::toT(aFileName).c_str(), &fd);
+
+ if (hFind == INVALID_HANDLE_VALUE) {
+ return -1;
+ } else {
+ FindClose(hFind);
+ return ((int64_t)fd.nFileSizeHigh << 32 | (int64_t)fd.nFileSizeLow);
+ }
+ }
+
+ static void ensureDirectory(const string& aFile) {
+ // Skip the first dir...
+ tstring file;
+ Text::toT(aFile, file);
+ wstring::size_type start = file.find_first_of(L"\\/");
+ if(start == string::npos)
+ return;
+ start++;
+ while( (start = file.find_first_of(L"\\/", start)) != string::npos) {
+ CreateDirectory(file.substr(0, start+1).c_str(), NULL);
+ start++;
+ }
+ }
+
+#else // _WIN32
+
+ enum {
+ READ = 0x01,
+ WRITE = 0x02,
+ RW = READ | WRITE
+ };
+ File(const string& aFileName, int access, int mode) throw(FileException) {
+ dcassert(access == WRITE || access == READ || access == (READ | WRITE));
+
+ int m = 0;
+ if(access == READ)
+ m |= O_RDONLY;
+ else if(access == WRITE)
+ m |= O_WRONLY;
+ else
+ m |= O_RDWR;
+
+ if(mode & CREATE) {
+ m |= O_CREAT;
+ }
+ if(mode & TRUNCATE) {
+ m |= O_TRUNC;
+ }
+ h = open(aFileName.c_str(), m, S_IRUSR | S_IWUSR);
+ if(h == -1)
+ throw FileException("Could not open file");
+ }
+
+ u_int32_t getLastModified() {
+ struct stat s;
+ if (::fstat(h, &s) == -1)
+ return 0;
+
+ return (u_int32_t)s.st_mtime;
+ }
+
+ bool isOpen() { return h != -1; };
+
+ virtual void close() throw() {
+ if(h != -1) {
+ ::close(h);
+ h = -1;
+ }
+ }
+
+ virtual int64_t getSize() throw(FileException) {
+ struct stat s;
+ if(::fstat(h, &s) == -1)
+ return -1;
+
+ return (int64_t)s.st_size;
+ }
+
+ virtual int64_t getPos() throw(FileException) {
+ return (int64_t) lseek(h, 0, SEEK_CUR);
+ }
+
+ virtual void setPos(int64_t pos) throw(FileException) { lseek(h, (off_t)pos, SEEK_SET); };
+ virtual void setEndPos(int64_t pos) throw(FileException) { lseek(h, (off_t)pos, SEEK_END); };
+ virtual void movePos(int64_t pos) throw(FileException) { lseek(h, (off_t)pos, SEEK_CUR); };
+
+ virtual size_t read(void* buf, size_t& len) throw(FileException) {
+ ssize_t x = ::read(h, buf, len);
+ if(x == -1)
+ throw("Read error");
+ len = x;
+ return (size_t)x;
+ }
+
+ virtual size_t write(const void* buf, size_t len) throw(FileException) {
+ ssize_t x = ::write(h, buf, len);
+ if(x == -1)
+ throw FileException("Write error");
+ if(x < (ssize_t)len)
+ throw FileException("Disk full(?)");
+ return x;
+ }
+
+ // some ftruncate implementations can't extend files like SetEndOfFile,
+ // not sure if the client code needs this...
+ int extendFile(int64_t len) {
+ char zero;
+
+ if ( (lseek(h,(off_t) len,SEEK_SET) != -1) && (::write(h,&zero,1) != -1) ) {
+ ftruncate(h,(off_t)len);
+ return 1;
+ }
+ return -1;
+ }
+
+ virtual void setEOF() throw(FileException) {
+ int64_t pos;
+ int64_t eof;
+ int ret;
+
+ pos = (int64_t) lseek(h,0,SEEK_CUR);
+ eof = (int64_t) lseek(h,0,SEEK_END);
+ if (eof < pos)
+ ret = extendFile(pos);
+ else
+ ret = ftruncate(h,(off_t)pos);
+ lseek(h,(off_t)pos,SEEK_SET);
+ if (ret == -1)
+ throw FileException(Util::translateError(errno));
+ }
+
+ virtual void setSize(int64_t newSize) throw(FileException) {
+ int64_t pos = getPos();
+ setPos(newSize);
+ setEOF();
+ setPos(pos);
+ }
+
+ virtual size_t flush() throw(Exception) {
+ if(fsync(h) == -1)
+ throw FileException(Util::translateError(errno));
+ return 0;
+ }
+
+ static void deleteFile(const string& aFileName) throw() { ::unlink(aFileName.c_str()); };
+ static void renameFile(const string& source, const string& target) throw() { ::rename(source.c_str(), target.c_str()); };
+
+ // This doesn't assume all bytes are written in one write call, it is a bit safer
+ static void copyFile(const string& source, const string& target) throw(FileException) {
+ const size_t BUF_SIZE = 64 * 1024;
+ AutoArray<char> buffer(BUF_SIZE);
+ size_t count = BUF_SIZE;
+ File src(source, File::READ, 0);
+ File dst(target, File::WRITE, File::CREATE | File::TRUNCATE);
+
+ while ( src.read((char*)buffer, count) > 0) {
+ char* p = (char*)buffer;
+ while (count > 0) {
+ size_t ret = dst.write(p, count);
+ p += ret;
+ count -= ret;
+ }
+ count = BUF_SIZE;
+ }
+ }
+
+ static int64_t getSize(const string& aFileName) {
+ struct stat s;
+ if(stat(aFileName.c_str(), &s) == -1)
+ return -1;
+
+ return s.st_size;
+ }
+
+ static void ensureDirectory(const string& aFile) {
+ string acp = Text::utf8ToAcp(aFile);
+ string::size_type start = 0;
+ while( (start = aFile.find_first_of(L'/', start)) != string::npos) {
+ mkdir(aFile.substr(0, start+1).c_str(), 0755);
+ start++;
+ }
+ }
+
+
+#endif // _WIN32
+
+ virtual ~File() throw() {
+ File::close();
+ }
+
+ string read(size_t len) throw(FileException) {
+ string s(len, 0);
+ size_t x = read(&s[0], len);
+ if(x != len)
+ s.resize(x);
+ return s;
+ }
+
+ string read() throw(FileException) {
+ setPos(0);
+ int64_t sz = getSize();
+ if(sz == -1)
+ return Util::emptyString;
+ return read((u_int32_t)sz);
+ }
+
+ void write(const string& aString) throw(FileException) { write((void*)aString.data(), aString.size()); };
+
+protected:
+#ifdef _WIN32
+ HANDLE h;
+#else
+ int h;
+#endif
+private:
+ File(const File&);
+ File& operator=(const File&);
+};
+
+#endif // FILE_H
+
+/**
+ * @file
+ * $Id: File.h,v 1.2 2005/08/21 14:03:43 olof Exp $
+ */
+
diff --git a/dcpp/FilteredFile.h b/dcpp/FilteredFile.h
new file mode 100644
index 0000000..9c6d9ed
--- /dev/null
+++ b/dcpp/FilteredFile.h
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2001-2005 Jacek Sieka, arnetheduck on gmail point 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 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.
+ */
+
+#ifndef _FILTERED_FILE
+#define _FILTERED_FILE
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+#include "Streams.h"
+
+template<class Filter, bool managed>
+class CalcOutputStream : public OutputStream {
+public:
+ using OutputStream::write;
+
+ CalcOutputStream(OutputStream* aStream) : s(aStream) { }
+ virtual ~CalcOutputStream() throw() { if(managed) delete s; }
+
+ size_t flush() throw(Exception) {
+ return s->flush();
+ }
+
+ size_t write(const void* buf, size_t len) throw(Exception) {
+ filter(buf, len);
+ return s->write(buf, len);
+ }
+
+
+ const Filter& getFilter() const { return filter; }
+private:
+ OutputStream* s;
+ Filter filter;
+};
+
+template<class Filter, bool managed>
+class CalcInputStream : public InputStream {
+public:
+ CalcInputStream(InputStream* aStream) : s(aStream) { }
+ virtual ~CalcInputStream() throw() { if(managed) delete s; }
+
+ size_t read(void* buf, size_t& len) throw(Exception) {
+ size_t x = s->read(buf, len);
+ filter(buf, x);
+ return x;
+ }
+
+ const Filter& getFilter() const { return filter; }
+private:
+ InputStream* s;
+ Filter filter;
+};
+
+template<class Filter, bool manage>
+class FilteredOutputStream : public OutputStream {
+public:
+ using OutputStream::write;
+
+ FilteredOutputStream(OutputStream* aFile) : f(aFile), flushed(false) { }
+ ~FilteredOutputStream() throw() { if(manage) delete f; }
+
+ size_t flush() throw(Exception) {
+ if(flushed)
+ return 0;
+
+ flushed = true;
+ size_t written = 0;
+
+ for(;;) {
+ size_t n = BUF_SIZE;
+ size_t zero = 0;
+ bool more = filter(NULL, zero, buf, n);
+
+ written += f->write(buf, n);
+
+ if(!more)
+ break;
+ }
+ return written + f->flush();
+ }
+
+ size_t write(const void* wbuf, size_t len) throw(Exception) {
+ if(flushed)
+ throw Exception("No filtered writes after flush");
+
+ u_int8_t* wb = (u_int8_t*)wbuf;
+ size_t written = 0;
+ while(len > 0) {
+ size_t n = BUF_SIZE;
+ size_t m = len;
+
+ bool more = filter(wb, m, buf, n);
+ wb += m;
+ len -= m;
+
+ written += f->write(buf, n);
+
+ if(!more) {
+ if(len > 0) {
+ throw Exception("Garbage data after end of stream");
+ }
+ flushed = true;
+ return written;
+ }
+ }
+ return written;
+ }
+
+private:
+ enum { BUF_SIZE = 64*1024 };
+
+ OutputStream* f;
+ Filter filter;
+
+ u_int8_t buf[BUF_SIZE];
+ bool flushed;
+};
+
+template<class Filter, bool managed>
+class FilteredInputStream : public InputStream {
+public:
+ FilteredInputStream(InputStream* aFile) : f(aFile), pos(0), valid(0), more(true) { }
+ virtual ~FilteredInputStream() throw() { if(managed) delete f; }
+
+ /**
+ * Read data through filter, keep calling until len returns 0.
+ * @param rbuf Data buffer
+ * @param len Buffer size on entry, bytes actually read on exit
+ * @return Length of data in buffer
+ */
+ size_t read(void* rbuf, size_t& len) throw(Exception) {
+ u_int8_t* rb = (u_int8_t*)rbuf;
+
+ size_t totalRead = 0;
+ size_t totalProduced = 0;
+
+ while(more && totalProduced < len) {
+ size_t curRead = BUF_SIZE;
+ if(valid == 0) {
+ dcassert(pos == 0);
+ valid = f->read(buf, curRead);
+ totalRead += curRead;
+ }
+
+ size_t n = len - totalProduced;
+ size_t m = valid - pos;
+ more = filter(buf + pos, m, rb, n);
+ pos += m;
+ if(pos == valid) {
+ valid = pos = 0;
+ }
+ totalProduced += n;
+ rb += n;
+ }
+ len = totalRead;
+ return totalProduced;
+ }
+
+private:
+ enum { BUF_SIZE = 64*1024 };
+
+ InputStream* f;
+ Filter filter;
+ u_int8_t buf[BUF_SIZE];
+ size_t pos;
+ size_t valid;
+ bool more;
+};
+
+#endif // _FILTERED_FILE
+
+/**
+* @file
+* $Id: FilteredFile.h,v 1.2 2005/08/21 14:03:43 olof Exp $
+*/
diff --git a/dcpp/FinishedManager.cpp b/dcpp/FinishedManager.cpp
new file mode 100644
index 0000000..38dc5ca
--- /dev/null
+++ b/dcpp/FinishedManager.cpp
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2001-2005 Jacek Sieka, arnetheduck on gmail point 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 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.
+ */
+
+#include "stdinc.h"
+#include "DCPlusPlus.h"
+
+#include "FinishedManager.h"
+
+FinishedManager::~FinishedManager() throw() {
+ Lock l(cs);
+ for_each(downloads.begin(), downloads.end(), DeleteFunction<FinishedItem*>());
+ for_each(uploads.begin(), uploads.end(), DeleteFunction<FinishedItem*>());
+ DownloadManager::getInstance()->removeListener(this);
+ UploadManager::getInstance()->removeListener(this);
+
+}
+
+void FinishedManager::remove(FinishedItem *item, bool upload /* = false */) {
+ {
+ Lock l(cs);
+ FinishedItem::List *listptr = upload ? &uploads : &downloads;
+ FinishedItem::Iter it = find(listptr->begin(), listptr->end(), item);
+
+ if(it != listptr->end())
+ listptr->erase(it);
+ else
+ return;
+ }
+ if (!upload)
+ fire(FinishedManagerListener::RemovedDl(), item);
+ else
+ fire(FinishedManagerListener::RemovedUl(), item);
+ delete item;
+}
+
+void FinishedManager::removeAll(bool upload /* = false */) {
+ {
+ Lock l(cs);
+ FinishedItem::List *listptr = upload ? &uploads : &downloads;
+ for_each(listptr->begin(), listptr->end(), DeleteFunction<FinishedItem*>());
+ listptr->clear();
+ }
+ if (!upload)
+ fire(FinishedManagerListener::RemovedAllDl());
+ else
+ fire(FinishedManagerListener::RemovedAllUl());
+}
+
+void FinishedManager::on(DownloadManagerListener::Complete, Download* d) throw()
+{
+ if(!d->isSet(Download::FLAG_TREE_DOWNLOAD) && (!d->isSet(Download::FLAG_USER_LIST) || BOOLSETTING(LOG_FILELIST_TRANSFERS))) {
+ FinishedItem *item = new FinishedItem(
+ d->getTarget(), d->getUserConnection()->getUser()->getNick(),
+ d->getUserConnection()->getUser()->getLastHubName(),
+ d->getSize(), d->getTotal(), (GET_TICK() - d->getStart()), GET_TIME(), d->isSet(Download::FLAG_CRC32_OK));
+ {
+ Lock l(cs);
+ downloads.push_back(item);
+ }
+
+ fire(FinishedManagerListener::AddedDl(), item);
+ }
+}
+
+void FinishedManager::on(UploadManagerListener::Complete, Upload* u) throw()
+{
+ if(!u->isSet(Upload::FLAG_TTH_LEAVES) && (!u->isSet(Upload::FLAG_USER_LIST) || BOOLSETTING(LOG_FILELIST_TRANSFERS))) {
+ FinishedItem *item = new FinishedItem(
+ u->getLocalFileName(), u->getUserConnection()->getUser()->getNick(),
+ u->getUserConnection()->getUser()->getLastHubName(),
+ u->getSize(), u->getTotal(), (GET_TICK() - u->getStart()), GET_TIME());
+ {
+ Lock l(cs);
+ uploads.push_back(item);
+ }
+
+ fire(FinishedManagerListener::AddedUl(), item);
+ }
+}
+
+/**
+ * @file
+ * $Id: FinishedManager.cpp,v 1.2 2005/08/21 14:03:43 olof Exp $
+ */
diff --git a/dcpp/FinishedManager.h b/dcpp/FinishedManager.h
new file mode 100644
index 0000000..2b1bc3d
--- /dev/null
+++ b/dcpp/FinishedManager.h
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2001-2005 Jacek Sieka, arnetheduck on gmail point 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 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.
+ */
+
+#ifndef FINISHEDMANAGER_H
+#define FINISHEDMANAGER_H
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+#include "DownloadManager.h"
+#include "UploadManager.h"
+
+#include "CriticalSection.h"
+#include "Singleton.h"
+
+class FinishedItem
+{
+public:
+ typedef FinishedItem* Ptr;
+ typedef vector<Ptr> List;
+ typedef List::iterator Iter;
+
+ FinishedItem(string const& aTarget, string const& aUser, string const& aHub,
+ int64_t aSize, int64_t aChunkSize, int64_t aMSeconds, u_int32_t aTime,
+ bool aCrc32 = false) :
+ target(aTarget), user(aUser), hub(aHub), size(aSize), chunkSize(aChunkSize),
+ milliSeconds(aMSeconds), time(aTime), crc32Checked(aCrc32)
+ {
+ }
+
+ int64_t getAvgSpeed() { return milliSeconds > 0 ? (chunkSize * ((int64_t)1000) / milliSeconds) : 0; };
+
+ GETSET(string, target, Target);
+ GETSET(string, user, User);
+ GETSET(string, hub, Hub);
+ GETSET(int64_t, size, Size);
+ GETSET(int64_t, chunkSize, ChunkSize);
+ GETSET(int64_t, milliSeconds, MilliSeconds);
+ GETSET(u_int32_t, time, Time);
+ GETSET(bool, crc32Checked, Crc32Checked)
+private:
+ friend class FinishedManager;
+
+};
+
+class FinishedManagerListener {
+public:
+ template<int I> struct X { enum { TYPE = I }; };
+
+ typedef X<0> AddedUl;
+ typedef X<1> AddedDl;
+ typedef X<2> RemovedUl;
+ typedef X<3> RemovedDl;
+ typedef X<4> RemovedAllUl;
+ typedef X<5> RemovedAllDl;
+
+ virtual void on(AddedDl, FinishedItem*) throw() { }
+ virtual void on(RemovedDl, FinishedItem*) throw() { }
+ virtual void on(RemovedAllDl) throw() { }
+ virtual void on(AddedUl, FinishedItem*) throw() { }
+ virtual void on(RemovedUl, FinishedItem*) throw() { }
+ virtual void on(RemovedAllUl) throw() { }
+
+};
+
+class FinishedManager : public Singleton<FinishedManager>,
+ public Speaker<FinishedManagerListener>, private DownloadManagerListener, private UploadManagerListener
+{
+public:
+ FinishedItem::List& lockList(bool upload = false) { cs.enter(); return upload ? uploads : downloads; };
+ void unlockList() { cs.leave(); };
+
+ void remove(FinishedItem *item, bool upload = false);
+ void removeAll(bool upload = false);
+private:
+ friend class Singleton<FinishedManager>;
+
+ FinishedManager() {
+ DownloadManager::getInstance()->addListener(this);
+ UploadManager::getInstance()->addListener(this);
+ }
+ virtual ~FinishedManager() throw();
+
+ virtual void on(DownloadManagerListener::Complete, Download* d) throw();
+ virtual void on(UploadManagerListener::Complete, Upload*) throw();
+
+ CriticalSection cs;
+ FinishedItem::List downloads, uploads;
+};
+
+#endif // FINISHEDMANAGER_H
+
+/**
+ * @file
+ * $Id: FinishedManager.h,v 1.2 2005/08/21 14:03:43 olof Exp $
+ */
diff --git a/dcpp/HashManager.cpp b/dcpp/HashManager.cpp
new file mode 100644
index 0000000..7cab600
--- /dev/null
+++ b/dcpp/HashManager.cpp
@@ -0,0 +1,678 @@
+/*
+ * Copyright (C) 2001-2005 Jacek Sieka, arnetheduck on gmail point 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 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.
+ */
+
+#include "stdinc.h"
+#include "DCPlusPlus.h"
+
+#include "HashManager.h"
+#include "ResourceManager.h"
+#include "SimpleXML.h"
+#include "LogManager.h"
+#include "File.h"
+
+#define HASH_FILE_VERSION_STRING "2"
+static const u_int32_t HASH_FILE_VERSION=2;
+const int64_t HashManager::MIN_BLOCK_SIZE = 64*1024;
+
+bool HashManager::checkTTH(const string& aFileName, int64_t aSize, u_int32_t aTimeStamp) {
+ Lock l(cs);
+ if(!store.checkTTH(aFileName, aSize, aTimeStamp)) {
+ hasher.hashFile(aFileName, aSize);
+ return false;
+ }
+ return true;
+}
+
+const TTHValue& HashManager::getTTH(const string& aFileName, int64_t aSize) throw(HashException) {
+ Lock l(cs);
+ const TTHValue* tth = store.getTTH(aFileName);
+ if(tth == NULL){
+ hasher.hashFile(aFileName, aSize);
+ throw HashException(Util::emptyString);
+ }
+ return *tth;
+}
+
+bool HashManager::getTree(const TTHValue& root, TigerTree& tt) {
+ Lock l(cs);
+ return store.getTree(root, tt);
+}
+
+void HashManager::hashDone(const string& aFileName, u_int32_t aTimeStamp, const TigerTree& tth, int64_t speed) {
+ const TTHValue* root = NULL;
+ {
+ Lock l(cs);
+ store.addFile(aFileName, aTimeStamp, tth, true);
+ root = store.getTTH(aFileName);
+ }
+
+ if(root != NULL) {
+ fire(HashManagerListener::TTHDone(), aFileName, *root);
+ }
+
+ string fn = aFileName;
+ if(count(fn.begin(), fn.end(), PATH_SEPARATOR) >= 2) {
+ string::size_type i = fn.rfind(PATH_SEPARATOR);
+ i = fn.rfind(PATH_SEPARATOR, i-1);
+ fn.erase(0, i);
+ fn.insert(0, "...");
+ }
+ if(speed > 0) {
+ LogManager::getInstance()->message(STRING(HASHING_FINISHED) + fn + " (" + Util::formatBytes(speed) + "/s)");
+ } else if(speed <= 0) {
+ LogManager::getInstance()->message(STRING(HASHING_FINISHED) + fn);
+ }
+}
+
+void HashManager::HashStore::addFile(const string& aFileName, u_int32_t aTimeStamp, const TigerTree& tth, bool aUsed) {
+ if(!addTree(tth))
+ return;
+
+ string fname = Text::toLower(Util::getFileName(aFileName));
+ string fpath = Text::toLower(Util::getFilePath(aFileName));
+
+ FileInfoList& fileList = fileIndex[fpath];
+
+ FileInfoIter j = find_if(fileList.begin(), fileList.end(), FileInfo::StringComp(fname));
+ if(j != fileList.end()) {
+ fileList.erase(j);
+ }
+
+ fileList.push_back(FileInfo(fname, tth.getRoot(), aTimeStamp, aUsed));
+ dirty = true;
+}
+
+bool HashManager::HashStore::addTree(const TigerTree& tth) {
+ try {
+ if(treeIndex.find(tth.getRoot()) == treeIndex.end()) {
+ int64_t index = addLeaves(tth.getLeaves());
+ if(index == STORE_FAILED)
+ return false;
+
+ dcassert((index != SMALL_TREE) || ((tth.getLeaves().size() == 1) && (tth.getRoot() == tth.getLeaves()[0])));
+
+ treeIndex.insert(make_pair(tth.getRoot(), TreeInfo(tth.getFileSize(), index, tth.getBlockSize())));
+ }
+ } catch(const Exception& ) {
+ return false;
+ }
+ dirty = true;
+ return true;
+}
+
+int64_t HashManager::HashStore::addLeaves(const TigerTree::MerkleList& leaves) {
+ if(leaves.size() == 1)
+ return SMALL_TREE;
+
+ File f(dataFile, File::RW, File::OPEN);
+ f.setPos(0);
+ int64_t pos = 0;
+ size_t n = sizeof(pos);
+ if(f.read(&pos, n) != sizeof(pos))
+ return STORE_FAILED;
+
+ // Check if we should grow the file, we grow by a meg at a time...
+ int64_t datsz = f.getSize();
+ if((pos + (int64_t)(leaves.size() * TTHValue::SIZE)) >= datsz) {
+ f.setPos(datsz + 1024*1024);
+ f.setEOF();
+ }
+ f.setPos(pos);
+ dcassert(leaves.size() > 0);
+ f.write(leaves[0].data, (leaves.size() * TTHValue::SIZE));
+ int64_t p2 = f.getPos();
+ f.setPos(0);
+ f.write(&p2, sizeof(p2));
+ return pos;
+}
+
+bool HashManager::HashStore::getTree(const TTHValue& root, TigerTree& tth) {
+ TreeIter i = treeIndex.find(root);
+ if(i == treeIndex.end())
+ return false;
+
+ TreeInfo& ti = i->second;
+
+ if(ti.getIndex() == SMALL_TREE) {
+ tth = TigerTree(ti.getSize(), ti.getBlockSize(), i->first);
+ return true;
+ }
+
+ try {
+ File f(dataFile, File::READ, File::OPEN);
+
+ f.setPos(ti.getIndex());
+ size_t datalen = TigerTree::calcBlocks(ti.getSize(), ti.getBlockSize()) * TTHValue::SIZE;
+ AutoArray<u_int8_t> buf(datalen);
+ f.read((u_int8_t*)buf, datalen);
+ tth = TigerTree(ti.getSize(), ti.getBlockSize(), buf);
+ if(!(tth.getRoot() == i->first))
+ return false;
+ } catch(const FileException& ) {
+ return false;
+ }
+ return true;
+}
+
+bool HashManager::HashStore::checkTTH(const string& aFileName, int64_t aSize, u_int32_t aTimeStamp) {
+ string fname = Text::toLower(Util::getFileName(aFileName));
+ string fpath = Text::toLower(Util::getFilePath(aFileName));
+ DirIter i = fileIndex.find(fpath);
+ if(i != fileIndex.end()) {
+ FileInfoIter j = find_if(i->second.begin(), i->second.end(), FileInfo::StringComp(fname));
+ if(j != i->second.end()) {
+ FileInfo& fi = *j;
+ TreeIter ti = treeIndex.find(fi.getRoot());
+ if(ti == treeIndex.end() || ti->second.getSize() != aSize || fi.getTimeStamp() != aTimeStamp) {
+ i->second.erase(j);
+ dirty = true;
+ return false;
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
+const TTHValue* HashManager::HashStore::getTTH(const string& aFileName) {
+ string fname = Text::toLower(Util::getFileName(aFileName));
+ string fpath = Text::toLower(Util::getFilePath(aFileName));
+
+ DirIter i = fileIndex.find(fpath);
+ if(i != fileIndex.end()) {
+ FileInfoIter j = find_if(i->second.begin(), i->second.end(), FileInfo::StringComp(fname));
+ if(j != i->second.end()) {
+ j->setUsed(true);
+ return &(j->getRoot());
+ }
+ }
+ return NULL;
+}
+
+void HashManager::HashStore::rebuild() {
+ string tmpName = dataFile + ".tmp";
+ try {
+ File::renameFile(dataFile, tmpName);
+ File src(tmpName, File::READ, File::OPEN);
+ createDataFile(dataFile);
+ File tgt(dataFile, File::WRITE, File::OPEN);
+ tgt.setEndPos(0);
+
+ TreeMap newIndex;
+ for(DirIter i = fileIndex.begin(); i != fileIndex.end(); ++i) {
+ for(FileInfoIter j = i->second.begin(); j != i->second.end(); ++j) {
+ if(!j->getUsed())
+ continue;
+
+ TreeIter k = treeIndex.find(j->getRoot());
+ if(k != treeIndex.end()) {
+ newIndex[j->getRoot()] = k->second;
+ }
+ }
+ }
+
+ treeIndex = newIndex;
+
+ vector<u_int8_t> buf;
+ for(TreeIter i = treeIndex.begin(); i != treeIndex.end(); ++i) {
+ src.setPos(i->second.getIndex());
+ size_t datalen = TigerTree::calcBlocks(i->second.getSize(), i->second.getBlockSize()) * TTHValue::SIZE;
+ buf.resize(datalen);
+ src.read(&buf[0], datalen);
+ tgt.write(&buf[0], datalen);
+ }
+
+ int64_t pos = tgt.getPos();
+ tgt.setPos(0);
+ tgt.write(&pos, sizeof(pos));
+ // Set size to the nearest mb boundary
+ tgt.setSize(((tgt.getSize() + 1024*1024 - 1) / 1024*1024) * 1024*1024);
+ } catch(const FileException&) {
+ try {
+ File::renameFile(tmpName, dataFile);
+ } catch(const FileException&) {
+ // Too bad...
+ }
+ }
+
+}
+
+#define LITERAL(x) x, sizeof(x)-1
+#define CHECKESCAPE(n) SimpleXML::escape(n, tmp, true)
+
+void HashManager::HashStore::save() {
+ if(dirty) {
+ try {
+ File ff(indexFile + ".tmp", File::WRITE, File::CREATE | File::TRUNCATE);
+ BufferedOutputStream<false> f(&ff);
+
+ string tmp;
+ string b32tmp;
+
+ f.write(SimpleXML::utf8Header);
+ f.write(LITERAL("<HashStore Version=\"" HASH_FILE_VERSION_STRING "\">\r\n"));
+
+ f.write(LITERAL("\t<Trees>\r\n"));
+
+ for(TreeIter i = treeIndex.begin(); i != treeIndex.end(); ++i) {
+ const TreeInfo& ti = i->second;
+ f.write(LITERAL("\t\t<Hash Type=\"TTH\" Index=\""));
+ f.write(Util::toString(ti.getIndex()));
+ f.write(LITERAL("\" BlockSize=\""));
+ f.write(Util::toString(ti.getBlockSize()));
+ f.write(LITERAL("\" Size=\""));
+ f.write(Util::toString(ti.getSize()));
+ f.write(LITERAL("\" Root=\""));
+ b32tmp.clear();
+ f.write(i->first.toBase32(b32tmp));
+ f.write(LITERAL("\"/>\r\n"));
+ }
+
+ f.write(LITERAL("\t</Trees>\r\n\t<Files>\r\n"));
+
+ for(DirIter i = fileIndex.begin(); i != fileIndex.end(); ++i) {
+ const string& dir = i->first;
+ for(FileInfoIter j = i->second.begin(); j != i->second.end(); ++j) {
+ const FileInfo& fi = *j;
+ f.write(LITERAL("\t\t<File Name=\""));
+ f.write(CHECKESCAPE(dir + fi.getFileName()));
+ f.write(LITERAL("\" TimeStamp=\""));
+ f.write(Util::toString(fi.getTimeStamp()));
+ f.write(LITERAL("\" Root=\""));
+ b32tmp.clear();
+ f.write(fi.getRoot().toBase32(b32tmp));
+ f.write(LITERAL("\"/>\r\n"));
+ }
+ }
+ f.write(LITERAL("\t</Files>\r\n</HashStore>"));
+ f.flush();
+ ff.close();
+ File::deleteFile(indexFile);
+ File::renameFile(indexFile + ".tmp", indexFile);
+
+ dirty = false;
+ } catch(const FileException&) {
+ // Too bad...
+ }
+ }
+}
+
+class HashLoader : public SimpleXMLReader::CallBack {
+public:
+ HashLoader(HashManager::HashStore& s) : store(s), size(0), timeStamp(0), version(HASH_FILE_VERSION), inTrees(false), inFiles(false), inHashStore(false) { };
+ virtual void startTag(const string& name, StringPairList& attribs, bool simple);
+ virtual void endTag(const string& name, const string& data);
+
+private:
+ HashManager::HashStore& store;
+
+ string file;
+ int64_t size;
+ u_int32_t timeStamp;
+ int version;
+
+ bool inTrees;
+ bool inFiles;
+ bool inHashStore;
+};
+
+void HashManager::HashStore::load() {
+ try {
+ HashLoader l(*this);
+ SimpleXMLReader(&l).fromXML(File(indexFile, File::READ, File::OPEN).read());
+ } catch(const Exception&) {
+ // ...
+ }
+}
+
+static const string sHashStore = "HashStore";
+static const string sversion = "version"; // Oops, v1 was like this
+static const string sVersion = "Version";
+static const string sTrees = "Trees";
+static const string sFiles = "Files";
+static const string sFile = "File";
+static const string sName = "Name";
+static const string sSize = "Size";
+static const string sHash = "Hash";
+static const string sType = "Type";
+static const string sTTH = "TTH";
+static const string sIndex = "Index";
+static const string sLeafSize = "LeafSize"; // Residue from v1 as well
+static const string sBlockSize = "BlockSize";
+static const string sTimeStamp = "TimeStamp";
+static const string sRoot = "Root";
+
+void HashLoader::startTag(const string& name, StringPairList& attribs, bool simple) {
+ if(!inHashStore && name == sHashStore) {
+ version = Util::toInt(getAttrib(attribs, sVersion, 0));
+ if(version == 0) {
+ version = Util::toInt(getAttrib(attribs, sversion, 0));
+ }
+ inHashStore = !simple;
+ } else if(version == 1) {
+ if(name == sFile && !simple) {
+ file = getAttrib(attribs, sName, 0);
+ size = Util::toInt64(getAttrib(attribs, sSize, 1));
+ timeStamp = Util::toUInt32(getAttrib(attribs, sTimeStamp, 2));
+ } else if(name == sHash) {
+ const string& type = getAttrib(attribs, sType, 0);
+ int64_t blockSize = Util::toInt64(getAttrib(attribs, sLeafSize, 1));
+ int64_t index = Util::toInt64(getAttrib(attribs, sIndex, 2));
+ const string& root = getAttrib(attribs, sRoot, 3);
+ if(!file.empty() && (type == sTTH) && (blockSize >= 1024) && (index >= 8) && !root.empty()) {
+ string fname = Text::toLower(Util::getFileName(file));
+ string fpath = Text::toLower(Util::getFilePath(file));
+
+ store.fileIndex[fpath].push_back(HashManager::HashStore::FileInfo(fname, TTHValue(root), timeStamp, false));
+ store.treeIndex[TTHValue(root)] = HashManager::HashStore::TreeInfo(size, index, blockSize);
+ }
+ }
+ } else if(version == 2) {
+ if(inTrees && name == sHash) {
+ const string& type = getAttrib(attribs, sType, 0);
+ int64_t index = Util::toInt64(getAttrib(attribs, sIndex, 1));
+ int64_t blockSize = Util::toInt64(getAttrib(attribs, sBlockSize, 2));
+ int64_t size = Util::toInt64(getAttrib(attribs, sSize, 3));
+ const string& root = getAttrib(attribs, sRoot, 4);
+ if(!root.empty() && type == sTTH && (index >= 8 || index == HashManager::SMALL_TREE) && blockSize >= 1024) {
+ store.treeIndex[TTHValue(root)] = HashManager::HashStore::TreeInfo(size, index, blockSize);
+ }
+ } else if(inFiles && name == sFile) {
+ file = getAttrib(attribs, sName, 0);
+ timeStamp = Util::toUInt32(getAttrib(attribs, sTimeStamp, 1));
+ const string& root = getAttrib(attribs, sRoot, 2);
+
+ if(!file.empty() && size >= 0 && timeStamp > 0 && !root.empty()) {
+ string fname = Text::toLower(Util::getFileName(file));
+ string fpath = Text::toLower(Util::getFilePath(file));
+
+ store.fileIndex[fpath].push_back(HashManager::HashStore::FileInfo(fname, TTHValue(root), timeStamp, false));
+ }
+ } else if(name == sTrees) {
+ inTrees = !simple;
+ } else if(name == sFiles) {
+ inFiles = !simple;
+ }
+ }
+}
+
+void HashLoader::endTag(const string& name, const string&) {
+ if(name == sFile) {
+ file.clear();
+ }
+}
+
+HashManager::HashStore::HashStore() : indexFile(Util::getAppPath() + "HashIndex.xml"),
+dataFile(Util::getAppPath() + "HashData.dat"), dirty(false)
+{
+ if(File::getSize(dataFile) <= 0) {
+ try {
+ createDataFile(dataFile);
+ } catch(const FileException&) {
+ // ?
+ }
+ }
+}
+
+/**
+ * Creates the data files for storing hash values.
+ * The data file is very simple in its format. The first 8 bytes
+ * are filled with an int64_t (little endian) of the next write position
+ * in the file counting from the start (so that file can be grown in chunks).
+ * We start with a 1 mb file, and then grow it as needed to avoid fragmentation.
+ * To find data inside the file, use the corresponding index file.
+ * Since file is never deleted, space will eventually be wasted, so a rebuild
+ * should occasionally be done.
+ */
+void HashManager::HashStore::createDataFile(const string& name) {
+ try {
+ File dat(name, File::WRITE, File::CREATE | File::TRUNCATE);
+ dat.setPos(1024*1024);
+ dat.setEOF();
+ dat.setPos(0);
+ int64_t start = sizeof(start);
+ dat.write(&start, sizeof(start));
+
+ } catch(const FileException&) {
+ /** @todo All further hashing will unfortunately be wasted(!) */
+ }
+}
+
+#define BUF_SIZE (256*1024)
+
+#ifdef _WIN32
+bool HashManager::Hasher::fastHash(const string& fname, u_int8_t* buf, TigerTree& tth, int64_t size) {
+ HANDLE h = INVALID_HANDLE_VALUE;
+ DWORD x, y;
+ if(!GetDiskFreeSpace(Text::toT(Util::getFilePath(fname)).c_str(), &y, &x, &y, &y)) {
+ return false;
+ } else {
+ if((BUF_SIZE % x) != 0) {
+ return false;
+ } else {
+ h = ::CreateFile(Text::toT(fname).c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_NO_BUFFERING | FILE_FLAG_OVERLAPPED, NULL);
+ if(h == INVALID_HANDLE_VALUE)
+ return false;
+ }
+ }
+ DWORD hn = 0;
+ DWORD rn = 0;
+ u_int8_t* hbuf = buf + BUF_SIZE;
+ u_int8_t* rbuf = buf;
+
+ OVERLAPPED over = { 0 };
+ over.hEvent = CreateEvent(NULL, FALSE, TRUE, NULL);
+
+ bool ok = false;
+
+ u_int32_t lastRead = GET_TICK();
+ if(!::ReadFile(h, hbuf, BUF_SIZE, &hn, &over)) {
+ if(GetLastError() == ERROR_HANDLE_EOF) {
+ hn = 0;
+ } else if(GetLastError() == ERROR_IO_PENDING) {
+ if(!GetOverlappedResult(h, &over, &hn, TRUE)) {
+ if(GetLastError() == ERROR_HANDLE_EOF) {
+ hn = 0;
+ } else {
+ goto cleanup;
+ }
+ }
+ } else {
+ goto cleanup;
+ }
+ }
+
+ over.Offset = hn;
+ size -= hn;
+ BOOL res = TRUE;
+ for(;;) {
+ if(size > 0) {
+ // Start a new overlapped read
+ ResetEvent(over.hEvent);
+ if(SETTING(MAX_HASH_SPEED) > 0) {
+ u_int32_t now = GET_TICK();
+ u_int32_t minTime = hn * 1000LL / (SETTING(MAX_HASH_SPEED) * 1024LL * 1024LL);
+ if(lastRead + minTime > now) {
+ u_int32_t diff = now - lastRead;
+ Thread::sleep(minTime - diff);
+ }
+ lastRead = lastRead + minTime;
+ } else {
+ lastRead = GET_TICK();
+ }
+ res = ReadFile(h, rbuf, BUF_SIZE, &rn, &over);
+ } else {
+ rn = 0;
+ }
+
+ tth.update(hbuf, hn);
+ total -= hn;
+
+ if(size == 0) {
+ ok = true;
+ break;
+ }
+
+ if (!res) {
+ // deal with the error code
+ switch (GetLastError()) {
+ case ERROR_IO_PENDING:
+ if(!GetOverlappedResult(h, &over, &rn, TRUE)) {
+ dcdebug("Error 0x%x: %s\n", GetLastError(), Util::translateError(GetLastError()).c_str());
+ goto cleanup;
+ }
+ break;
+ default:
+ dcdebug("Error 0x%x: %s\n", GetLastError(), Util::translateError(GetLastError()).c_str());
+ goto cleanup;
+ }
+ }
+
+ *((u_int64_t*)&over.Offset) += rn;
+ size -= rn;
+
+ swap(rbuf, hbuf);
+ swap(rn, hn);
+ }
+
+cleanup:
+ ::CloseHandle(over.hEvent);
+ ::CloseHandle(h);
+ return ok;
+}
+#endif
+
+int HashManager::Hasher::run() {
+ setThreadPriority(Thread::IDLE);
+
+ u_int8_t* buf = NULL;
+ bool virtualBuf = true;
+
+ string fname;
+ bool last = false;
+ for(;;) {
+ s.wait();
+ if(stop)
+ break;
+ if(rebuild) {
+ HashManager::getInstance()->doRebuild();
+ rebuild = false;
+ LogManager::getInstance()->message(STRING(HASH_REBUILT));
+ continue;
+ }
+ {
+ Lock l(cs);
+ if(!w.empty()) {
+ file = fname = w.begin()->first;
+ w.erase(w.begin());
+ last = w.empty();
+ } else {
+ last = true;
+ fname.clear();
+ }
+ }
+ running = true;
+
+ if(!fname.empty()) {
+ int64_t size = File::getSize(fname);
+ int64_t sizeLeft = size;
+#ifdef _WIN32
+ if(buf == NULL) {
+ virtualBuf = true;
+ buf = (u_int8_t*)VirtualAlloc(NULL, 2*BUF_SIZE, MEM_COMMIT, PAGE_READWRITE);
+ }
+#endif
+ if(buf == NULL) {
+ virtualBuf = false;
+ buf = new u_int8_t[BUF_SIZE];
+ }
+ try {
+ File f(fname, File::READ, File::OPEN);
+ int64_t bs = max(TigerTree::calcBlockSize(f.getSize(), 10), MIN_BLOCK_SIZE);
+#ifdef _WIN32
+ u_int32_t start = GET_TICK();
+#endif
+ u_int32_t timestamp = f.getLastModified();
+ TigerTree slowTTH(bs);
+ TigerTree* tth = &slowTTH;
+ size_t n = 0;
+#ifdef _WIN32
+ TigerTree fastTTH(bs);
+ tth = &fastTTH;
+ if(!virtualBuf || !fastHash(fname, buf, fastTTH, size)) {
+ tth = &slowTTH;
+#endif
+ u_int32_t lastRead = GET_TICK();
+
+ do {
+ size_t bufSize = BUF_SIZE;
+ if(SETTING(MAX_HASH_SPEED) > 0) {
+ u_int32_t now = GET_TICK();
+ u_int32_t minTime = n * 1000LL / (SETTING(MAX_HASH_SPEED) * 1024LL * 1024LL);
+ if(lastRead + minTime > now) {
+ Thread::sleep(minTime - (now - lastRead));
+ }
+ }
+ n = f.read(buf, bufSize);
+ tth->update(buf, n);
+
+ total -= n;
+ sizeLeft -= n;
+ } while (n > 0 && !stop);
+#ifdef _WIN32
+ } else {
+ sizeLeft = 0;
+ }
+#endif
+ f.close();
+ tth->finalize();
+#ifdef _WIN32
+ u_int32_t end = GET_TICK();
+ int64_t speed = 0;
+ if(end > start) {
+ speed = size * 1000LL / (end - start);
+ }
+#else
+ int64_t speed = 0;
+#endif
+ HashManager::getInstance()->hashDone(fname, timestamp, *tth, speed);
+ } catch(const FileException&) {
+ // Ignore, it'll be readded on the next share refresh...
+ }
+
+ total -= sizeLeft;
+ }
+ running = false;
+ if(buf != NULL && (last || stop)) {
+ if(virtualBuf) {
+#ifdef _WIN32
+ VirtualFree(buf, 0, MEM_RELEASE);
+#endif
+ } else {
+ delete buf;
+ }
+ buf = NULL;
+ }
+ }
+ return 0;
+}
+
+/**
+ * @file
+ * $Id: HashManager.cpp,v 1.2 2005/08/21 14:03:43 olof Exp $
+ */
diff --git a/dcpp/HashManager.h b/dcpp/HashManager.h
new file mode 100644
index 0000000..4b085a7
--- /dev/null
+++ b/dcpp/HashManager.h
@@ -0,0 +1,283 @@
+/*
+ * Copyright (C) 2001-2005 Jacek Sieka, arnetheduck on gmail point 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 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.
+ */
+
+#ifndef _HASH_MANAGER
+#define _HASH_MANAGER
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+#include "Singleton.h"
+#include "MerkleTree.h"
+#include "Thread.h"
+#include "CriticalSection.h"
+#include "Semaphore.h"
+#include "TimerManager.h"
+#include "Util.h"
+#include "FastAlloc.h"
+#include "Text.h"
+
+STANDARD_EXCEPTION(HashException);
+
+class HashManagerListener {
+public:
+ template<int I> struct X { enum { TYPE = I }; };
+
+ typedef X<0> TTHDone;
+
+ virtual void on(TTHDone, const string& /* fileName */, const TTHValue& /* root */) throw() = 0;
+};
+
+class HashLoader;
+
+class HashManager : public Singleton<HashManager>, public Speaker<HashManagerListener>,
+ private TimerManagerListener
+{
+public:
+
+ /** We don't keep leaves for blocks smaller than this... */
+ static const int64_t MIN_BLOCK_SIZE;
+
+ HashManager() {
+ TimerManager::getInstance()->addListener(this);
+ }
+ virtual ~HashManager() throw() {
+ TimerManager::getInstance()->removeListener(this);
+ hasher.join();
+ }
+
+ /**
+ * Check if the TTH tree associated with the filename is current.
+ */
+ bool checkTTH(const string& aFileName, int64_t aSize, u_int32_t aTimeStamp);
+
+ void stopHashing(const string& baseDir) {
+ hasher.stopHashing(baseDir);
+ }
+
+ void setPriority(Thread::Priority p) {
+ hasher.setThreadPriority(p);
+ }
+ /**
+ * @return TTH root
+ */
+ const TTHValue& getTTH(const string& aFileName, int64_t aSize) throw(HashException);
+
+ bool getTree(const TTHValue& root, TigerTree& tt);
+
+ void addTree(const string& aFileName, u_int32_t aTimeStamp, const TigerTree& tt) {
+ hashDone(aFileName, aTimeStamp, tt, -1);
+ }
+ void addTree(const TigerTree& tt) {
+ Lock l(cs);
+ store.addTree(tt);
+ }
+
+ void getStats(string& curFile, int64_t& bytesLeft, size_t& filesLeft) {
+ hasher.getStats(curFile, bytesLeft, filesLeft);
+ }
+
+ /**
+ * Rebuild hash data file
+ */
+ void rebuild() {
+ hasher.scheduleRebuild();
+ }
+
+ void startup() {
+ hasher.start();
+ store.load();
+ }
+
+ void shutdown() {
+ hasher.shutdown();
+ hasher.join();
+ Lock l(cs);
+ store.save();
+ }
+
+private:
+
+ class Hasher : public Thread {
+ public:
+ Hasher() : stop(false), running(false), rebuild(false), total(0) { }
+
+ void hashFile(const string& fileName, int64_t size) {
+ Lock l(cs);
+ if(w.insert(make_pair(fileName, size)).second) {
+ s.signal();
+ total += size;
+ }
+ }
+
+ void stopHashing(const string& baseDir) {
+ Lock l(cs);
+ for(WorkIter i = w.begin(); i != w.end(); ) {
+ if(Util::strnicmp(baseDir, i->first, baseDir.length()) == 0) {
+ total -= i->second;
+ w.erase(i++);
+ } else {
+ ++i;
+ }
+ }
+ }
+
+ virtual int run();
+#ifdef _WIN32
+ bool fastHash(const string& fname, u_int8_t* buf, TigerTree& tth, int64_t size);
+#endif
+ void getStats(string& curFile, int64_t& bytesLeft, size_t& filesLeft) {
+ Lock l(cs);
+ curFile = file;
+ filesLeft = w.size();
+ if(running)
+ filesLeft++;
+ // Just in case...
+ if(total < 0)
+ total = 0;
+ bytesLeft = total;
+ }
+ void shutdown() {
+ stop = true;
+ s.signal();
+ }
+ void scheduleRebuild() {
+ rebuild = true;
+ s.signal();
+ }
+
+ private:
+ // Case-sensitive (faster), it is rather unlikely that case changes, and if it does it's harmless.
+ // map because it's sorted (to avoid random hash order that would create quite strange shares while hashing)
+ typedef map<string, int64_t> WorkMap;
+ typedef WorkMap::iterator WorkIter;
+
+ WorkMap w;
+ CriticalSection cs;
+ Semaphore s;
+
+ bool stop;
+ bool running;
+ bool rebuild;
+ int64_t total;
+ string file;
+ };
+
+ friend class Hasher;
+
+ class HashStore {
+ public:
+ HashStore();
+ void addFile(const string& aFileName, u_int32_t aTimeStamp, const TigerTree& tth, bool aUsed);
+ bool addTree(const TigerTree& tt);
+
+ void load();
+ void save();
+
+ void rebuild();
+
+ bool checkTTH(const string& aFileName, int64_t aSize, u_int32_t aTimeStamp);
+
+ const TTHValue* getTTH(const string& aFileName);
+ bool getTree(const TTHValue& root, TigerTree& tth);
+ bool isDirty() { return dirty; };
+ private:
+ /** Root -> tree mapping info, we assume there's only one tree for each root (a collision would mean we've broken tiger...) */
+ struct TreeInfo {
+ TreeInfo() : size(0), index(0), blockSize(0) { }
+ TreeInfo(int64_t aSize, int64_t aIndex, int64_t aBlockSize) : size(aSize), index(aIndex), blockSize(aBlockSize) { }
+ TreeInfo(const TreeInfo& rhs) : size(rhs.size), index(rhs.index), blockSize(rhs.blockSize) { }
+ TreeInfo& operator=(const TreeInfo& rhs) { size = rhs.size; index = rhs.index; blockSize = rhs.blockSize; return *this; }
+
+ GETSET(int64_t, size, Size);
+ GETSET(int64_t, index, Index);
+ GETSET(int64_t, blockSize, BlockSize);
+ };
+
+ /** File -> root mapping info */
+ struct FileInfo {
+ public:
+ struct StringComp {
+ const string& str;
+ StringComp(const string& aStr) : str(aStr) { }
+ bool operator()(const FileInfo& a) { return a.getFileName() == str; }
+ private:
+ StringComp& operator=(const StringComp&);
+ };
+
+ FileInfo(const string& aFileName, const TTHValue& aRoot, u_int32_t aTimeStamp, bool aUsed) :
+ fileName(aFileName), root(aRoot), timeStamp(aTimeStamp), used(aUsed) { }
+
+ GETSET(string, fileName, FileName);
+ GETSET(TTHValue, root, Root);
+ GETSET(u_int32_t, timeStamp, TimeStamp);
+ GETSET(bool, used, Used);
+ };
+
+ typedef vector<FileInfo> FileInfoList;
+ typedef FileInfoList::iterator FileInfoIter;
+
+ typedef HASH_MAP<string, FileInfoList> DirMap;
+ typedef DirMap::iterator DirIter;
+
+ typedef HASH_MAP_X(TTHValue, TreeInfo, TTHValue::Hash, TTHValue::Hash, TTHValue::Less) TreeMap;
+ typedef TreeMap::iterator TreeIter;
+
+ friend class HashLoader;
+
+ DirMap fileIndex;
+ TreeMap treeIndex;
+
+ string indexFile;
+ string dataFile;
+
+ bool dirty;
+
+ void createDataFile(const string& name);
+ int64_t addLeaves(const TigerTree::MerkleList& leaves);
+ };
+
+ friend class HashLoader;
+
+ Hasher hasher;
+ HashStore store;
+
+ CriticalSection cs;
+
+ /** Single node tree where node = root, no storage in HashData.dat */
+ static const int64_t SMALL_TREE = -1;
+ static const int64_t STORE_FAILED = 0;
+
+ void hashDone(const string& aFileName, u_int32_t aTimeStamp, const TigerTree& tth, int64_t speed);
+ void doRebuild() {
+ Lock l(cs);
+ store.rebuild();
+ }
+ virtual void on(TimerManagerListener::Minute, u_int32_t) throw() {
+ Lock l(cs);
+ store.save();
+ }
+};
+
+#endif // _HASH_MANAGER
+
+/**
+ * @file
+ * $Id: HashManager.h,v 1.2 2005/08/21 14:03:43 olof Exp $
+ */
diff --git a/dcpp/HashValue.h b/dcpp/HashValue.h
new file mode 100644
index 0000000..42e5e60
--- /dev/null
+++ b/dcpp/HashValue.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2001-2005 Jacek Sieka, arnetheduck on gmail point 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 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.
+ */
+
+#ifndef _HASH_VALUE
+#define _HASH_VALUE
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+#include "FastAlloc.h"
+
+template<class Hasher>
+struct HashValue : FastAlloc<HashValue<Hasher> >{
+ static const size_t SIZE = Hasher::HASH_SIZE;
+
+ typedef HashValue* Ptr;
+ struct PtrHash {
+ size_t operator()(const Ptr rhs) const { return *(size_t*)rhs; };
+ bool operator()(const Ptr lhs, const Ptr rhs) const { return (*lhs) == (*rhs); };
+ };
+ struct PtrLess {
+ int operator()(const Ptr lhs, const Ptr rhs) { return (*lhs) < (*rhs); };
+ };
+
+ struct Hash {
+ size_t operator()(const HashValue& rhs) const { return *(size_t*)&rhs; };
+ bool operator()(const HashValue& lhs, const HashValue& rhs) const { return lhs == rhs; };
+ };
+ struct Less {
+ int operator()(const HashValue& lhs, const HashValue& rhs) { return lhs < rhs; };
+ };
+
+ HashValue() { };
+ explicit HashValue(u_int8_t* aData) { memcpy(data, aData, SIZE); }
+ explicit HashValue(const string& base32) { Encoder::fromBase32(base32.c_str(), data, SIZE); };
+ HashValue(const HashValue& rhs) { memcpy(data, rhs.data, SIZE); }
+ HashValue& operator=(const HashValue& rhs) { memcpy(data, rhs.data, SIZE); return *this; }
+ bool operator!=(const HashValue& rhs) const { return !(*this == rhs); }
+ bool operator==(const HashValue& rhs) const { return memcmp(data, rhs.data, SIZE) == 0; }
+ bool operator<(const HashValue& rhs) const { return memcmp(data, rhs.data, SIZE) < 0; }
+
+ string toBase32() const { return Encoder::toBase32(data, SIZE); };
+ string& toBase32(string& tmp) const { return Encoder::toBase32(data, SIZE, tmp); };
+
+ u_int8_t data[SIZE];
+};
+
+#endif // _HASH_VALUE
+
+/**
+* @file
+* $Id: HashValue.h,v 1.2 2005/08/21 14:03:43 olof Exp $
+*/
diff --git a/dcpp/HttpConnection.cpp b/dcpp/HttpConnection.cpp
new file mode 100644
index 0000000..1ac55d8
--- /dev/null
+++ b/dcpp/HttpConnection.cpp
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2001-2005 Jacek Sieka, arnetheduck on gmail point 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 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.
+ */
+
+#include "stdinc.h"
+#include "DCPlusPlus.h"
+
+#include "HttpConnection.h"
+
+#include "SettingsManager.h"
+#include "version.h"
+
+/**
+ * Downloads a file and returns it as a string
+ * @todo Report exceptions
+ * @todo Abort download
+ * @param aUrl Full URL of file
+ * @return A string with the content, or empty if download failed
+ */
+void HttpConnection::downloadFile(const string& aUrl) {
+ dcassert(Util::findSubString(aUrl, "http://") == 0);
+ currentUrl = aUrl;
+ // Trim spaces
+ while(currentUrl[0] == ' ')
+ currentUrl.erase(0, 1);
+ while(currentUrl[currentUrl.length() - 1] == ' ') {
+ currentUrl.erase(currentUrl.length()-1);
+ }
+ // reset all settings (as in constructor), moved here from onLine(302) because ok was not reset properly
+ moved302 = false;
+ ok = false;
+ size = -1;
+ // set download type
+ if(Util::stricmp(currentUrl.substr(currentUrl.size() - 4), ".bz2") == 0) {
+ fire(HttpConnectionListener::TypeBZ2(), this);
+ } else {
+ fire(HttpConnectionListener::TypeNormal(), this);
+ }
+
+ if(SETTING(HTTP_PROXY).empty()) {
+ Util::decodeUrl(currentUrl, server, port, file);
+ if(file.empty())
+ file = "/";
+ } else {
+ Util::decodeUrl(SETTING(HTTP_PROXY), server, port, file);
+ file = currentUrl;
+ }
+
+ if(port == 0)
+ port = 80;
+
+ if(!socket) {
+ socket = BufferedSocket::getSocket(0x0a);
+ }
+ socket->setNoproxy(true);
+ socket->addListener(this);
+ socket->connect(server, port);
+}
+
+void HttpConnection::on(BufferedSocketListener::Connected) throw() {
+ dcassert(socket);
+ socket->write("GET " + file + " HTTP/1.1\r\n");
+ socket->write("User-Agent: " APPNAME " v" VERSIONSTRING "\r\n");
+
+ string sRemoteServer = server;
+ if(!SETTING(HTTP_PROXY).empty())
+ {
+ string tfile;
+ u_int16_t tport;
+ Util::decodeUrl(file, sRemoteServer, tport, tfile);
+ }
+ socket->write("Host: " + sRemoteServer + "\r\n");
+ socket->write("Connection: close\r\n"); // we'll only be doing one request
+ socket->write("Cache-Control: no-cache\r\n\r\n");
+}
+
+void HttpConnection::on(BufferedSocketListener::Line, const string& aLine) throw() {
+ if(!ok) {
+ if(aLine.find("200") == string::npos) {
+ if(aLine.find("302") != string::npos){
+ moved302 = true;
+ } else {
+ socket->disconnect();
+ socket->removeListener(this);
+ BufferedSocket::putSocket(socket);
+ socket = NULL;
+ fire(HttpConnectionListener::Failed(), this, aLine + " (" + currentUrl + ")");
+ return;
+ }
+ }
+ ok = true;
+ } else if(moved302 && Util::findSubString(aLine, "Location") != string::npos){
+ dcassert(socket);
+ socket->removeListener(this);
+ socket->disconnect();
+ BufferedSocket::putSocket(socket);
+ socket = NULL;
+
+ string location302 = aLine.substr(10, aLine.length() - 11);
+ // make sure we can also handle redirects with relative paths
+ if(Util::strnicmp(location302.c_str(), "http://", 7) != 0) {
+ if(location302[0] == '/') {
+ Util::decodeUrl(currentUrl, server, port, file);
+ string tmp = "http://" + server;
+ if(port != 80)
+ tmp += ':' + Util::toString(port);
+ location302 = tmp + location302;
+ } else {
+ string::size_type i = currentUrl.rfind('/');
+ dcassert(i != string::npos);
+ location302 = currentUrl.substr(0, i + 1) + location302;
+ }
+ }
+ fire(HttpConnectionListener::Redirected(), this, location302);
+
+ downloadFile(location302);
+ } else if(aLine == "\x0d") {
+ socket->setDataMode(size);
+ } else if(Util::findSubString(aLine, "Content-Length") != string::npos) {
+ size = Util::toInt(aLine.substr(16, aLine.length() - 17));
+ } else if(Util::findSubString(aLine, "Content-Encoding") != string::npos) {
+ if(aLine.substr(18, aLine.length() - 19) == "x-bzip2")
+ fire(HttpConnectionListener::TypeBZ2(), this);
+ }
+}
+
+void HttpConnection::on(BufferedSocketListener::Failed, const string& aLine) throw() {
+ socket->removeListener(this);
+ BufferedSocket::putSocket(socket);
+ socket = NULL;
+ fire(HttpConnectionListener::Failed(), this, aLine + " (" + currentUrl + ")");
+}
+
+void HttpConnection::on(BufferedSocketListener::ModeChange) throw() {
+ socket->removeListener(this);
+ socket->disconnect();
+ BufferedSocket::putSocket(socket);
+ socket = NULL;
+ fire(HttpConnectionListener::Complete(), this, currentUrl);
+}
+void HttpConnection::on(BufferedSocketListener::Data, u_int8_t* aBuf, size_t aLen) throw() {
+ fire(HttpConnectionListener::Data(), this, aBuf, aLen);
+}
+
+/**
+ * @file
+ * $Id: HttpConnection.cpp,v 1.2 2005/08/21 14:03:43 olof Exp $
+ */
+
diff --git a/dcpp/HttpConnection.h b/dcpp/HttpConnection.h
new file mode 100644
index 0000000..c9aa5c4
--- /dev/null
+++ b/dcpp/HttpConnection.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2001-2005 Jacek Sieka, arnetheduck on gmail point 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 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.
+ */
+
+#if !defined(AFX_HTTPCONNECTION_H__47AE2649_8D90_4C38_B048_69B3C26B3954__INCLUDED_)
+#define AFX_HTTPCONNECTION_H__47AE2649_8D90_4C38_B048_69B3C26B3954__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+#include "BufferedSocket.h"
+
+class HttpConnection;
+
+class HttpConnectionListener {
+public:
+ template<int I> struct X { enum { TYPE = I }; };
+
+ typedef X<0> Data;
+ typedef X<1> Failed;
+ typedef X<2> Complete;
+ typedef X<3> Redirected;
+ typedef X<4> TypeNormal;
+ typedef X<5> TypeBZ2;
+
+ virtual void on(Data, HttpConnection*, const u_int8_t*, size_t) throw() =0;
+ virtual void on(Failed, HttpConnection*, const string&) throw() { }
+ virtual void on(Complete, HttpConnection*, const string&) throw() { }
+ virtual void on(Redirected, HttpConnection*, const string&) throw() { }
+ virtual void on(TypeNormal, HttpConnection*) throw() { }
+ virtual void on(TypeBZ2, HttpConnection*) throw() { }
+};
+
+class HttpConnection : BufferedSocketListener, public Speaker<HttpConnectionListener>
+{
+public:
+ void downloadFile(const string& aUrl);
+ HttpConnection() : ok(false), port(80), size(-1), moved302(false), socket(NULL) { };
+ virtual ~HttpConnection() throw() {
+ if(socket) {
+ socket->removeListener(this);
+ BufferedSocket::putSocket(socket);
+ }
+ }
+
+private:
+
+ HttpConnection(const HttpConnection&);
+ HttpConnection& operator=(const HttpConnection&);
+
+ string currentUrl;
+ string file;
+ string server;
+ bool ok;
+ u_int16_t port;
+ int64_t size;
+ bool moved302;
+
+ BufferedSocket* socket;
+
+ // BufferedSocketListener
+ virtual void on(Connected) throw();
+ virtual void on(Line, const string&) throw();
+ virtual void on(Data, u_int8_t*, size_t) throw();
+ virtual void on(ModeChange) throw();
+ virtual void on(Failed, const string&) throw();
+
+ void onConnected();
+ void onLine(const string& aLine);
+
+};
+
+#endif // !defined(AFX_HTTPCONNECTION_H__47AE2649_8D90_4C38_B048_69B3C26B3954__INCLUDED_)
+
+/**
+ * @file
+ * $Id: HttpConnection.h,v 1.2 2005/08/21 14:03:43 olof Exp $
+ */
+
diff --git a/dcpp/HubManager.cpp b/dcpp/HubManager.cpp
new file mode 100644
index 0000000..6801c58
--- /dev/null
+++ b/dcpp/HubManager.cpp
@@ -0,0 +1,604 @@
+/*
+ * Copyright (C) 2001-2005 Jacek Sieka, arnetheduck on gmail point 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 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.
+ */
+
+#include "stdinc.h"
+#include "DCPlusPlus.h"
+
+#include "HubManager.h"
+
+#include "ClientManager.h"
+#include "ResourceManager.h"
+#include "CryptoManager.h"
+
+#include "HttpConnection.h"
+#include "StringTokenizer.h"
+#include "SimpleXML.h"
+#include "UserCommand.h"
+
+#define FAVORITES_FILE "Favorites.xml"
+
+UserCommand HubManager::addUserCommand(int type, int ctx, int flags, const string& name, const string& command, const string& hub) {
+ // No dupes, add it...
+ Lock l(cs);
+ userCommands.push_back(UserCommand(lastId++, type, ctx, flags, name, command, hub));
+ UserCommand& uc = userCommands.back();
+ if(!uc.isSet(UserCommand::FLAG_NOSAVE))
+ save();
+ return userCommands.back();
+}
+
+bool HubManager::getUserCommand(int cid, UserCommand& uc) {
+ Lock l(cs);
+ for(UserCommand::Iter i = userCommands.begin(); i != userCommands.end(); ++i) {
+ if(i->getId() == cid) {
+ uc = *i;
+ return true;
+ }
+ }
+ return false;
+}
+
+bool HubManager::moveUserCommand(int cid, int pos) {
+ dcassert(pos == -1 || pos == 1);
+ Lock l(cs);
+ for(UserCommand::Iter i = userCommands.begin(); i != userCommands.end(); ++i) {
+ if(i->getId() == cid) {
+ swap(*i, *(i + pos));
+ return true;
+ }
+ }
+ return false;
+}
+
+void HubManager::updateUserCommand(const UserCommand& uc) {
+ bool nosave = true;
+ Lock l(cs);
+ for(UserCommand::Iter i = userCommands.begin(); i != userCommands.end(); ++i) {
+ if(i->getId() == uc.getId()) {
+ *i = uc;
+ nosave = uc.isSet(UserCommand::FLAG_NOSAVE);
+ break;
+ }
+ }
+ if(!nosave)
+ save();
+}
+
+void HubManager::removeUserCommand(int cid) {
+ bool nosave = true;
+ Lock l(cs);
+ for(UserCommand::Iter i = userCommands.begin(); i != userCommands.end(); ++i) {
+ if(i->getId() == cid) {
+ nosave = i->isSet(UserCommand::FLAG_NOSAVE);
+ userCommands.erase(i);
+ break;
+ }
+ }
+ if(!nosave)
+ save();
+}
+void HubManager::removeUserCommand(const string& srv) {
+ Lock l(cs);
+ for(UserCommand::Iter i = userCommands.begin(); i != userCommands.end(); ) {
+ if((i->getHub() == srv) && i->isSet(UserCommand::FLAG_NOSAVE)) {
+ i = userCommands.erase(i);
+ } else {
+ ++i;
+ }
+ }
+}
+
+void HubManager::removeHubUserCommands(int ctx, const string& hub) {
+ Lock l(cs);
+ for(UserCommand::Iter i = userCommands.begin(); i != userCommands.end(); ) {
+ if(i->getHub() == hub && i->isSet(UserCommand::FLAG_NOSAVE) && i->getCtx() & ctx) {
+ i = userCommands.erase(i);
+ } else {
+ ++i;
+ }
+ }
+}
+
+
+void HubManager::addFavoriteUser(User::Ptr& aUser) {
+ if(find(users.begin(), users.end(), aUser) == users.end()) {
+ users.push_back(aUser);
+ aUser->setFavoriteUser(new FavoriteUser());
+ fire(HubManagerListener::UserAdded(), aUser);
+ save();
+ }
+}
+
+void HubManager::removeFavoriteUser(User::Ptr& aUser) {
+ User::Iter i = find(users.begin(), users.end(), aUser);
+ if(i != users.end()) {
+ aUser->setFavoriteUser(NULL);
+ fire(HubManagerListener::UserRemoved(), aUser);
+ users.erase(i);
+ save();
+ }
+}
+
+void HubManager::addFavorite(const FavoriteHubEntry& aEntry) {
+ FavoriteHubEntry* f;
+
+ FavoriteHubEntry::Iter i = getFavoriteHub(aEntry.getServer());
+ if(i != favoriteHubs.end()) {
+ return;
+ }
+ f = new FavoriteHubEntry(aEntry);
+ favoriteHubs.push_back(f);
+ fire(HubManagerListener::FavoriteAdded(), f);
+ save();
+}
+
+void HubManager::removeFavorite(FavoriteHubEntry* entry) {
+ FavoriteHubEntry::Iter i = find(favoriteHubs.begin(), favoriteHubs.end(), entry);
+ if(i == favoriteHubs.end()) {
+ return;
+ }
+
+ fire(HubManagerListener::FavoriteRemoved(), entry);
+ favoriteHubs.erase(i);
+ delete entry;
+ save();
+}
+
+bool HubManager::addFavoriteDir(const string& aDirectory, const string & aName){
+ string path = aDirectory;
+
+ if( path[ path.length() -1 ] != PATH_SEPARATOR )
+ path += PATH_SEPARATOR;
+
+ for(StringPairIter i = favoriteDirs.begin(); i != favoriteDirs.end(); ++i) {
+ if((Util::strnicmp(path, i->first, i->first.length()) == 0) && (Util::strnicmp(path, i->first, path.length()) == 0)) {
+ return false;
+ }
+ if(Util::stricmp(aName, i->second) == 0) {
+ return false;
+ }
+ }
+ favoriteDirs.push_back(make_pair(aDirectory, aName));
+ save();
+ return true;
+}
+
+bool HubManager::removeFavoriteDir(const string& aName) {
+ string d(aName);
+
+ if(d[d.length() - 1] != PATH_SEPARATOR)
+ d += PATH_SEPARATOR;
+
+ for(StringPairIter j = favoriteDirs.begin(); j != favoriteDirs.end(); ++j) {
+ if(Util::stricmp(j->first.c_str(), d.c_str()) == 0) {
+ favoriteDirs.erase(j);
+ save();
+ return true;
+ }
+ }
+ return false;
+}
+
+bool HubManager::renameFavoriteDir(const string& aName, const string& anotherName) {
+
+ for(StringPairIter j = favoriteDirs.begin(); j != favoriteDirs.end(); ++j) {
+ if(Util::stricmp(j->second.c_str(), aName.c_str()) == 0) {
+ j->second = anotherName;
+ save();
+ return true;
+ }
+ }
+ return false;
+}
+
+void HubManager::onHttpFinished() throw() {
+ string::size_type i, j;
+ string* x;
+ string bzlist;
+
+ if(listType == TYPE_BZIP2) {
+ try {
+ CryptoManager::getInstance()->decodeBZ2((u_int8_t*)downloadBuf.data(), downloadBuf.size(), bzlist);
+ } catch(const CryptoException&) {
+ bzlist.clear();
+ }
+ x = &bzlist;
+ } else {
+ x = &downloadBuf;
+ }
+
+ {
+ Lock l(cs);
+ publicListMatrix[publicListServer].clear();
+
+ if(x->compare(0, 5, "<?xml") == 0 || x->compare(0, 8, "\xEF\xBB\xBF<?xml") == 0) {
+ loadXmlList(*x);
+ } else {
+ i = 0;
+
+ string utfText = Text::acpToUtf8(*x);
+
+ while( (i < utfText.size()) && ((j=utfText.find("\r\n", i)) != string::npos)) {
+ StringTokenizer<string> tok(utfText.substr(i, j-i), '|');
+ i = j + 2;
+ if(tok.getTokens().size() < 4)
+ continue;
+
+ StringList::const_iterator k = tok.getTokens().begin();
+ const string& name = *k++;
+ const string& server = *k++;
+ const string& desc = *k++;
+ const string& usersOnline = *k++;
+ publicListMatrix[publicListServer].push_back(HubEntry(name, server, desc, usersOnline));
+ }
+ }
+ }
+ downloadBuf = Util::emptyString;
+}
+
+class XmlListLoader : public SimpleXMLReader::CallBack {
+public:
+ XmlListLoader(HubEntry::List& lst) : publicHubs(lst) { };
+ virtual ~XmlListLoader() { }
+ virtual void startTag(const string& name, StringPairList& attribs, bool) {
+ if(name == "Hub") {
+ const string& name = getAttrib(attribs, "Name", 0);
+ const string& server = getAttrib(attribs, "Address", 1);
+ const string& description = getAttrib(attribs, "Description", 2);
+ const string& users = getAttrib(attribs, "Users", 3);
+ const string& country = getAttrib(attribs, "Country", 4);
+ const string& shared = getAttrib(attribs, "Shared", 5);
+ const string& minShare = getAttrib(attribs, "Minshare", 5);
+ const string& minSlots = getAttrib(attribs, "Minslots", 5);
+ const string& maxHubs = getAttrib(attribs, "Maxhubs", 5);
+ const string& maxUsers = getAttrib(attribs, "Maxusers", 5);
+ const string& reliability = getAttrib(attribs, "Reliability", 5);
+ const string& rating = getAttrib(attribs, "Rating", 5);
+ publicHubs.push_back(HubEntry(name, server, description, users, country, shared, minShare, minSlots, maxHubs, maxUsers, reliability, rating));
+ }
+ }
+ virtual void endTag(const string&, const string&) {
+
+ }
+private:
+ HubEntry::List& publicHubs;
+};
+
+void HubManager::loadXmlList(const string& xml) {
+ try {
+ XmlListLoader loader(publicListMatrix[publicListServer]);
+ SimpleXMLReader(&loader).fromXML(xml);
+ } catch(const SimpleXMLException&) {
+
+ }
+}
+
+void HubManager::save() {
+ if(dontSave)
+ return;
+
+ Lock l(cs);
+ try {
+ SimpleXML xml;
+
+ xml.addTag("Favorites");
+ xml.stepIn();
+
+ xml.addTag("Hubs");
+ xml.stepIn();
+
+ for(FavoriteHubEntry::Iter i = favoriteHubs.begin(); i != favoriteHubs.end(); ++i) {
+ xml.addTag("Hub");
+ xml.addChildAttrib("Name", (*i)->getName());
+ xml.addChildAttrib("Connect", (*i)->getConnect());
+ xml.addChildAttrib("Description", (*i)->getDescription());
+ xml.addChildAttrib("Nick", (*i)->getNick(false));
+ xml.addChildAttrib("Password", (*i)->getPassword());
+ xml.addChildAttrib("Server", (*i)->getServer());
+ xml.addChildAttrib("UserDescription", (*i)->getUserDescription());
+ xml.addChildAttrib("Bottom", Util::toString((*i)->getBottom()));
+ xml.addChildAttrib("Top", Util::toString((*i)->getTop()));
+ xml.addChildAttrib("Right", Util::toString((*i)->getRight()));
+ xml.addChildAttrib("Left", Util::toString((*i)->getLeft()));
+ }
+ xml.stepOut();
+ xml.addTag("Users");
+ xml.stepIn();
+ for(User::Iter j = users.begin(); j != users.end(); ++j) {
+ xml.addTag("User");
+ xml.addChildAttrib("Nick", (*j)->getNick());
+ xml.addChildAttrib("LastHubAddress", (*j)->getLastHubAddress());
+ xml.addChildAttrib("LastHubName", (*j)->getLastHubName());
+ xml.addChildAttrib("LastSeen", (*j)->getFavoriteLastSeen());
+ xml.addChildAttrib("GrantSlot", (*j)->getFavoriteGrantSlot());
+ xml.addChildAttrib("UserDescription", (*j)->getUserDescription());
+ }
+ xml.stepOut();
+ xml.addTag("UserCommands");
+ xml.stepIn();
+ for(UserCommand::Iter k = userCommands.begin(); k != userCommands.end(); ++k) {
+ if(!k->isSet(UserCommand::FLAG_NOSAVE)) {
+ xml.addTag("UserCommand");
+ xml.addChildAttrib("Type", k->getType());
+ xml.addChildAttrib("Context", k->getCtx());
+ xml.addChildAttrib("Name", k->getName());
+ xml.addChildAttrib("Command", k->getCommand());
+ xml.addChildAttrib("Hub", k->getHub());
+ }
+ }
+ xml.stepOut();
+ //Favorite download to dirs
+ xml.addTag("FavoriteDirs");
+ xml.stepIn();
+ StringPairList spl = getFavoriteDirs();
+ for(StringPairIter i = spl.begin(); i != spl.end(); ++i) {
+ xml.addTag("Directory", i->first);
+ xml.addChildAttrib("Name", i->second);
+ }
+ xml.stepOut();
+
+ xml.stepOut();
+
+ string fname = Util::getAppPath() + FAVORITES_FILE;
+
+ File f(fname + ".tmp", File::WRITE, File::CREATE | File::TRUNCATE);
+ f.write(SimpleXML::utf8Header);
+ f.write(xml.toXML());
+ f.close();
+ File::deleteFile(fname);
+ File::renameFile(fname + ".tmp", fname);
+
+ } catch(const Exception& e) {
+ dcdebug("HubManager::save: %s\n", e.getError().c_str());
+ }
+}
+
+void HubManager::load() {
+
+ // Add NMDC standard op commands
+ static const char kickstr[] =
+ "$To: %[nick] From: %[mynick] $<%[mynick]> You are being kicked because: %[line:Reason]|<%[mynick]> %[mynick] is kicking %[nick] because: %[line:Reason]|$Kick %[nick]|";
+ addUserCommand(UserCommand::TYPE_RAW_ONCE, UserCommand::CONTEXT_CHAT | UserCommand::CONTEXT_SEARCH, UserCommand::FLAG_NOSAVE,
+ STRING(KICK_USER), kickstr, "op");
+ static const char redirstr[] =
+ "$OpForceMove $Who:%[nick]$Where:%[line:Target Server]$Msg:%[line:Message]|";
+ addUserCommand(UserCommand::TYPE_RAW_ONCE, UserCommand::CONTEXT_CHAT | UserCommand::CONTEXT_SEARCH, UserCommand::FLAG_NOSAVE,
+ STRING(REDIRECT_USER), redirstr, "op");
+
+ // Add ADC standard op commands
+ static const char adc_disconnectstr[] =
+ "HDSC %[mycid] %[cid] DI ND Friendly\\ disconnect\n";
+ addUserCommand(UserCommand::TYPE_RAW_ONCE, UserCommand::CONTEXT_CHAT | UserCommand::CONTEXT_SEARCH, UserCommand::FLAG_NOSAVE,
+ STRING(DISCONNECT_USER), adc_disconnectstr, "adc://op");
+ static const char adc_kickstr[] =
+ "HDSC %[mycid] %[cid] KK KK %[line:Reason]\n";
+ addUserCommand(UserCommand::TYPE_RAW_ONCE, UserCommand::CONTEXT_CHAT | UserCommand::CONTEXT_SEARCH, UserCommand::FLAG_NOSAVE,
+ STRING(KICK_USER), adc_kickstr, "adc://op");
+ static const char adc_banstr[] =
+ "HDSC %[mycid] %[cid] BN BN %[line:Seconds (-1 = forever)] %[line:Reason]\n";
+ addUserCommand(UserCommand::TYPE_RAW_ONCE, UserCommand::CONTEXT_CHAT | UserCommand::CONTEXT_SEARCH, UserCommand::FLAG_NOSAVE,
+ STRING(BAN_USER), adc_banstr, "adc://op");
+ static const char adc_redirstr[] =
+ "HDSC %[mycid] %[cid] RD RD %[line:Redirect address] %[line:Reason]\n";
+ addUserCommand(UserCommand::TYPE_RAW_ONCE, UserCommand::CONTEXT_CHAT | UserCommand::CONTEXT_SEARCH, UserCommand::FLAG_NOSAVE,
+ STRING(REDIRECT_USER), adc_redirstr, "adc://op");
+
+
+ try {
+ SimpleXML xml;
+ xml.fromXML(File(Util::getAppPath() + FAVORITES_FILE, File::READ, File::OPEN).read());
+
+ if(xml.findChild("Favorites")) {
+ xml.stepIn();
+ load(&xml);
+ xml.stepOut();
+ }
+ } catch(const Exception& e) {
+ dcdebug("HubManager::load: %s\n", e.getError().c_str());
+ }
+}
+
+void HubManager::load(SimpleXML* aXml) {
+ dontSave = true;
+
+ // Old names...load for compatibility.
+ aXml->resetCurrentChild();
+ if(aXml->findChild("Favorites")) {
+ aXml->stepIn();
+ while(aXml->findChild("Favorite")) {
+ FavoriteHubEntry* e = new FavoriteHubEntry();
+ e->setName(aXml->getChildAttrib("Name"));
+ e->setConnect(aXml->getBoolChildAttrib("Connect"));
+ e->setDescription(aXml->getChildAttrib("Description"));
+ e->setNick(aXml->getChildAttrib("Nick"));
+ e->setPassword(aXml->getChildAttrib("Password"));
+ e->setServer(aXml->getChildAttrib("Server"));
+ e->setUserDescription(aXml->getChildAttrib("UserDescription"));
+ favoriteHubs.push_back(e);
+ }
+ aXml->stepOut();
+ }
+ aXml->resetCurrentChild();
+ if(aXml->findChild("Commands")) {
+ aXml->stepIn();
+ while(aXml->findChild("Command")) {
+ const string& name = aXml->getChildAttrib("Name");
+ const string& command = aXml->getChildAttrib("Command");
+ const string& hub = aXml->getChildAttrib("Hub");
+ const string& nick = aXml->getChildAttrib("Nick");
+ if(nick.empty()) {
+ // Old mainchat style command
+ addUserCommand(UserCommand::TYPE_RAW, UserCommand::CONTEXT_CHAT | UserCommand::CONTEXT_SEARCH,
+ 0, name, "<%[mynick]> " + command + "|", hub);
+ } else {
+ addUserCommand(UserCommand::TYPE_RAW, UserCommand::CONTEXT_CHAT | UserCommand::CONTEXT_SEARCH,
+ 0, name, "$To: " + nick + " From: %[mynick] $" + command + "|", hub);
+ }
+ }
+ aXml->stepOut();
+ }
+ // End old names
+
+ aXml->resetCurrentChild();
+ if(aXml->findChild("Hubs")) {
+ aXml->stepIn();
+ while(aXml->findChild("Hub")) {
+ FavoriteHubEntry* e = new FavoriteHubEntry();
+ e->setName(aXml->getChildAttrib("Name"));
+ e->setConnect(aXml->getBoolChildAttrib("Connect"));
+ e->setDescription(aXml->getChildAttrib("Description"));
+ e->setNick(aXml->getChildAttrib("Nick"));
+ e->setPassword(aXml->getChildAttrib("Password"));
+ e->setServer(aXml->getChildAttrib("Server"));
+ e->setUserDescription(aXml->getChildAttrib("UserDescription"));
+ e->setBottom((u_int16_t)aXml->getIntChildAttrib("Bottom") );
+ e->setTop((u_int16_t)aXml->getIntChildAttrib("Top"));
+ e->setRight((u_int16_t)aXml->getIntChildAttrib("Right"));
+ e->setLeft((u_int16_t)aXml->getIntChildAttrib("Left"));
+ favoriteHubs.push_back(e);
+ }
+ aXml->stepOut();
+ }
+ aXml->resetCurrentChild();
+ if(aXml->findChild("Users")) {
+ aXml->stepIn();
+ while(aXml->findChild("User")) {
+ User::Ptr u = ClientManager::getInstance()->getUser(aXml->getChildAttrib("Nick"), aXml->getChildAttrib("LastHubAddress"));
+ if(!u->isOnline()) {
+ u->setLastHubAddress(aXml->getChildAttrib("LastHubAddress"));
+ u->setLastHubName(aXml->getChildAttrib("LastHubName"));
+ }
+ addFavoriteUser(u);
+ u->setFavoriteGrantSlot(aXml->getBoolChildAttrib("GrantSlot"));
+ u->setFavoriteLastSeen((u_int32_t)aXml->getIntChildAttrib("LastSeen"));
+ u->setUserDescription(aXml->getChildAttrib("UserDescription"));
+ }
+ aXml->stepOut();
+ }
+ aXml->resetCurrentChild();
+ if(aXml->findChild("UserCommands")) {
+ aXml->stepIn();
+ while(aXml->findChild("UserCommand")) {
+ addUserCommand(aXml->getIntChildAttrib("Type"), aXml->getIntChildAttrib("Context"),
+ 0, aXml->getChildAttrib("Name"), aXml->getChildAttrib("Command"), aXml->getChildAttrib("Hub"));
+ }
+ aXml->stepOut();
+ }
+ //Favorite download to dirs
+ aXml->resetCurrentChild();
+ if(aXml->findChild("FavoriteDirs")) {
+ aXml->stepIn();
+ while(aXml->findChild("Directory")) {
+ string virt = aXml->getChildAttrib("Name");
+ string d(aXml->getChildData());
+ HubManager::getInstance()->addFavoriteDir(d, virt);
+ }
+ aXml->stepOut();
+ }
+
+ dontSave = false;
+}
+
+StringList HubManager::getHubLists() {
+ StringTokenizer<string> lists(SETTING(HUBLIST_SERVERS), ';');
+ return lists.getTokens();
+}
+
+bool HubManager::setHubList(int aHubList) {
+ if(!running) {
+ lastServer = aHubList;
+ StringList sl = getHubLists();
+ publicListServer = sl[(lastServer) % sl.size()];
+ return true;
+ }
+ return false;
+}
+
+void HubManager::refresh() {
+ StringList sl = getHubLists();
+ if(sl.empty())
+ return;
+ publicListServer = sl[(lastServer) % sl.size()];
+ if(Util::strnicmp(publicListServer.c_str(), "http://", 7) != 0) {
+ lastServer++;
+ return;
+ }
+
+ fire(HubManagerListener::DownloadStarting(), publicListServer);
+ if(!running) {
+ if(!c)
+ c = new HttpConnection();
+ {
+ Lock l(cs);
+ publicListMatrix[publicListServer].clear();
+ }
+ c->addListener(this);
+ c->downloadFile(publicListServer);
+ running = true;
+ }
+}
+
+UserCommand::List HubManager::getUserCommands(int ctx, const string& hub, bool op) {
+ Lock l(cs);
+ UserCommand::List lst;
+ bool adc = hub.size() >= 6 && hub.substr(0, 6) == "adc://";
+ for(UserCommand::Iter i = userCommands.begin(); i != userCommands.end(); ++i) {
+ UserCommand& uc = *i;
+ if(uc.getCtx() & ctx) {
+ if( (!adc && (uc.getHub().empty() || (op && uc.getHub() == "op"))) ||
+ (adc && (uc.getHub() == "adc://" || (op && uc.getHub() == "adc://op"))) ||
+ (Util::stricmp(hub, uc.getHub()) == 0) )
+ {
+ lst.push_back(*i);
+ }
+ }
+ }
+ return lst;
+}
+
+// HttpConnectionListener
+void HubManager::on(Data, HttpConnection*, const u_int8_t* buf, size_t len) throw() {
+ downloadBuf.append((const char*)buf, len);
+}
+
+void HubManager::on(Failed, HttpConnection*, const string& aLine) throw() {
+ c->removeListener(this);
+ lastServer++;
+ running = false;
+ fire(HubManagerListener::DownloadFailed(), aLine);
+}
+void HubManager::on(Complete, HttpConnection*, const string& aLine) throw() {
+ c->removeListener(this);
+ onHttpFinished();
+ running = false;
+ fire(HubManagerListener::DownloadFinished(), aLine);
+}
+void HubManager::on(Redirected, HttpConnection*, const string& aLine) throw() {
+ fire(HubManagerListener::DownloadStarting(), aLine);
+}
+void HubManager::on(TypeNormal, HttpConnection*) throw() {
+ listType = TYPE_NORMAL;
+}
+void HubManager::on(TypeBZ2, HttpConnection*) throw() {
+ listType = TYPE_BZIP2;
+}
+
+/**
+ * @file
+ * $Id: HubManager.cpp,v 1.2 2005/08/21 14:03:43 olof Exp $
+ */
diff --git a/dcpp/HubManager.h b/dcpp/HubManager.h
new file mode 100644
index 0000000..e703b18
--- /dev/null
+++ b/dcpp/HubManager.h
@@ -0,0 +1,274 @@
+/*
+ * Copyright (C) 2001-2005 Jacek Sieka, arnetheduck on gmail point 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 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.
+ */
+
+#if !defined(AFX_HUBMANAGER_H__75858D5D_F12F_40D0_B127_5DDED226C098__INCLUDED_)
+#define AFX_HUBMANAGER_H__75858D5D_F12F_40D0_B127_5DDED226C098__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+#include "SettingsManager.h"
+
+#include "CriticalSection.h"
+#include "HttpConnection.h"
+#include "User.h"
+#include "UserCommand.h"
+#include "FavoriteUser.h"
+#include "Singleton.h"
+
+class HubEntry {
+public:
+ typedef vector<HubEntry> List;
+ typedef List::iterator Iter;
+
+ HubEntry(const string& aName, const string& aServer, const string& aDescription, const string& aUsers) throw() :
+ name(aName), server(aServer), description(aDescription), country(Util::emptyString),
+ rating(Util::emptyString), reliability(0.0), shared(0), minShare(0), users(Util::toInt(aUsers)), minSlots(0), maxHubs(0), maxUsers(0) { };
+
+ HubEntry(const string& aName, const string& aServer, const string& aDescription, const string& aUsers, const string& aCountry,
+ const string& aShared, const string& aMinShare, const string& aMinSlots, const string& aMaxHubs, const string& aMaxUsers,
+ const string& aReliability, const string& aRating) : name(aName), server(aServer), description(aDescription), country(aCountry),
+ rating(aRating), reliability((float)(Util::toFloat(aReliability) / 100.0)), shared(Util::toInt64(aShared)), minShare(Util::toInt64(aMinShare)),
+ users(Util::toInt(aUsers)), minSlots(Util::toInt(aMinSlots)), maxHubs(Util::toInt(aMaxHubs)), maxUsers(Util::toInt(aMaxUsers))
+ {
+
+ }
+
+ HubEntry() throw() { };
+ HubEntry(const HubEntry& rhs) throw() : name(rhs.name), server(rhs.server), description(rhs.description), country(rhs.country),
+ rating(rhs.rating), reliability(rhs.reliability), shared(rhs.shared), minShare(rhs.minShare), users(rhs.users), minSlots(rhs.minSlots),
+ maxHubs(rhs.maxHubs), maxUsers(rhs.maxUsers) { }
+
+ ~HubEntry() throw() { };
+
+ GETSET(string, name, Name);
+ GETSET(string, server, Server);
+ GETSET(string, description, Description);
+ GETSET(string, country, Country);
+ GETSET(string, rating, Rating);
+ GETSET(float, reliability, Reliability);
+ GETSET(int64_t, shared, Shared);
+ GETSET(int64_t, minShare, MinShare);
+ GETSET(int, users, Users);
+ GETSET(int, minSlots, MinSlots);
+ GETSET(int, maxHubs, MaxHubs)
+ GETSET(int, maxUsers, MaxUsers);
+};
+
+class FavoriteHubEntry {
+public:
+ typedef FavoriteHubEntry* Ptr;
+ typedef vector<Ptr> List;
+ typedef List::iterator Iter;
+
+ FavoriteHubEntry() throw() : connect(false), bottom(0), top(0), left(0), right(0){ };
+ FavoriteHubEntry(const HubEntry& rhs) throw() : name(rhs.getName()), server(rhs.getServer()), description(rhs.getDescription()), connect(false), bottom(0), top(0), left(0), right(0){ };
+ FavoriteHubEntry(const FavoriteHubEntry& rhs) throw() : userdescription(rhs.userdescription), name(rhs.getName()), server(rhs.getServer()), description(rhs.getDescription()),
+ password(rhs.getPassword()), connect(rhs.getConnect()), bottom(rhs.getBottom()), top(rhs.getTop()), left(rhs.getLeft()), right(rhs.getRight()), nick(rhs.nick){ };
+ ~FavoriteHubEntry() throw() { }
+
+ const string& getNick(bool useDefault = true) const {
+ return (!nick.empty() || !useDefault) ? nick : SETTING(NICK);
+ }
+
+ void setNick(const string& aNick) { nick = aNick; };
+
+ GETSET(string, userdescription, UserDescription);
+ GETSET(string, name, Name);
+ GETSET(string, server, Server);
+ GETSET(string, description, Description);
+ GETSET(string, password, Password);
+ GETSET(bool, connect, Connect);
+
+ GETSET(u_int16_t, bottom, Bottom);
+ GETSET(u_int16_t, top, Top);
+ GETSET(u_int16_t, left, Left);
+ GETSET(u_int16_t, right, Right);
+
+
+private:
+ string nick;
+};
+
+class HubManagerListener {
+public:
+ template<int I> struct X { enum { TYPE = I }; };
+
+ typedef X<0> DownloadStarting;
+ typedef X<1> DownloadFailed;
+ typedef X<2> DownloadFinished;
+ typedef X<3> FavoriteAdded;
+ typedef X<4> FavoriteRemoved;
+ typedef X<5> UserAdded;
+ typedef X<6> UserRemoved;
+
+ virtual void on(DownloadStarting, const string&) throw() { }
+ virtual void on(DownloadFailed, const string&) throw() { }
+ virtual void on(DownloadFinished, const string&) throw() { }
+ virtual void on(FavoriteAdded, const FavoriteHubEntry*) throw() { }
+ virtual void on(FavoriteRemoved, const FavoriteHubEntry*) throw() { }
+ virtual void on(UserAdded, const User::Ptr&) throw() { }
+ virtual void on(UserRemoved, const User::Ptr&) throw() { }
+};
+
+class SimpleXML;
+
+/**
+ * Public hub list, favorites (hub&user). Assumed to be called only by UI thread.
+ */
+class HubManager : public Speaker<HubManagerListener>, private HttpConnectionListener, public Singleton<HubManager>,
+ private SettingsManagerListener
+{
+public:
+// Public Hubs
+ enum HubTypes {
+ TYPE_NORMAL,
+ TYPE_BZIP2
+ };
+ StringList getHubLists();
+ bool setHubList(int /*aHubList*/);
+ int getSelectedHubList() { return lastServer; };
+ void refresh();
+ HubTypes getHubListType() { return listType; };
+ HubEntry::List getPublicHubs() {
+ Lock l(cs);
+ return publicListMatrix[publicListServer];
+ }
+ bool isDownloading() { return running; };
+
+// Favorite Users
+ User::List& getFavoriteUsers() { return users; };
+
+ void addFavoriteUser(User::Ptr& aUser);
+ void removeFavoriteUser(User::Ptr& aUser);
+
+// Favorite Hubs
+ FavoriteHubEntry::List& getFavoriteHubs() { return favoriteHubs; };
+
+ void addFavorite(const FavoriteHubEntry& aEntry);
+ void removeFavorite(FavoriteHubEntry* entry);
+
+// Favorite Directories
+ bool addFavoriteDir(const string& aDirectory, const string& aName);
+ bool removeFavoriteDir(const string& aName);
+ bool renameFavoriteDir(const string& aName, const string& anotherName);
+ StringPairList getFavoriteDirs() { return favoriteDirs; }
+
+ FavoriteHubEntry* getFavoriteHubEntry(const string& aServer) {
+ for(FavoriteHubEntry::Iter i = favoriteHubs.begin(); i != favoriteHubs.end(); ++i) {
+ FavoriteHubEntry* hub = *i;
+ if(Util::stricmp(hub->getServer(), aServer) == 0) {
+ return hub;
+ }
+ }
+ return NULL;
+ }
+
+// User Commands
+ UserCommand addUserCommand(int type, int ctx, int flags, const string& name, const string& command, const string& hub);
+ bool getUserCommand(int cid, UserCommand& uc);
+ bool moveUserCommand(int cid, int pos);
+ void updateUserCommand(const UserCommand& uc);
+ void removeUserCommand(int cid);
+ void removeUserCommand(const string& srv);
+ void removeHubUserCommands(int ctx, const string& hub);
+
+ UserCommand::List getUserCommands() { Lock l(cs); return userCommands; };
+ UserCommand::List getUserCommands(int ctx, const string& hub, bool op);
+
+ void load();
+ void save();
+
+private:
+ FavoriteHubEntry::List favoriteHubs;
+ StringPairList favoriteDirs;
+ UserCommand::List userCommands;
+ int lastId;
+
+ User::List users;
+
+ CriticalSection cs;
+
+ // Public Hubs
+ typedef map<string, HubEntry::List> PubListMap;
+ PubListMap publicListMatrix;
+ string publicListServer;
+ bool running;
+ HttpConnection* c;
+ int lastServer;
+ HubTypes listType;
+ string downloadBuf;
+
+ /** Used during loading to prevent saving. */
+ bool dontSave;
+
+ friend class Singleton<HubManager>;
+
+ HubManager() : lastId(0), running(false), c(NULL), lastServer(0), listType(TYPE_NORMAL), dontSave(false) {
+ SettingsManager::getInstance()->addListener(this);
+ }
+
+ virtual ~HubManager() throw() {
+ SettingsManager::getInstance()->removeListener(this);
+ if(c) {
+ c->removeListener(this);
+ delete c;
+ c = NULL;
+ }
+
+ for_each(favoriteHubs.begin(), favoriteHubs.end(), DeleteFunction<FavoriteHubEntry*>());
+ }
+
+ FavoriteHubEntry::Iter getFavoriteHub(const string& aServer) {
+ for(FavoriteHubEntry::Iter i = favoriteHubs.begin(); i != favoriteHubs.end(); ++i) {
+ if(Util::stricmp((*i)->getServer(), aServer) == 0) {
+ return i;
+ }
+ }
+ return favoriteHubs.end();
+ }
+
+ void loadXmlList(const string& xml);
+
+ // HttpConnectionListener
+ virtual void on(Data, HttpConnection*, const u_int8_t*, size_t) throw();
+ virtual void on(Failed, HttpConnection*, const string&) throw();
+ virtual void on(Complete, HttpConnection*, const string&) throw();
+ virtual void on(Redirected, HttpConnection*, const string&) throw();
+ virtual void on(TypeNormal, HttpConnection*) throw();
+ virtual void on(TypeBZ2, HttpConnection*) throw();
+
+ void onHttpFinished() throw();
+
+ // SettingsManagerListener
+ virtual void on(SettingsManagerListener::Load, SimpleXML* xml) throw() {
+ load(xml);
+ }
+
+ void load(SimpleXML* aXml);
+
+};
+
+#endif // !defined(AFX_HUBMANAGER_H__75858D5D_F12F_40D0_B127_5DDED226C098__INCLUDED_)
+
+/**
+ * @file
+ * $Id: HubManager.h,v 1.2 2005/08/21 14:03:43 olof Exp $
+ */
+
diff --git a/dcpp/LogManager.cpp b/dcpp/LogManager.cpp
new file mode 100644
index 0000000..6d3bd81
--- /dev/null
+++ b/dcpp/LogManager.cpp
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2001-2005 Jacek Sieka, arnetheduck on gmail point 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 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.
+ */
+
+#include "stdinc.h"
+#include "DCPlusPlus.h"
+
+#include "LogManager.h"
+
+/**
+ * @file
+ * $Id: LogManager.cpp,v 1.2 2005/08/21 14:03:43 olof Exp $
+ */
diff --git a/dcpp/LogManager.h b/dcpp/LogManager.h
new file mode 100644
index 0000000..f80bc59
--- /dev/null
+++ b/dcpp/LogManager.h
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2001-2005 Jacek Sieka, arnetheduck on gmail point 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 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.
+ */
+
+#if !defined(AFX_LOGMANAGER_H__73C7E0F5_5C7D_4A2A_827B_53267D0EF4C5__INCLUDED_)
+#define AFX_LOGMANAGER_H__73C7E0F5_5C7D_4A2A_827B_53267D0EF4C5__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+#include "File.h"
+#include "CriticalSection.h"
+#include "Singleton.h"
+#include "TimerManager.h"
+
+class LogManagerListener {
+public:
+ template<int I> struct X { enum { TYPE = I }; };
+
+ typedef X<0> Message;
+ virtual void on(Message, const string&) throw() { };
+};
+
+class LogManager : public Singleton<LogManager>, public Speaker<LogManagerListener>
+{
+public:
+ enum LogArea { CHAT, PM, DOWNLOAD, UPLOAD, SYSTEM, STATUS, LAST };
+ enum {FILE, FORMAT};
+
+ void log(LogArea area, StringMap& params) throw() {
+ string path = SETTING(LOG_DIRECTORY);
+ string msg;
+
+ path += Util::formatParams(getSetting(area, FILE), params);
+ msg = Util::formatParams(getSetting(area, FORMAT), params);
+
+ log(path, msg);
+ }
+
+ void message(const string& msg) {
+ if(BOOLSETTING(LOG_SYSTEM)) {
+ StringMap params;
+ params["message"] = msg;
+ log(LogManager::SYSTEM, params);
+ }
+ fire(LogManagerListener::Message(), msg);
+ }
+
+ const string& getSetting(int area, int sel) {
+ return SettingsManager::getInstance()->get(static_cast<SettingsManager::StrSetting>(logOptions[area][sel]), true);
+ }
+
+ void saveSetting(int area, int sel, const string& setting) {
+ SettingsManager::getInstance()->set(static_cast<SettingsManager::StrSetting>(logOptions[area][sel]), setting);
+ }
+
+private:
+ void log(const string& area, const string& msg) throw() {
+ Lock l(cs);
+ try {
+ string aArea = Util::validateFileName(area);
+ File::ensureDirectory(aArea);
+ File f(aArea, File::WRITE, File::OPEN | File::CREATE);
+ f.setEndPos(0);
+ f.write(msg + "\r\n");
+ } catch (const FileException&) {
+ // ...
+ }
+ }
+
+ friend class Singleton<LogManager>;
+ CriticalSection cs;
+
+ int logOptions[LAST][2];
+
+ LogManager() {
+ logOptions[UPLOAD][FILE] = SettingsManager::LOG_FILE_UPLOAD;
+ logOptions[UPLOAD][FORMAT] = SettingsManager::LOG_FORMAT_POST_UPLOAD;
+ logOptions[DOWNLOAD][FILE] = SettingsManager::LOG_FILE_DOWNLOAD;
+ logOptions[DOWNLOAD][FORMAT] = SettingsManager::LOG_FORMAT_POST_DOWNLOAD;
+ logOptions[CHAT][FILE] = SettingsManager::LOG_FILE_MAIN_CHAT;
+ logOptions[CHAT][FORMAT] = SettingsManager::LOG_FORMAT_MAIN_CHAT;
+ logOptions[PM][FILE] = SettingsManager::LOG_FILE_PRIVATE_CHAT;
+ logOptions[PM][FORMAT] = SettingsManager::LOG_FORMAT_PRIVATE_CHAT;
+ logOptions[SYSTEM][FILE] = SettingsManager::LOG_FILE_SYSTEM;
+ logOptions[SYSTEM][FORMAT] = SettingsManager::LOG_FORMAT_SYSTEM;
+ logOptions[STATUS][FILE] = SettingsManager::LOG_FILE_STATUS;
+ logOptions[STATUS][FORMAT] = SettingsManager::LOG_FORMAT_STATUS;
+ };
+ virtual ~LogManager() throw() { };
+
+};
+
+#define LOG(area, msg) LogManager::getInstance()->log(area, msg)
+
+#endif // !defined(AFX_LOGMANAGER_H__73C7E0F5_5C7D_4A2A_827B_53267D0EF4C5__INCLUDED_)
+
+/**
+ * @file
+ * $Id: LogManager.h,v 1.2 2005/08/21 14:03:43 olof Exp $
+ */
diff --git a/dcpp/MerkleCheckOutputStream.h b/dcpp/MerkleCheckOutputStream.h
new file mode 100644
index 0000000..19f27b8
--- /dev/null
+++ b/dcpp/MerkleCheckOutputStream.h
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2001-2005 Jacek Sieka, arnetheduck on gmail point 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 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.
+ */
+
+#include "Streams.h"
+#include "MerkleTree.h"
+
+template<class TreeType, bool managed>
+class MerkleCheckOutputStream : public OutputStream {
+public:
+ MerkleCheckOutputStream(const TreeType& aTree, OutputStream* aStream, int64_t start) : s(aStream), real(aTree), cur(aTree.getBlockSize()), verified(0), bufPos(0) {
+ // Only start at block boundaries
+ dcassert(start % aTree.getBlockSize() == 0);
+ // Sanity check
+ dcassert(aTree.getLeaves().size() > (size_t)(start / aTree.getBlockSize()));
+ cur.setFileSize(start);
+ cur.getLeaves().insert(cur.getLeaves().begin(), aTree.getLeaves().begin(), aTree.getLeaves().begin() + (size_t)(start / aTree.getBlockSize()));
+ }
+
+ virtual ~MerkleCheckOutputStream() throw() { if(managed) delete s; };
+
+ virtual size_t flush() throw(FileException) {
+ if (bufPos != 0)
+ cur.update(buf, bufPos);
+ bufPos = 0;
+
+ cur.finalize();
+ if(cur.getLeaves().size() == real.getLeaves().size()) {
+ if (cur.getRoot() != real.getRoot())
+ throw FileException(STRING(TTH_INCONSISTENCY));
+ } else {
+ checkTrees();
+ }
+ return s->flush();
+ }
+
+ virtual size_t write(const void* b, size_t len) throw(FileException) {
+ u_int8_t* xb = (u_int8_t*)b;
+ size_t pos = 0;
+
+ if(bufPos != 0) {
+ size_t bytes = min(TreeType::BASE_BLOCK_SIZE - bufPos, len);
+ memcpy(buf + bufPos, xb, bytes);
+ pos = bytes;
+ bufPos += bytes;
+
+ if(bufPos == TreeType::BASE_BLOCK_SIZE) {
+ cur.update(buf, TreeType::BASE_BLOCK_SIZE);
+ bufPos = 0;
+ }
+ }
+
+ if(pos < len) {
+ dcassert(bufPos == 0);
+ size_t left = len - pos;
+ size_t part = left - (left % TreeType::BASE_BLOCK_SIZE);
+ if(part > 0) {
+ cur.update(xb + pos, part);
+ pos += part;
+ }
+ left = len - pos;
+ memcpy(buf, xb + pos, left);
+ bufPos = left;
+ }
+
+ checkTrees();
+ return s->write(b, len);
+ }
+
+ int64_t verifiedBytes() {
+ return min(real.getFileSize(), (int64_t)(cur.getBlockSize() * cur.getLeaves().size()));
+ }
+private:
+ OutputStream* s;
+ const TreeType& real;
+ TreeType cur;
+ size_t verified;
+
+ u_int8_t buf[TreeType::BASE_BLOCK_SIZE];
+ size_t bufPos;
+
+ void checkTrees() throw(FileException) {
+ while(cur.getLeaves().size() > verified) {
+ if(cur.getLeaves().size() > real.getLeaves().size() ||
+ !(cur.getLeaves()[verified] == real.getLeaves()[verified]))
+ {
+ throw FileException(STRING(TTH_INCONSISTENCY));
+ }
+ verified++;
+ }
+ }
+};
+
+/**
+ * @file
+ * $Id: MerkleCheckOutputStream.h,v 1.2 2005/08/21 14:03:43 olof Exp $
+ */
diff --git a/dcpp/MerkleTree.h b/dcpp/MerkleTree.h
new file mode 100644
index 0000000..40fe436
--- /dev/null
+++ b/dcpp/MerkleTree.h
@@ -0,0 +1,233 @@
+/*
+ * Copyright (C) 2001-2005 Jacek Sieka, arnetheduck on gmail point 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 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.
+ */
+
+#ifndef _MERKLE_TREE
+#define _MERKLE_TREE
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+#include "TigerHash.h"
+#include "Encoder.h"
+#include "HashValue.h"
+
+/**
+ * A class that represents a Merkle Tree hash. Storing
+ * only the leaves of the tree, it is rather memory efficient,
+ * but can still take a significant amount of memory during / after
+ * hash generation.
+ * The root hash produced can be used like any
+ * other hash to verify the integrity of a whole file, while
+ * the leaves provide checking of smaller parts of the file.
+ */
+template<class Hasher, size_t baseBlockSize = 1024>
+class MerkleTree {
+public:
+ enum { HASH_SIZE = Hasher::HASH_SIZE };
+ enum { BASE_BLOCK_SIZE = baseBlockSize };
+
+ typedef HashValue<Hasher> MerkleValue;
+ typedef vector<MerkleValue> MerkleList;
+ typedef typename MerkleList::iterator MerkleIter;
+
+ MerkleTree() : fileSize(0), blockSize(baseBlockSize) { }
+ MerkleTree(int64_t aBlockSize) : fileSize(0), blockSize(aBlockSize) { }
+
+ /**
+ * Loads a set of leaf hashes, calculating the root
+ * @param data Pointer to (aFileSize + aBlockSize - 1) / aBlockSize) hash values,
+ * stored consecutively left to right
+ */
+ MerkleTree(int64_t aFileSize, int64_t aBlockSize, u_int8_t* aData) :
+ fileSize(aFileSize), blockSize(aBlockSize)
+ {
+ size_t n = calcBlocks(aFileSize, aBlockSize);
+ for(size_t i = 0; i < n; i++)
+ leaves.push_back(MerkleValue(aData + i * Hasher::HASH_SIZE));
+
+ calcRoot();
+ }
+
+ /** Initialise a single root tree */
+ MerkleTree(int64_t aFileSize, int64_t aBlockSize, const MerkleValue& aRoot) : root(aRoot), fileSize(aFileSize), blockSize(aBlockSize) {
+ leaves.push_back(root);
+ }
+
+ ~MerkleTree() {
+ }
+
+ static int64_t calcBlockSize(int64_t aFileSize, int maxLevels) {
+ int64_t tmp = baseBlockSize;
+ int64_t maxHashes = ((int64_t)1) << (maxLevels - 1);
+ while((maxHashes * tmp) < aFileSize)
+ tmp *= 2;
+ return tmp;
+ }
+
+ static size_t calcBlocks(int64_t aFileSize, int64_t aBlockSize) {
+ return max((size_t)((aFileSize + aBlockSize - 1) / aBlockSize), (size_t)1);
+ }
+
+ /**
+ * Update the merkle tree.
+ * @param len Length of data, must be a multiple of baseBlockSize, unless it's
+ * the last block.
+ */
+ void update(const void* data, size_t len) {
+ u_int8_t* buf = (u_int8_t*)data;
+ u_int8_t zero = 0;
+ size_t i = 0;
+
+ // Skip empty data sets if we already added at least one of them...
+ if(len == 0 && !(leaves.empty() && blocks.empty()))
+ return;
+
+ do {
+ size_t n = min(baseBlockSize, len-i);
+ Hasher h;
+ h.update(&zero, 1);
+ h.update(buf + i, n);
+ if((int64_t)baseBlockSize < blockSize) {
+ blocks.push_back(make_pair(MerkleValue(h.finalize()), baseBlockSize));
+ reduceBlocks();
+ } else {
+ leaves.push_back(MerkleValue(h.finalize()));
+ }
+ i += n;
+ } while(i < len);
+ fileSize += len;
+ }
+
+ u_int8_t* finalize() {
+ while(blocks.size() > 1) {
+ MerkleBlock& a = blocks[blocks.size()-2];
+ MerkleBlock& b = blocks[blocks.size()-1];
+ a.first = combine(a.first, b.first);
+ blocks.pop_back();
+ }
+ dcassert(blocks.size() == 0 || blocks.size() == 1);
+ if(!blocks.empty()) {
+ leaves.push_back(blocks[0].first);
+ }
+ calcRoot();
+ return root.data;
+ }
+
+ MerkleValue& getRoot() { return root; }
+ const MerkleValue& getRoot() const { return root; }
+ MerkleList& getLeaves() { return leaves; }
+ const MerkleList& getLeaves() const { return leaves; }
+
+ int64_t getBlockSize() const { return blockSize; }
+ void setBlockSize(int64_t aSize) { blockSize = aSize; }
+
+ int64_t getFileSize() const { return fileSize; }
+ void setFileSize(int64_t aSize) { fileSize = aSize; }
+
+ bool verifyRoot(const u_int8_t* aRoot) {
+ return memcmp(aRoot, getRoot().data(), HASH_SIZE) == 0;
+ }
+
+ void calcRoot() {
+ root = getHash(0, fileSize);
+ }
+
+ vector<u_int8_t> getLeafData() {
+ vector<u_int8_t> buf(getLeaves().size() * HASH_SIZE);
+ u_int8_t* p = &buf[0];
+ for(size_t i = 0; i < getLeaves().size(); ++i) {
+ memcpy(p + i * HASH_SIZE, &getLeaves()[i], HASH_SIZE);
+ }
+ return buf;
+ }
+
+private:
+ typedef pair<MerkleValue, int64_t> MerkleBlock;
+ typedef vector<MerkleBlock> MBList;
+
+ MBList blocks;
+
+ MerkleList leaves;
+
+ MerkleValue root;
+ /** Total size of hashed data */
+ int64_t fileSize;
+ /** Final block size */
+ int64_t blockSize;
+
+ MerkleValue getHash(int64_t start, int64_t length) {
+ dcassert((start % blockSize) == 0);
+ if(length <= blockSize) {
+ dcassert((start / blockSize) < (int64_t)leaves.size());
+ return leaves[(u_int32_t)(start / blockSize)];
+ } else {
+ int64_t l = blockSize;
+ while(l * 2 < length)
+ l *= 2;
+ return combine(getHash(start, l), getHash(start+l, length - l));
+ }
+ }
+
+ MerkleValue combine(const MerkleValue& a, const MerkleValue& b) {
+ u_int8_t one = 1;
+ Hasher h;
+ h.update(&one, 1);
+ h.update(a.data, MerkleValue::SIZE);
+ h.update(b.data, MerkleValue::SIZE);
+ return MerkleValue(h.finalize());
+ }
+
+ void reduceBlocks() {
+ while(blocks.size() > 1) {
+ MerkleBlock& a = blocks[blocks.size()-2];
+ MerkleBlock& b = blocks[blocks.size()-1];
+ if(a.second == b.second) {
+ if(a.second*2 == blockSize) {
+ leaves.push_back(combine(a.first, b.first));
+ blocks.pop_back();
+ blocks.pop_back();
+ } else {
+ a.second *= 2;
+ a.first = combine(a.first, b.first);
+ blocks.pop_back();
+ }
+ } else {
+ break;
+ }
+ }
+ }
+};
+
+typedef MerkleTree<TigerHash> TigerTree;
+typedef TigerTree::MerkleValue TTHValue;
+
+struct TTFilter {
+ TTFilter(int64_t aBlockSize) : tt(aBlockSize) { };
+ void operator()(const void* data, size_t len) { tt.update(data, len); }
+ TigerTree& getTree() { tt.finalize(); return tt; }
+private:
+ TigerTree tt;
+};
+
+#endif // _MERKLE_TREE
+
+/**
+ * @file
+ * $Id: MerkleTree.h,v 1.2 2005/08/21 14:03:43 olof Exp $
+ */
diff --git a/dcpp/NmdcHub.cpp b/dcpp/NmdcHub.cpp
new file mode 100644
index 0000000..7e4b80d
--- /dev/null
+++ b/dcpp/NmdcHub.cpp
@@ -0,0 +1,723 @@
+/*
+ * Copyright (C) 2001-2005 Jacek Sieka, arnetheduck on gmail point 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 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.
+ */
+
+#include "stdinc.h"
+#include "DCPlusPlus.h"
+
+#include "NmdcHub.h"
+
+#include "ResourceManager.h"
+#include "ClientManager.h"
+#include "SearchManager.h"
+#include "ShareManager.h"
+#include "CryptoManager.h"
+#include "ConnectionManager.h"
+
+#include "Socket.h"
+#include "UserCommand.h"
+#include "StringTokenizer.h"
+
+
+NmdcHub::NmdcHub(const string& aHubURL) : Client(aHubURL, '|'), supportFlags(0),
+ adapter(this), state(STATE_CONNECT),
+ lastActivity(GET_TICK()),
+ reconnect(true), lastUpdate(0)
+{
+ TimerManager::getInstance()->addListener(this);
+}
+
+NmdcHub::~NmdcHub() throw() {
+ TimerManager::getInstance()->removeListener(this);
+ Speaker<NmdcHubListener>::removeListeners();
+
+ Lock l(cs);
+ clearUsers();
+}
+
+void NmdcHub::connect() {
+ setRegistered(false);
+ setReconnDelay(120 + Util::rand(0, 60));
+ reconnect = true;
+ supportFlags = 0;
+ lastMyInfoA.clear();
+ lastMyInfoB.clear();
+ lastUpdate = 0;
+
+ if(socket->isConnected()) {
+ disconnect();
+ }
+
+ reloadSettings();
+
+ state = STATE_LOCK;
+
+ if(getPort() == 0) {
+ setPort(411);
+ }
+ socket->connect(getAddress(), getPort());
+}
+
+void NmdcHub::connect(const User* aUser) {
+ checkstate();
+ dcdebug("NmdcHub::connectToMe %s\n", aUser->getNick().c_str());
+ if(SETTING(CONNECTION_TYPE) == SettingsManager::CONNECTION_ACTIVE) {
+ send("$ConnectToMe " + toNmdc(aUser->getNick()) + " " + getLocalIp() + ":" + Util::toString(SETTING(IN_PORT)) + "|");
+ } else {
+ send("$RevConnectToMe " + toNmdc(getNick()) + " " + toNmdc(aUser->getNick()) + "|");
+ }
+}
+
+int64_t NmdcHub::getAvailable() const {
+ Lock l(cs);
+ int64_t x = 0;
+ for(User::NickMap::const_iterator i = users.begin(); i != users.end(); ++i) {
+ x+=i->second->getBytesShared();
+ }
+ return x;
+}
+
+void NmdcHub::refreshUserList(bool unknownOnly /* = false */) {
+ Lock l(cs);
+ if(unknownOnly) {
+ for(User::NickIter i = users.begin(); i != users.end(); ++i) {
+ if(i->second->getConnection().empty()) {
+ getInfo(i->second);
+ }
+ }
+ } else {
+ clearUsers();
+ getNickList();
+ }
+}
+
+void NmdcHub::clearUsers() {
+ for(User::NickIter i = users.begin(); i != users.end(); ++i) {
+ ClientManager::getInstance()->putUserOffline(i->second);
+ }
+ users.clear();
+}
+
+void NmdcHub::onLine(const string& aLine) throw() {
+ lastActivity = GET_TICK();
+
+ if(aLine.length() == 0)
+ return;
+
+ if(aLine[0] != '$') {
+ // Check if we're being banned...
+ if(state != STATE_CONNECTED) {
+ if(Util::findSubString(aLine, "banned") != string::npos) {
+ reconnect = false;
+ }
+ }
+
+ Speaker<NmdcHubListener>::fire(NmdcHubListener::Message(), this, Util::validateMessage(fromNmdc(aLine), true));
+ return;
+ }
+
+ string cmd;
+ string param;
+ string::size_type x;
+
+ if( (x = aLine.find(' ')) == string::npos) {
+ cmd = aLine;
+ } else {
+ cmd = aLine.substr(0, x);
+ param = aLine.substr(x+1);
+ }
+
+ if(cmd == "$Search") {
+ if(state != STATE_CONNECTED) {
+ return;
+ }
+ string::size_type i = 0;
+ string::size_type j = param.find(' ', i);
+ if(j == string::npos || i == j)
+ return;
+
+ string seeker = fromNmdc(param.substr(i, j-i));
+
+ // Filter own searches
+ if(SETTING(CONNECTION_TYPE) == SettingsManager::CONNECTION_ACTIVE) {
+ if(seeker == (getLocalIp() + ":" + Util::toString(SETTING(UDP_PORT)))) {
+ return;
+ }
+ } else {
+ // Hub:seeker
+ if(Util::stricmp(seeker.c_str() + 4, getNick().c_str()) == 0) {
+ return;
+ }
+ }
+
+ i = j + 1;
+
+ {
+ Lock l(cs);
+ u_int32_t tick = GET_TICK();
+
+ seekers.push_back(make_pair(seeker, tick));
+
+ // First, check if it's a flooder
+ FloodIter fi;
+ for(fi = flooders.begin(); fi != flooders.end(); ++fi) {
+ if(fi->first == seeker) {
+ return;
+ }
+ }
+
+ int count = 0;
+ for(fi = seekers.begin(); fi != seekers.end(); ++fi) {
+ if(fi->first == seeker)
+ count++;
+
+ if(count > 7) {
+ if(seeker.compare(0, 4, "Hub:") == 0)
+ Speaker<NmdcHubListener>::fire(NmdcHubListener::SearchFlood(), this, seeker.substr(4));
+ else
+ Speaker<NmdcHubListener>::fire(NmdcHubListener::SearchFlood(), this, seeker + STRING(NICK_UNKNOWN));
+
+ flooders.push_back(make_pair(seeker, tick));
+ return;
+ }
+ }
+ }
+
+ int a;
+ if(param[i] == 'F') {
+ a = SearchManager::SIZE_DONTCARE;
+ } else if(param[i+2] == 'F') {
+ a = SearchManager::SIZE_ATLEAST;
+ } else {
+ a = SearchManager::SIZE_ATMOST;
+ }
+ i += 4;
+ j = param.find('?', i);
+ if(j == string::npos || i == j)
+ return;
+ string size = param.substr(i, j-i);
+ i = j + 1;
+ j = param.find('?', i);
+ if(j == string::npos || i == j)
+ return;
+ int type = Util::toInt(param.substr(i, j-i)) - 1;
+ i = j + 1;
+ param = param.substr(i);
+
+ if(param.size() > 0) {
+ Speaker<NmdcHubListener>::fire(NmdcHubListener::Search(), this, seeker, a, Util::toInt64(size), type, fromNmdc(param));
+
+ if(seeker.compare(0, 4, "Hub:") == 0) {
+ User::Ptr u;
+ {
+ Lock l(cs);
+ User::NickIter ni = users.find(seeker.substr(4));
+ if(ni != users.end() && !ni->second->isSet(User::PASSIVE)) {
+ u = ni->second;
+ u->setFlag(User::PASSIVE);
+ }
+ }
+
+ if(u) {
+ updated(u);
+ }
+ }
+ }
+ } else if(cmd == "$MyINFO") {
+ string::size_type i, j;
+ i = 5;
+ j = param.find(' ', i);
+ if( (j == string::npos) || (j == i) )
+ return;
+ string nick = fromNmdc(param.substr(i, j-i));
+ i = j + 1;
+ User::Ptr u;
+ dcassert(nick.size() > 0);
+
+ {
+ Lock l(cs);
+ User::NickIter ni = users.find(nick);
+ if(ni == users.end()) {
+ u = users[nick] = ClientManager::getInstance()->getUser(nick, this);
+ } else {
+ u = ni->second;
+ }
+ }
+ j = param.find('$', i);
+ if(j == string::npos)
+ return;
+ string tmpDesc = Util::validateMessage(fromNmdc(param.substr(i, j-i)), true);
+ // Look for a tag...
+ if(tmpDesc.size() > 0 && tmpDesc[tmpDesc.size()-1] == '>') {
+ x = tmpDesc.rfind('<');
+ if(x != string::npos) {
+ // Hm, we have something...
+ u->setTag(tmpDesc.substr(x));
+ tmpDesc.erase(x);
+ } else {
+ u->setTag(Util::emptyString);
+ }
+ } else {
+ u->setTag(Util::emptyString);
+ }
+ u->setDescription(tmpDesc);
+ i = j + 3;
+ j = param.find('$', i);
+ if(j == string::npos)
+ return;
+ u->setConnection(fromNmdc(param.substr(i, j-i-1)));
+ i = j + 1;
+ j = param.find('$', i);
+ if(j == string::npos)
+ return;
+ u->setEmail(Util::validateMessage(fromNmdc(param.substr(i, j-i)), true));
+ i = j + 1;
+ j = param.find('$', i);
+ if(j == string::npos)
+ return;
+ u->setBytesShared(param.substr(i, j-i));
+
+ Speaker<NmdcHubListener>::fire(NmdcHubListener::MyInfo(), this, u);
+ } else if(cmd == "$Quit") {
+ if(!param.empty()) {
+ User::Ptr u;
+ {
+ Lock l(cs);
+ User::NickIter i = users.find(fromNmdc(param));
+ if(i == users.end()) {
+ dcdebug("C::onLine Quitting user %s not found\n", param.c_str());
+ return;
+ }
+
+ u = i->second;
+ users.erase(i);
+ }
+
+ Speaker<NmdcHubListener>::fire(NmdcHubListener::Quit(), this, u);
+ ClientManager::getInstance()->putUserOffline(u, true);
+ }
+ } else if(cmd == "$ConnectToMe") {
+ if(state != STATE_CONNECTED) {
+ return;
+ }
+ string::size_type i = param.find(' ');
+ string::size_type j;
+ if( (i == string::npos) || ((i + 1) >= param.size()) ) {
+ return;
+ }
+ i++;
+ j = param.find(':', i);
+ if(j == string::npos) {
+ return;
+ }
+ string server = fromNmdc(param.substr(i, j-i));
+ if(j+1 >= param.size()) {
+ return;
+ }
+ string port = param.substr(j+1);
+ ConnectionManager::getInstance()->nmdcConnect(server, (short)Util::toInt(port), getNick());
+ Speaker<NmdcHubListener>::fire(NmdcHubListener::ConnectToMe(), this, server, (short)Util::toInt(port));
+ } else if(cmd == "$RevConnectToMe") {
+ if(state != STATE_CONNECTED) {
+ return;
+ }
+ User::Ptr u;
+ bool up = false;
+ {
+ Lock l(cs);
+ string::size_type j = param.find(' ');
+ if(j == string::npos) {
+ return;
+ }
+
+ User::NickIter i = users.find(fromNmdc(param.substr(0, j)));
+ if(i == users.end()) {
+ return;
+ }
+
+ u = i->second;
+ if(!u->isSet(User::PASSIVE)) {
+ u->setFlag(User::PASSIVE);
+ up = true;
+ }
+ }
+
+ if(u) {
+ if(SETTING(CONNECTION_TYPE) == SettingsManager::CONNECTION_ACTIVE) {
+ connectToMe(u);
+ Speaker<NmdcHubListener>::fire(NmdcHubListener::RevConnectToMe(), this, u);
+ } else {
+ // Notify the user that we're passive too...
+ if(up)
+ revConnectToMe(u);
+ }
+
+ if(up)
+ updated(u);
+ }
+ } else if(cmd == "$SR") {
+ SearchManager::getInstance()->onSearchResult(aLine);
+ } else if(cmd == "$HubName") {
+ name = fromNmdc(param);
+ Speaker<NmdcHubListener>::fire(NmdcHubListener::HubName(), this);
+ } else if(cmd == "$Supports") {
+ StringTokenizer<string> st(param, ' ');
+ StringList& sl = st.getTokens();
+ for(StringIter i = sl.begin(); i != sl.end(); ++i) {
+ if(*i == "UserCommand") {
+ supportFlags |= SUPPORTS_USERCOMMAND;
+ } else if(*i == "NoGetINFO") {
+ supportFlags |= SUPPORTS_NOGETINFO;
+ } else if(*i == "UserIP2") {
+ supportFlags |= SUPPORTS_USERIP2;
+ }
+ }
+ Speaker<NmdcHubListener>::fire(NmdcHubListener::Supports(), this, sl);
+ } else if(cmd == "$UserCommand") {
+ string::size_type i = 0;
+ string::size_type j = param.find(' ');
+ if(j == string::npos)
+ return;
+
+ int type = Util::toInt(param.substr(0, j));
+ i = j+1;
+ if(type == UserCommand::TYPE_SEPARATOR || type == UserCommand::TYPE_CLEAR) {
+ int ctx = Util::toInt(param.substr(i));
+ Speaker<NmdcHubListener>::fire(NmdcHubListener::UserCommand(), this, type, ctx, Util::emptyString, Util::emptyString);
+ } else if(type == UserCommand::TYPE_RAW || type == UserCommand::TYPE_RAW_ONCE) {
+ j = param.find(' ', i);
+ if(j == string::npos)
+ return;
+ int ctx = Util::toInt(param.substr(i));
+ i = j+1;
+ j = param.find('$');
+ if(j == string::npos)
+ return;
+ string name = fromNmdc(param.substr(i, j-i));
+ i = j+1;
+ string command = fromNmdc(param.substr(i, param.length() - i));
+ Speaker<NmdcHubListener>::fire(NmdcHubListener::UserCommand(), this, type, ctx, Util::validateMessage(name, true, false), Util::validateMessage(command, true, false));
+ }
+ } else if(cmd == "$Lock") {
+ if(state != STATE_LOCK) {
+ return;
+ }
+ state = STATE_HELLO;
+
+ if(!param.empty()) {
+ string::size_type j = param.find(" Pk=");
+ string lock, pk;
+ if( j != string::npos ) {
+ lock = param.substr(0, j);
+ pk = param.substr(j + 4);
+ } else {
+ // Workaround for faulty linux hubs...
+ j = param.find(" ");
+ if(j != string::npos)
+ lock = param.substr(0, j);
+ else
+ lock = param;
+ }
+
+ if(CryptoManager::getInstance()->isExtended(lock)) {
+ StringList feat;
+ feat.push_back("UserCommand");
+ feat.push_back("NoGetINFO");
+ feat.push_back("NoHello");
+ feat.push_back("UserIP2");
+ feat.push_back("TTHSearch");
+
+ if(BOOLSETTING(COMPRESS_TRANSFERS))
+ feat.push_back("GetZBlock");
+ supports(feat);
+ }
+
+ key(CryptoManager::getInstance()->makeKey(lock));
+ validateNick(getNick());
+
+ Speaker<NmdcHubListener>::fire(NmdcHubListener::CLock(), this, lock, pk);
+ }
+ } else if(cmd == "$Hello") {
+ if(!param.empty()) {
+ string nick = fromNmdc(param);
+ User::Ptr u = ClientManager::getInstance()->getUser(nick, this);
+ {
+ Lock l(cs);
+ users[nick] = u;
+ }
+
+ if(getNick() == nick) {
+ setMe(u);
+
+ u->setFlag(User::DCPLUSPLUS);
+ if(SETTING(CONNECTION_TYPE) != SettingsManager::CONNECTION_ACTIVE)
+ u->setFlag(User::PASSIVE);
+ else
+ u->unsetFlag(User::PASSIVE);
+ }
+
+ if(state == STATE_HELLO) {
+ state = STATE_CONNECTED;
+ updateCounts(false);
+
+ version();
+ getNickList();
+ myInfo(true);
+ }
+
+ Speaker<NmdcHubListener>::fire(NmdcHubListener::Hello(), this, u);
+ }
+ } else if(cmd == "$ForceMove") {
+ disconnect();
+ Speaker<NmdcHubListener>::fire(NmdcHubListener::Redirect(), this, param);
+ } else if(cmd == "$HubIsFull") {
+ Speaker<NmdcHubListener>::fire(NmdcHubListener::HubFull(), this);
+ } else if(cmd == "$ValidateDenide") { // Mind the spelling...
+ disconnect();
+ Speaker<NmdcHubListener>::fire(NmdcHubListener::ValidateDenied(), this);
+ } else if(cmd == "$UserIP") {
+ if(!param.empty()) {
+ User::List v;
+ StringTokenizer<string> t(fromNmdc(param), "$$");
+ StringList& l = t.getTokens();
+ for(StringIter it = l.begin(); it != l.end(); ++it) {
+ string::size_type j = 0;
+ if((j = it->find(' ')) == string::npos)
+ continue;
+ if((j+1) == it->length())
+ continue;
+ v.push_back(ClientManager::getInstance()->getUser(it->substr(0, j), this));
+ v.back()->setIp(it->substr(j+1));
+ }
+
+ Speaker<NmdcHubListener>::fire(NmdcHubListener::UserIp(), this, v);
+ }
+ } else if(cmd == "$NickList") {
+ if(!param.empty()) {
+ User::List v;
+ StringTokenizer<string> t(fromNmdc(param), "$$");
+ StringList& sl = t.getTokens();
+
+ for(StringIter it = sl.begin(); it != sl.end(); ++it) {
+ if(it->empty())
+ continue;
+ v.push_back(ClientManager::getInstance()->getUser(*it, this));
+ }
+
+ {
+ Lock l(cs);
+ for(User::Iter it2 = v.begin(); it2 != v.end(); ++it2) {
+ users[(*it2)->getNick()] = *it2;
+ }
+ }
+
+ if(!(getSupportFlags() & SUPPORTS_NOGETINFO)) {
+ string tmp;
+ // Let's assume 10 characters per nick...
+ tmp.reserve(v.size() * (11 + 10 + getNick().length()));
+ string n = ' ' + toNmdc(getNick()) + '|';
+ for(User::List::const_iterator i = v.begin(); i != v.end(); ++i) {
+ tmp += "$GetINFO ";
+ tmp += toNmdc((*i)->getNick());
+ tmp += n;
+ }
+ if(!tmp.empty()) {
+ send(tmp);
+ }
+ }
+
+ Speaker<NmdcHubListener>::fire(NmdcHubListener::NickList(), this, v);
+ }
+ } else if(cmd == "$OpList") {
+ if(!param.empty()) {
+ User::List v;
+ StringTokenizer<string> t(fromNmdc(param), "$$");
+ StringList& sl = t.getTokens();
+ for(StringIter it = sl.begin(); it != sl.end(); ++it) {
+ if(it->empty())
+ continue;
+ v.push_back(ClientManager::getInstance()->getUser(*it, this));
+ v.back()->setFlag(User::OP);
+ }
+
+ {
+ Lock l(cs);
+ for(User::Iter it2 = v.begin(); it2 != v.end(); ++it2) {
+ users[(*it2)->getNick()] = *it2;
+ }
+ }
+ Speaker<NmdcHubListener>::fire(NmdcHubListener::OpList(), this, v);
+ updateCounts(false);
+ // Special...to avoid op's complaining that their count is not correctly
+ // updated when they log in (they'll be counted as registered first...)
+ myInfo(false);
+ }
+ } else if(cmd == "$To:") {
+ string::size_type i = param.find("From:");
+ if(i != string::npos) {
+ i+=6;
+ string::size_type j = param.find("$");
+ if(j != string::npos) {
+ string from = fromNmdc(param.substr(i, j - 1 - i));
+ if(from.size() > 0 && param.size() > (j + 1)) {
+ Speaker<NmdcHubListener>::fire(NmdcHubListener::PrivateMessage(), this, ClientManager::getInstance()->getUser(from, this, false), Util::validateMessage(fromNmdc(param.substr(j + 1)), true));
+ }
+ }
+ }
+ } else if(cmd == "$GetPass") {
+ setRegistered(true);
+ Speaker<NmdcHubListener>::fire(NmdcHubListener::GetPassword(), this);
+ } else if(cmd == "$BadPass") {
+ Speaker<NmdcHubListener>::fire(NmdcHubListener::BadPassword(), this);
+ } else if(cmd == "$LogedIn") {
+ Speaker<NmdcHubListener>::fire(NmdcHubListener::LoggedIn(), this);
+ } else {
+ dcassert(cmd[0] == '$');
+ dcdebug("NmdcHub::onLine Unknown command %s\n", aLine.c_str());
+ }
+}
+
+string NmdcHub::checkNick(const string& aNick) {
+ string tmp = aNick;
+ string::size_type i = 0;
+ while( (i = tmp.find_first_of("|$ ", i)) != string::npos) {
+ tmp[i++]='_';
+ }
+ return tmp;
+}
+
+string NmdcHub::getHubURL() {
+ return getAddressPort();
+}
+
+void NmdcHub::myInfo(bool alwaysSend) {
+ checkstate();
+
+ dcdebug("MyInfo %s...\n", getNick().c_str());
+ lastCounts = counts;
+
+ string tmp1 = ";**\x1fU9";
+ string tmp2 = "+L9";
+ string tmp3 = "+G9";
+ string tmp4 = "+R9";
+ string tmp5 = "+N9";
+ string::size_type i;
+
+ for(i = 0; i < 6; i++) {
+ tmp1[i]++;
+ }
+ for(i = 0; i < 3; i++) {
+ tmp2[i]++; tmp3[i]++; tmp4[i]++; tmp5[i]++;
+ }
+ char modeChar = '?';
+ if(SETTING(CONNECTION_TYPE) == SettingsManager::CONNECTION_ACTIVE)
+ modeChar = 'A';
+ else if(SETTING(CONNECTION_TYPE) == SettingsManager::CONNECTION_PASSIVE)
+ modeChar = 'P';
+ else if(SETTING(CONNECTION_TYPE) == SettingsManager::CONNECTION_SOCKS5)
+ modeChar = '5';
+
+ string uMin = (SETTING(MIN_UPLOAD_SPEED) == 0) ? Util::emptyString : tmp5 + Util::toString(SETTING(MIN_UPLOAD_SPEED));
+ string myInfoA =
+ "$MyINFO $ALL " + toNmdc(checkNick(getNick())) + " " + toNmdc(Util::validateMessage(getDescription(), false)) +
+ tmp1 + VERSIONSTRING + tmp2 + modeChar + tmp3 + getCounts() + tmp4 + Util::toString(SETTING(SLOTS)) + uMin +
+ ">$ $" + SETTING(CONNECTION) + "\x01$" + toNmdc(Util::validateMessage(SETTING(EMAIL), false)) + '$';
+ string myInfoB = ShareManager::getInstance()->getShareSizeString() + "$|";
+
+ if(lastMyInfoA != myInfoA || alwaysSend || (lastMyInfoB != myInfoB && lastUpdate + 15*60*1000 < GET_TICK()) ){
+ send(myInfoA + myInfoB);
+ lastMyInfoA = myInfoA;
+ lastMyInfoB = myInfoB;
+ lastUpdate = GET_TICK();
+ }
+}
+
+void NmdcHub::disconnect() throw() {
+ state = STATE_CONNECT;
+ Client::disconnect();
+ {
+ Lock l(cs);
+ clearUsers();
+ }
+}
+
+void NmdcHub::search(int aSizeType, int64_t aSize, int aFileType, const string& aString, const string&) {
+ checkstate();
+ AutoArray<char> buf((char*)NULL);
+ char c1 = (aSizeType == SearchManager::SIZE_DONTCARE) ? 'F' : 'T';
+ char c2 = (aSizeType == SearchManager::SIZE_ATLEAST) ? 'F' : 'T';
+ string tmp = Util::validateMessage(toNmdc((aFileType == SearchManager::TYPE_TTH) ? "TTH:" + aString : aString), false);
+ string::size_type i;
+ while((i = tmp.find(' ')) != string::npos) {
+ tmp[i] = '$';
+ }
+ int chars = 0;
+ if(SETTING(CONNECTION_TYPE) == SettingsManager::CONNECTION_ACTIVE) {
+ string x = getLocalIp();
+ buf = new char[x.length() + aString.length() + 64];
+ chars = sprintf(buf, "$Search %s:%d %c?%c?%s?%d?%s|", x.c_str(), SETTING(UDP_PORT), c1, c2, Util::toString(aSize).c_str(), aFileType+1, tmp.c_str());
+ } else {
+ buf = new char[getNick().length() + aString.length() + 64];
+ chars = sprintf(buf, "$Search Hub:%s %c?%c?%s?%d?%s|", toNmdc(getNick()).c_str(), c1, c2, Util::toString(aSize).c_str(), aFileType+1, tmp.c_str());
+ }
+ send(buf, chars);
+}
+
+// TimerManagerListener
+void NmdcHub::on(TimerManagerListener::Second, u_int32_t aTick) throw() {
+ if(socket && (lastActivity + getReconnDelay() * 1000) < aTick) {
+ // Nothing's happened for ~120 seconds, check if we're connected, if not, try to connect...
+ lastActivity = aTick;
+ // Try to send something for the fun of it...
+ if(isConnected()) {
+ dcdebug("Testing writing...\n");
+ socket->write("|", 1);
+ } else {
+ // Try to reconnect...
+ if(reconnect && !getAddress().empty())
+ connect();
+ }
+ }
+ {
+ Lock l(cs);
+
+ while(!seekers.empty() && seekers.front().second + (5 * 1000) < aTick) {
+ seekers.pop_front();
+ }
+
+ while(!flooders.empty() && flooders.front().second + (120 * 1000) < aTick) {
+ flooders.pop_front();
+ }
+ }
+}
+
+// BufferedSocketListener
+void NmdcHub::on(BufferedSocketListener::Failed, const string& aLine) throw() {
+ {
+ Lock l(cs);
+ clearUsers();
+ }
+ if(state == STATE_CONNECTED)
+ state = STATE_CONNECT;
+ Speaker<NmdcHubListener>::fire(NmdcHubListener::Failed(), this, aLine);
+}
+
+/**
+ * @file
+ * $Id: NmdcHub.cpp,v 1.4 2005/09/01 18:09:27 olof Exp $
+ */
+
diff --git a/dcpp/NmdcHub.h b/dcpp/NmdcHub.h
new file mode 100644
index 0000000..2cfdfe1
--- /dev/null
+++ b/dcpp/NmdcHub.h
@@ -0,0 +1,269 @@
+/*
+ * Copyright (C) 2001-2005 Jacek Sieka, arnetheduck on gmail point 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 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.
+ */
+
+#if !defined(AFX_NmdcHub_H__089CBD05_4833_4E30_9A57_BB636231D78E__INCLUDED_)
+#define AFX_NmdcHub_H__089CBD05_4833_4E30_9A57_BB636231D78E__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+#include "TimerManager.h"
+#include "SettingsManager.h"
+
+#include "ClientManager.h"
+
+#include "BufferedSocket.h"
+#include "User.h"
+#include "CriticalSection.h"
+#include "Text.h"
+
+class NmdcHub;
+
+class NmdcHubListener
+{
+public:
+ template<int I> struct X { enum { TYPE = I }; };
+
+ typedef X<0> Connecting;
+ typedef X<1> Connected;
+ typedef X<2> BadPassword;
+ typedef X<3> MyInfo;
+ typedef X<4> NickList;
+ typedef X<5> OpList;
+ typedef X<6> Redirect;
+ typedef X<7> Failed;
+ typedef X<8> GetPassword;
+ typedef X<9> HubName;
+ typedef X<11> Message;
+ typedef X<12> PrivateMessage;
+ typedef X<13> UserCommand;
+ typedef X<14> HubFull;
+ typedef X<15> NickTaken;
+ typedef X<16> SearchFlood;
+ typedef X<17> ConnectToMe;
+ typedef X<18> Hello;
+ typedef X<19> Supports;
+ typedef X<20> CLock;
+ typedef X<21> LoggedIn;
+ typedef X<22> UserIp;
+ typedef X<23> RevConnectToMe;
+ typedef X<24> Search;
+ typedef X<25> Unknown;
+ typedef X<26> ValidateDenied;
+ typedef X<27> Quit;
+
+ virtual void on(Connecting, NmdcHub*) throw() { }
+ virtual void on(Connected, NmdcHub*) throw() { }
+ virtual void on(BadPassword, NmdcHub*) throw() { }
+ virtual void on(MyInfo, NmdcHub*, const User::Ptr&) throw() { }
+ virtual void on(NickList, NmdcHub*, const User::List&) throw() { }
+ virtual void on(OpList, NmdcHub*, const User::List&) throw() { }
+ virtual void on(Quit, NmdcHub*, const User::Ptr&) throw() { }
+ virtual void on(Redirect, NmdcHub*, const string&) throw() { }
+ virtual void on(Failed, NmdcHub*, const string&) throw() { }
+ virtual void on(GetPassword, NmdcHub*) throw() { }
+ virtual void on(HubName, NmdcHub*) throw() { }
+ virtual void on(Message, NmdcHub*, const string&) throw() { }
+ virtual void on(PrivateMessage, NmdcHub*, const User::Ptr&, const string&) throw() { }
+ virtual void on(UserCommand, NmdcHub*, int, int, const string&, const string&) throw() { }
+ virtual void on(HubFull, NmdcHub*) throw() { }
+ virtual void on(NickTaken, NmdcHub*) throw() { }
+ virtual void on(SearchFlood, NmdcHub*, const string&) throw() { }
+ virtual void on(ValidateDenied, NmdcHub*) throw() { }
+ virtual void on(Search, NmdcHub*, const string&, int, int64_t, int, const string&) throw() { }
+ virtual void on(ConnectToMe, NmdcHub*, const string&, short) throw() { }
+ virtual void on(RevConnectToMe, NmdcHub*, const User::Ptr&) throw() { }
+ virtual void on(Supports, NmdcHub*, const StringList&) throw() { }
+ virtual void on(CLock, NmdcHub*, const string&, const string&) throw() { }
+ virtual void on(UserIp, NmdcHub*, const User::List&) throw() { }
+ virtual void on(LoggedIn, NmdcHub*) throw() { }
+ virtual void on(Hello, NmdcHub*, const User::Ptr&) throw() { }
+};
+
+class NmdcHub : public Client, public Speaker<NmdcHubListener>, private TimerManagerListener, private Flags
+{
+ friend class ClientManager;
+public:
+ typedef NmdcHub* Ptr;
+ typedef list<Ptr> List;
+ typedef List::iterator Iter;
+
+ enum SupportFlags {
+ SUPPORTS_USERCOMMAND = 0x01,
+ SUPPORTS_NOGETINFO = 0x02,
+ SUPPORTS_USERIP2 = 0x04
+ };
+
+#define checkstate() if(state != STATE_CONNECTED) return
+
+ virtual void connect(const User* aUser);
+ virtual void hubMessage(const string& aMessage) { checkstate(); send(toNmdc( "<" + getNick() + "> " + Util::validateMessage(aMessage, false) + "|" ) ); }
+ virtual void privateMessage(const User* aUser, const string& aMessage) { privateMessage(aUser->getNick(), string("<") + getNick() + "> " + aMessage); }
+ virtual void send(const string& a) throw() {
+ lastActivity = GET_TICK();
+ //dcdebug("Sending %d to %s: %.40s\n", a.size(), getName().c_str(), a.c_str());
+ socket->write(a);
+ }
+ virtual void sendUserCmd(const string& aUserCmd) throw() {
+ send(toNmdc(aUserCmd));
+ }
+ virtual void search(int aSizeType, int64_t aSize, int aFileType, const string& aString, const string& aToken);
+ virtual void password(const string& aPass) { send("$MyPass " + toNmdc(aPass) + "|"); }
+ virtual void info(bool alwaysSend) { myInfo(alwaysSend); }
+
+ virtual size_t getUserCount() const { Lock l(cs); return users.size(); }
+ virtual int64_t getAvailable() const;
+ virtual const string& getName() const { return name; };
+ virtual bool getOp() const { return getMe() ? getMe()->isSet(User::OP) : false; };
+
+ virtual User::NickMap& lockUserList() { cs.enter(); return users; };
+ virtual void unlockUserList() { cs.leave(); };
+
+ virtual string escape(string const& str) const { return Util::validateMessage(str, false); };
+
+ void disconnect() throw();
+ void myInfo(bool alwaysSend);
+
+ void refreshUserList(bool unknownOnly = false);
+
+ void validateNick(const string& aNick) { send("$ValidateNick " + toNmdc(aNick) + "|"); }
+ void key(const string& aKey) { send("$Key " + aKey + "|"); };
+ void version() { send("$Version 1,0091|"); };
+ void getNickList() { checkstate(); send("$GetNickList|"); };
+ void getInfo(User::Ptr aUser) { checkstate(); send("$GetINFO " + toNmdc(aUser->getNick()) + " " + toNmdc(getNick()) + "|"); };
+ void getInfo(User* aUser) { checkstate(); send("$GetINFO " + toNmdc(aUser->getNick()) + " " + toNmdc(getNick()) + "|"); };
+
+ void connectToMe(const User::Ptr& aUser) {
+ checkstate();
+ dcdebug("NmdcHub::connectToMe %s\n", aUser->getNick().c_str());
+ send("$ConnectToMe " + toNmdc(aUser->getNick()) + " " + getLocalIp() + ":" + Util::toString(SETTING(IN_PORT)) + "|");
+ }
+
+ void privateMessage(const User::Ptr& aUser, const string& aMessage) {
+ privateMessage(aUser->getNick(), string("<") + getNick() + "> " + aMessage);
+ }
+ void privateMessage(const string& aNick, const string& aMessage) {
+ checkstate();
+ send("$To: " + toNmdc(aNick) + " From: " + toNmdc(getNick()) + " $" + toNmdc(Util::validateMessage(aMessage, false)) + "|");
+ }
+ void supports(const StringList& feat) {
+ string x;
+ for(StringList::const_iterator i = feat.begin(); i != feat.end(); ++i) {
+ x+= *i + ' ';
+ }
+ send("$Supports " + x + '|');
+ }
+ void revConnectToMe(const User::Ptr& aUser) {
+ checkstate();
+ dcdebug("NmdcHub::revConnectToMe %s\n", aUser->getNick().c_str());
+ send("$RevConnectToMe " + toNmdc(getNick()) + " " + toNmdc(aUser->getNick()) + "|");
+ }
+
+ void send(const char* aBuf, int aLen) throw() {
+ lastActivity = GET_TICK();
+ socket->write(aBuf, aLen);
+ }
+
+ GETSET(int, supportFlags, SupportFlags);
+private:
+
+ struct ClientAdapter : public NmdcHubListener {
+ ClientAdapter(NmdcHub* aClient) : c(aClient) { aClient->Speaker<NmdcHubListener>::addListener(this); }
+ Client* c;
+ virtual void on(Connecting, NmdcHub*) throw() { c->fire(ClientListener::Connecting(), c); }
+ virtual void on(Connected, NmdcHub*) throw() { c->fire(ClientListener::Connected(), c); }
+ virtual void on(BadPassword, NmdcHub*) throw() { c->fire(ClientListener::BadPassword(), c); }
+ virtual void on(MyInfo, NmdcHub*, const User::Ptr& u) throw() { c->fire(ClientListener::UserUpdated(), c, u); }
+ virtual void on(NickList, NmdcHub*, const User::List& l) throw() { c->fire(ClientListener::UsersUpdated(), c, l); }
+ virtual void on(OpList, NmdcHub*, const User::List& l) throw() { c->fire(ClientListener::UsersUpdated(), c, l); }
+ virtual void on(Quit, NmdcHub*, const User::Ptr& u) throw() { c->fire(ClientListener::UserRemoved(), c, u); }
+ virtual void on(Redirect, NmdcHub*, const string& aLine) throw() { c->fire(ClientListener::Redirect(), c, aLine); }
+ virtual void on(Failed, NmdcHub*, const string& aLine) throw() { c->fire(ClientListener::Failed(), c, aLine); }
+ virtual void on(GetPassword, NmdcHub*) throw() { c->fire(ClientListener::GetPassword(), c); }
+ virtual void on(HubName, NmdcHub*) throw() { c->fire(ClientListener::HubUpdated(), c); }
+ virtual void on(Message, NmdcHub*, const string& aLine) throw() { c->fire(ClientListener::Message(), c, aLine); }
+ virtual void on(PrivateMessage, NmdcHub*, const User::Ptr& u, const string& aLine) throw() { c->fire(ClientListener::PrivateMessage(), c, u, aLine); }
+ virtual void on(UserCommand, NmdcHub*, int a, int b, const string& l1, const string& l2) throw() { c->fire(ClientListener::UserCommand(), c, a, b, l1, l2); }
+ virtual void on(HubFull, NmdcHub*) throw() { c->fire(ClientListener::HubFull(), c); }
+ virtual void on(NickTaken, NmdcHub*) throw() { c->fire(ClientListener::NickTaken(), c); }
+ virtual void on(SearchFlood, NmdcHub*, const string& aLine) throw() { c->fire(ClientListener::SearchFlood(), c, aLine); }
+ virtual void on(ValidateDenied, NmdcHub*) throw() { c->fire(ClientListener::NickTaken(), c); }
+ virtual void on(Hello, NmdcHub*, const User::Ptr& u) throw() { c->fire(ClientListener::UserUpdated(), c, u); }
+ virtual void on(Search, NmdcHub*, const string& a, int b, int64_t d, int e, const string& f) throw() { c->fire(ClientListener::NmdcSearch(), c, a, b, d, e, f); }
+ } adapter;
+
+ enum States {
+ STATE_CONNECT,
+ STATE_LOCK,
+ STATE_HELLO,
+ STATE_CONNECTED
+ } state;
+
+ string name;
+
+ u_int32_t lastActivity;
+
+ mutable CriticalSection cs;
+
+ User::NickMap users;
+
+ bool reconnect;
+ u_int32_t lastUpdate;
+ string lastMyInfoA, lastMyInfoB;
+
+ typedef list<pair<string, u_int32_t> > FloodMap;
+ typedef FloodMap::iterator FloodIter;
+ FloodMap seekers;
+ FloodMap flooders;
+
+ NmdcHub(const string& aHubURL);
+ virtual ~NmdcHub() throw();
+
+ // Dummy
+ NmdcHub(const NmdcHub&);
+ NmdcHub& operator=(const NmdcHub&);
+
+ void connect();
+
+ void clearUsers();
+ void onLine(const string& aLine) throw();
+
+ string fromNmdc(const string& str) const { return Text::acpToUtf8(str); }
+ string toNmdc(const string& str) const { return Text::utf8ToAcp(str); }
+
+ virtual string checkNick(const string& aNick);
+ virtual string getHubURL();
+
+ // TimerManagerListener
+ virtual void on(TimerManagerListener::Second, u_int32_t aTick) throw();
+
+ virtual void on(Connecting) throw() { Speaker<NmdcHubListener>::fire(NmdcHubListener::Connecting(), this); }
+ virtual void on(Connected) throw() { lastActivity = GET_TICK(); Speaker<NmdcHubListener>::fire(NmdcHubListener::Connected(), this); }
+ virtual void on(Line, const string& l) throw() { onLine(l); }
+ virtual void on(Failed, const string&) throw();
+
+};
+
+#endif // !defined(AFX_NmdcHub_H__089CBD05_4833_4E30_9A57_BB636231D78E__INCLUDED_)
+
+/**
+ * @file
+ * $Id: NmdcHub.h,v 1.2 2005/08/21 14:03:43 olof Exp $
+ */
+
diff --git a/dcpp/Pointer.h b/dcpp/Pointer.h
new file mode 100644
index 0000000..219406c
--- /dev/null
+++ b/dcpp/Pointer.h
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2001-2005 Jacek Sieka, arnetheduck on gmail point 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 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.
+ */
+
+#if !defined(AFX_POINTER_H__FCC38D23_858F_43AC_BF23_73FD708FDC82__INCLUDED_)
+#define AFX_POINTER_H__FCC38D23_858F_43AC_BF23_73FD708FDC82__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+#include "Thread.h"
+
+class PointerBase
+{
+public:
+ void inc() throw() {
+ dcassert(ref>=0);
+ Thread::safeInc(ref);
+ }
+
+ void dec() throw() {
+ dcassert(ref>0);
+
+ if ( (Thread::safeDec(ref)) == 0 ) {
+ //dcdebug("Smart Object at 0x%08x deleted\n", this);
+ delete this;
+ }
+ }
+ bool unique() throw() {
+ return (ref == 1);
+ }
+
+protected:
+ PointerBase() throw() : ref(0) { };
+
+ virtual ~PointerBase() throw() {
+ dcassert(!ref);
+ }
+
+private:
+ volatile long ref;
+};
+
+/**
+ * Note; don't forget to make the destructor virtual if deriving from this class
+ */
+template <class T>
+class Pointer
+{
+public:
+ Pointer ( PointerBase *aBase = 0) throw() : base(aBase) {
+ if ( base ) {
+ base->inc();
+ }
+ }
+
+ Pointer( const Pointer &rhs ) throw() : base(rhs.base) {
+ if ( base ) {
+ base->inc();
+ }
+ }
+
+ Pointer &operator =( const Pointer &rhs ) throw() {
+ if ( rhs.base ) {
+ rhs.base->inc();
+ }
+
+ if ( base ) {
+ base->dec();
+ }
+
+ base = rhs.base;
+ return *this;
+ }
+
+ Pointer &operator =( T* rhs ) throw() {
+ if (rhs) {
+ rhs->inc();
+ if ( base ) {
+ base->dec();
+ }
+ base = rhs;
+ }
+
+
+ return *this;
+ }
+
+ ~Pointer() throw() {
+ if ( base ) {
+ base->dec();
+ }
+ }
+
+ T* operator->() { return asT(); }
+ T& operator* () { return *asT(); }
+ const T* operator->() const { return asT(); }
+ const T& operator* () const { return *asT(); }
+
+ operator bool() const { return base != NULL; }
+
+ bool operator==(T* rhs) const { return (T*)base == rhs; };
+ bool operator==(const Pointer& rhs) const { return base == rhs.base; };
+ bool operator!=(T* rhs) const { return (T*)base != rhs; };
+ bool operator!=(const Pointer& rhs) const { return base != rhs.base; };
+ bool operator<(T* rhs) const { return (T*)base < rhs; };
+ bool operator<(const Pointer& rhs) const { return base < rhs.base; };
+ bool operator>(T* rhs) const { return (T*)base > rhs; };
+ bool operator>(const Pointer& rhs) const { return base > rhs.base; };
+
+
+ static void swap ( Pointer &lhs, Pointer &rhs ) {
+ PointerBase *temp = lhs.base;
+ lhs.base = rhs.base;
+ rhs.base = temp;
+ }
+
+ void release() {
+ if ( base ) {
+ base->dec();
+ base = 0;
+ }
+ }
+private:
+ PointerBase* base;
+
+ T* asT () {
+ dcassert(base);
+ return (T*)base;
+ }
+
+ const T* asT() const {
+ dcassert(base);
+ return (T*)base;
+ }
+};
+
+template <class T>
+bool operator==(T* lhs, const Pointer<T>& rhs) { return rhs == lhs; }
+template <class T>
+bool operator<(T* lhs, const Pointer<T>& rhs) { return rhs > lhs; }
+template <class T>
+bool operator>(T* lhs, const Pointer<T>& rhs) { return rhs < lhs; }
+
+template <class T>
+struct DeleteFunction {
+ void operator()(const T& p) const { delete p; };
+};
+
+#endif // !defined(AFX_POINTER_H__FCC38D23_858F_43AC_BF23_73FD708FDC82__INCLUDED_)
+
+/**
+ * @file
+ * $Id: Pointer.h,v 1.2 2005/08/21 14:03:43 olof Exp $
+ */
+
diff --git a/dcpp/QueueItem.h b/dcpp/QueueItem.h
new file mode 100644
index 0000000..e58fb8c
--- /dev/null
+++ b/dcpp/QueueItem.h
@@ -0,0 +1,288 @@
+/*
+* Copyright (C) 2001-2005 Jacek Sieka, arnetheduck on gmail point 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 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.
+*/
+
+#if !defined(AFX_QUEUEITEM_H__07D44A33_1277_482D_AFB4_05E3473B4379__INCLUDED_)
+#define AFX_QUEUEITEM_H__07D44A33_1277_482D_AFB4_05E3473B4379__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+class QueueManager;
+class Download;
+
+#include "User.h"
+#include "FastAlloc.h"
+#include "MerkleTree.h"
+
+class QueueItem : public Flags, public FastAlloc<QueueItem> {
+public:
+ typedef QueueItem* Ptr;
+ // Strange, the vc7 optimizer won't take a deque here...
+ typedef vector<Ptr> List;
+ typedef List::iterator Iter;
+ typedef map<string*, Ptr, noCaseStringLess> StringMap;
+ // typedef HASH_MAP<string, Ptr, noCaseStringHash, noCaseStringEq> StringMap;
+ typedef StringMap::iterator StringIter;
+ typedef HASH_MAP_X(User::Ptr, Ptr, User::HashFunction, equal_to<User::Ptr>, less<User::Ptr>) UserMap;
+ typedef UserMap::iterator UserIter;
+ typedef HASH_MAP_X(User::Ptr, List, User::HashFunction, equal_to<User::Ptr>, less<User::Ptr>) UserListMap;
+ typedef UserListMap::iterator UserListIter;
+
+ enum Status {
+ /** The queue item is waiting to be downloaded and can be found in userQueue */
+ STATUS_WAITING,
+ /** This item is being downloaded and can be found in running */
+ STATUS_RUNNING
+ };
+
+ enum Priority {
+ DEFAULT = -1,
+ PAUSED = 0,
+ LOWEST,
+ LOW,
+ NORMAL,
+ HIGH,
+ HIGHEST,
+ LAST
+ };
+
+ enum FileFlags {
+ /** Normal download, no flags set */
+ FLAG_NORMAL = 0x00,
+ /** This download should be resumed if possible */
+ FLAG_RESUME = 0x01,
+ /** This is a user file listing download */
+ FLAG_USER_LIST = 0x02,
+ /** The file list is downloaded to use for directory download (used with USER_LIST) */
+ FLAG_DIRECTORY_DOWNLOAD = 0x04,
+ /** The file is downloaded to be viewed in the gui */
+ FLAG_CLIENT_VIEW = 0x08,
+ /** Flag to indicate that file should be viewed as a text file */
+ FLAG_TEXT = 0x20,
+ /** This file exists on the hard disk and should be prioritised */
+ FLAG_EXISTS = 0x40,
+ /** Match the queue against this list */
+ FLAG_MATCH_QUEUE = 0x80,
+ /** The source being added has its filename in utf-8 */
+ FLAG_SOURCE_UTF8 = 0x100,
+ /** The file list downloaded was actually an .xml.bz2 list */
+ FLAG_XML_BZLIST = 0x200
+ };
+
+ class Source : public Flags, public FastAlloc<Source> {
+ public:
+ typedef Source* Ptr;
+ typedef vector<Ptr> List;
+ typedef List::iterator Iter;
+ typedef List::const_iterator ConstIter;
+ enum {
+ FLAG_NONE = 0x00,
+ FLAG_FILE_NOT_AVAILABLE = 0x01,
+ FLAG_ROLLBACK_INCONSISTENCY = 0x02,
+ FLAG_PASSIVE = 0x04,
+ FLAG_REMOVED = 0x08,
+ FLAG_CRC_FAILED = 0x10,
+ FLAG_CRC_WARN = 0x20,
+ FLAG_UTF8 = 0x40,
+ FLAG_BAD_TREE = 0x80,
+ FLAG_NO_TREE = 0x100,
+ FLAG_MASK = FLAG_FILE_NOT_AVAILABLE | FLAG_ROLLBACK_INCONSISTENCY
+ | FLAG_PASSIVE | FLAG_REMOVED | FLAG_CRC_FAILED | FLAG_CRC_WARN | FLAG_UTF8
+ | FLAG_BAD_TREE | FLAG_NO_TREE
+ };
+
+ Source(const User::Ptr& aUser, const string& aPath) : path(aPath), user(aUser) { };
+ Source(const Source& aSource) : Flags(aSource), path(aSource.path), user(aSource.user) { }
+
+ User::Ptr& getUser() { return user; };
+ const User::Ptr& getUser() const { return user; };
+ void setUser(const User::Ptr& aUser) { user = aUser; };
+ string getFileName() { return Util::getFileName(path); };
+
+ GETSET(string, path, Path);
+ private:
+ User::Ptr user;
+ };
+
+ QueueItem(const string& aTarget, int64_t aSize,
+ Priority aPriority, int aFlag, int64_t aDownloadedBytes, u_int32_t aAdded, const TTHValue* tth) :
+ Flags(aFlag), target(aTarget),
+ size(aSize), downloadedBytes(aDownloadedBytes), status(STATUS_WAITING),
+ priority(aPriority), current(NULL), currentDownload(NULL), added(aAdded),
+ tthRoot(tth == NULL ? NULL : new TTHValue(*tth))
+ { };
+
+ QueueItem(const QueueItem& rhs) :
+ Flags(rhs), target(rhs.target), tempTarget(rhs.tempTarget),
+ size(rhs.size), downloadedBytes(rhs.downloadedBytes), status(rhs.status), priority(rhs.priority),
+ current(rhs.current), currentDownload(rhs.currentDownload), added(rhs.added), tthRoot(rhs.tthRoot == NULL ? NULL : new TTHValue(*rhs.tthRoot))
+ {
+ // Deep copy the source lists
+ Source::List::const_iterator i;
+ for(i = rhs.sources.begin(); i != rhs.sources.end(); ++i) {
+ sources.push_back(new Source(*(*i)));
+ }
+ for(i = rhs.badSources.begin(); i != rhs.badSources.end(); ++i) {
+ badSources.push_back(new Source(*(*i)));
+ }
+ }
+
+ virtual ~QueueItem() {
+ for_each(sources.begin(), sources.end(), DeleteFunction<Source*>());
+ for_each(badSources.begin(), badSources.end(), DeleteFunction<Source*>());
+ delete tthRoot;
+ };
+
+ int countOnlineUsers() const {
+ int n = 0;
+ Source::List::const_iterator i = sources.begin();
+ for(; i != sources.end(); ++i) {
+ if((*i)->getUser()->isOnline())
+ n++;
+ }
+ return n;
+ }
+ bool hasOnlineUsers() const { return countOnlineUsers() > 0; };
+
+ const string& getSourcePath(const User::Ptr& aUser) {
+ dcassert(isSource(aUser));
+ return (*getSource(aUser, sources))->getPath();
+ }
+
+ Source::List& getSources() { return sources; };
+ Source::List& getBadSources() { return badSources; };
+
+ void getOnlineUsers(User::List& l) const {
+ for(Source::List::const_iterator i = sources.begin(); i != sources.end(); ++i)
+ if((*i)->getUser()->isOnline())
+ l.push_back((*i)->getUser());
+ }
+
+ string getTargetFileName() const { return Util::getFileName(getTarget()); };
+
+ Source::Iter getSource(const User::Ptr& aUser) { return getSource(aUser, sources); };
+ Source::Iter getBadSource(const User::Ptr& aUser) { return getSource(aUser, badSources); };
+
+ bool isSource(const User::Ptr& aUser) { return (getSource(aUser, sources) != sources.end()); };
+ bool isBadSource(const User::Ptr& aUser) { return (getSource(aUser, badSources) != badSources.end()); };
+
+ bool isSource(const User::Ptr& aUser, const string& aFile) const { return isSource(aUser, aFile, sources); };
+ bool isBadSource(const User::Ptr& aUser, const string& aFile) const { return isSource(aUser, aFile, badSources); };
+ bool isBadSourceExcept(const User::Ptr& aUser, const string& aFile, Flags::MaskType exceptions) const {
+ Source::ConstIter i = getSource(aUser, aFile, badSources);
+ if(i != badSources.end())
+ return (*i)->isAnySet(exceptions^Source::FLAG_MASK);
+ return false;
+ };
+
+ void setCurrent(const User::Ptr& aUser) {
+ dcassert(isSource(aUser));
+ current = *getSource(aUser, sources);
+ }
+
+ string getListName() {
+ dcassert(isSet(QueueItem::FLAG_USER_LIST));
+ if(isSet(QueueItem::FLAG_XML_BZLIST)) {
+ return getTarget() + ".xml.bz2";
+ } else {
+ return getTarget() + ".DcLst";
+ }
+ }
+
+ string getSearchString() const;
+
+ const string& getTempTarget();
+ void setTempTarget(const string& aTempTarget) {
+ tempTarget = aTempTarget;
+ }
+ GETSET(string, target, Target);
+ string tempTarget;
+ GETSET(int64_t, size, Size);
+ GETSET(int64_t, downloadedBytes, DownloadedBytes);
+ GETSET(Status, status, Status);
+ GETSET(Priority, priority, Priority);
+ GETSET(Source*, current, Current);
+ GETSET(Download*, currentDownload, CurrentDownload);
+ GETSET(u_int32_t, added, Added);
+ GETSET(TTHValue*, tthRoot, TTH);
+private:
+ QueueItem& operator=(const QueueItem&);
+
+ friend class QueueManager;
+ Source::List sources;
+ Source::List badSources;
+
+ Source* addSource(const User::Ptr& aUser, const string& aPath) {
+ dcassert(!isSource(aUser));
+ Source* s = NULL;
+ Source::Iter i = getSource(aUser, badSources);
+ if(i != badSources.end()) {
+ s = *i;
+ badSources.erase(i);
+ s->setPath(aPath);
+ } else {
+ s = new Source(aUser, aPath);
+ }
+
+ sources.push_back(s);
+ return s;
+ }
+
+ void removeSource(const User::Ptr& aUser, int reason) {
+ Source::Iter i = getSource(aUser, sources);
+ dcassert(i != sources.end());
+ (*i)->setFlag(reason);
+ badSources.push_back(*i);
+ sources.erase(i);
+ }
+
+ static Source::Iter getSource(const User::Ptr& aUser, Source::List& lst) {
+ for(Source::Iter i = lst.begin(); i != lst.end(); ++i)
+ if((*i)->getUser() == aUser)
+ return i;
+ return lst.end();
+ }
+ static Source::ConstIter getSource(const User::Ptr& aUser, const string& aFile, const Source::List& lst) {
+ for(Source::ConstIter i = lst.begin(); i != lst.end(); ++i) {
+ const Source* s = *i;
+ if( (s->getUser() == aUser) ||
+ ((s->getUser()->getNick() == aUser->getNick()) && (s->getPath() == aFile)) )
+ return i;
+ }
+
+ return lst.end();
+ }
+ static bool isSource(const User::Ptr& aUser, const string& aFile, const Source::List& lst) {
+ for(Source::List::const_iterator i = lst.begin(); i != lst.end(); ++i) {
+ Source* s = *i;
+ if( (s->getUser() == aUser) ||
+ ((s->getUser()->getNick() == aUser->getNick()) && (s->getPath() == aFile)) )
+ return true;
+ }
+ return false;
+ }
+
+};
+
+#endif // !defined(AFX_QUEUEITEM_H__07D44A33_1277_482D_AFB4_05E3473B4379__INCLUDED_)
+
+/**
+* @file
+* $Id: QueueItem.h,v 1.2 2005/08/21 14:03:43 olof Exp $
+*/
diff --git a/dcpp/QueueManager.cpp b/dcpp/QueueManager.cpp
new file mode 100644
index 0000000..b500f71
--- /dev/null
+++ b/dcpp/QueueManager.cpp
@@ -0,0 +1,1388 @@
+/*
+ * Copyright (C) 2001-2005 Jacek Sieka, arnetheduck on gmail point 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 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.
+ */
+
+#include "stdinc.h"
+#include "DCPlusPlus.h"
+
+#include "QueueManager.h"
+
+#include "ConnectionManager.h"
+#include "SearchManager.h"
+#include "ClientManager.h"
+#include "DownloadManager.h"
+#include "ShareManager.h"
+#include "LogManager.h"
+#include "ResourceManager.h"
+#include "version.h"
+
+#include "UserConnection.h"
+#include "SimpleXML.h"
+#include "StringTokenizer.h"
+#include "DirectoryListing.h"
+
+#include <limits>
+
+#ifdef _WIN32
+#define FILELISTS_DIR "FileLists\\"
+#else
+#define FILELISTS_DIR "FileLists/"
+#endif
+
+#ifdef ff
+#undef ff
+#endif
+
+#ifndef _WIN32
+#include <sys/types.h>
+#include <dirent.h>
+#include <fnmatch.h>
+#endif
+
+/// TODO remove this / RIKARD
+#include <iostream>
+using namespace std;
+
+
+const string QueueManager::USER_LIST_NAME = "MyList.DcLst";
+
+namespace {
+ const char* badChars = "$|.[]()-_+";
+ const string TEMP_EXTENSION = ".dctmp";
+
+ string getTempName(const string& aFileName, const TTHValue* aRoot) {
+ string tmp(aFileName);
+ if(aRoot != NULL) {
+ TTHValue tmpRoot(*aRoot);
+ tmp += "." + tmpRoot.toBase32();
+ }
+ tmp += TEMP_EXTENSION;
+ return tmp;
+ }
+}
+string QueueItem::getSearchString() const {
+ return SearchManager::clean(getTargetFileName());
+}
+
+const string& QueueItem::getTempTarget() {
+ if(!isSet(QueueItem::FLAG_USER_LIST) && tempTarget.empty()) {
+ if(!SETTING(TEMP_DOWNLOAD_DIRECTORY).empty() && (File::getSize(getTarget()) == -1)) {
+#ifdef _WIN32
+ ::StringMap sm;
+ if(target.length() >= 3 && target[1] == ':' && target[2] == '\\')
+ sm["targetdrive"] = target.substr(0, 3);
+ else
+ sm["targetdrive"] = Util::getAppPath().substr(0, 3);
+ setTempTarget(Util::formatParams(SETTING(TEMP_DOWNLOAD_DIRECTORY), sm) + getTempName(getTargetFileName(), getTTH()));
+#else //_WIN32
+ setTempTarget(SETTING(TEMP_DOWNLOAD_DIRECTORY) + getTempName(getTargetFileName(), getTTH()));
+#endif //_WIN32
+ }
+ }
+ return tempTarget;
+}
+
+QueueItem* QueueManager::FileQueue::add(const string& aTarget, int64_t aSize,
+ int aFlags, QueueItem::Priority p, const string& aTempTarget,
+ int64_t aDownloadedBytes, u_int32_t aAdded, const TTHValue* root) throw(QueueException, FileException)
+{
+ if(p == QueueItem::DEFAULT) {
+ p = QueueItem::NORMAL;
+ if(aSize <= SETTING(PRIO_HIGHEST_SIZE)*1024) {
+ p = QueueItem::HIGHEST;
+ } else if(aSize <= SETTING(PRIO_HIGH_SIZE)*1024) {
+ p = QueueItem::HIGH;
+ } else if(aSize <= SETTING(PRIO_NORMAL_SIZE)*1024) {
+ p = QueueItem::NORMAL;
+ } else if(aSize <= SETTING(PRIO_LOW_SIZE)*1024) {
+ p = QueueItem::LOW;
+ } else if(SETTING(PRIO_LOWEST)) {
+ p = QueueItem::LOWEST;
+ }
+ }
+
+ QueueItem* qi = new QueueItem(aTarget, aSize, p, aFlags, aDownloadedBytes, aAdded, root);
+
+ if(!qi->isSet(QueueItem::FLAG_USER_LIST)) {
+ if(!aTempTarget.empty()) {
+ qi->setTempTarget(aTempTarget);
+ }
+ }
+
+ if((qi->getDownloadedBytes() > 0))
+ qi->setFlag(QueueItem::FLAG_EXISTS);
+
+ dcassert(find(aTarget) == NULL);
+ add(qi);
+ return qi;
+}
+
+void QueueManager::FileQueue::add(QueueItem* qi) {
+ if(lastInsert == queue.end())
+ lastInsert = queue.insert(make_pair(const_cast<string*>(&qi->getTarget()), qi)).first;
+ else
+ lastInsert = queue.insert(lastInsert, make_pair(const_cast<string*>(&qi->getTarget()), qi));
+}
+
+QueueItem* QueueManager::FileQueue::find(const string& target) {
+ QueueItem::StringIter i = queue.find(const_cast<string*>(&target));
+ return (i == queue.end()) ? NULL : i->second;
+}
+
+void QueueManager::FileQueue::find(QueueItem::List& sl, int64_t aSize, const string& suffix) {
+ for(QueueItem::StringIter i = queue.begin(); i != queue.end(); ++i) {
+ if(i->second->getSize() == aSize) {
+ const string& t = i->second->getTarget();
+ if(suffix.empty() || (suffix.length() < t.length() &&
+ Util::stricmp(suffix.c_str(), t.c_str() + (t.length() - suffix.length())) == 0) )
+ sl.push_back(i->second);
+ }
+ }
+}
+
+void QueueManager::FileQueue::find(QueueItem::List& ql, const TTHValue& tth) {
+ for(QueueItem::StringIter i = queue.begin(); i != queue.end(); ++i) {
+ QueueItem* qi = i->second;
+ if(qi->getTTH() != NULL && *qi->getTTH() == tth) {
+ ql.push_back(qi);
+ }
+ }
+}
+
+static QueueItem* findCandidate(QueueItem::StringIter start, QueueItem::StringIter end, StringList& recent) {
+ QueueItem* cand = NULL;
+ for(QueueItem::StringIter i = start; i != end; ++i) {
+ QueueItem* q = i->second;
+
+ // We prefer to search for things that are not running...
+ if((cand != NULL) && (q->getStatus() == QueueItem::STATUS_RUNNING))
+ continue;
+ // No user lists
+ if(q->isSet(QueueItem::FLAG_USER_LIST))
+ continue;
+ // No paused downloads
+ if(q->getPriority() == QueueItem::PAUSED)
+ continue;
+ // No files that already have more than 5 online sources
+ if(q->countOnlineUsers() >= 5)
+ continue;
+ // No files without TTH
+ if(q->getTTH() == NULL)
+ continue;
+ // Did we search for it recently?
+ if(find(recent.begin(), recent.end(), q->getTarget()) != recent.end())
+ continue;
+
+ cand = q;
+
+ if(cand->getStatus() != QueueItem::STATUS_RUNNING)
+ break;
+ }
+ return cand;
+}
+
+QueueItem* QueueManager::FileQueue::findAutoSearch(StringList& recent) {
+ // We pick a start position at random, hoping that we will find something to search for...
+ QueueItem::StringMap::size_type start = (QueueItem::StringMap::size_type)Util::rand((u_int32_t)queue.size());
+
+ QueueItem::StringIter i = queue.begin();
+ advance(i, start);
+
+ QueueItem* cand = findCandidate(i, queue.end(), recent);
+ if(cand == NULL) {
+ cand = findCandidate(queue.begin(), i, recent);
+ } else if(cand->getStatus() == QueueItem::STATUS_RUNNING) {
+ QueueItem* cand2 = findCandidate(queue.begin(), i, recent);
+ if(cand2 != NULL && (cand2->getStatus() != QueueItem::STATUS_RUNNING)) {
+ cand = cand2;
+ }
+ }
+ return cand;
+}
+
+void QueueManager::FileQueue::move(QueueItem* qi, const string& aTarget) {
+ if(lastInsert != queue.end() && Util::stricmp(*lastInsert->first, qi->getTarget()) == 0)
+ lastInsert = queue.end();
+ queue.erase(const_cast<string*>(&qi->getTarget()));
+ qi->setTarget(aTarget);
+ add(qi);
+}
+
+void QueueManager::UserQueue::add(QueueItem* qi) {
+ for(QueueItem::Source::Iter i = qi->getSources().begin(); i != qi->getSources().end(); ++i) {
+ add(qi, (*i)->getUser());
+ }
+}
+
+void QueueManager::UserQueue::add(QueueItem* qi, const User::Ptr& aUser) {
+ dcassert(qi->getStatus() == QueueItem::STATUS_WAITING);
+ dcassert(qi->isSource(aUser));
+ dcassert(qi->getCurrent() == NULL);
+
+ QueueItem::List& l = userQueue[qi->getPriority()][aUser];
+ if(qi->isSet(QueueItem::FLAG_EXISTS)) {
+ l.insert(l.begin(), qi);
+ } else {
+ l.push_back(qi);
+ }
+}
+
+QueueItem* QueueManager::UserQueue::getNext(const User::Ptr& aUser, QueueItem::Priority minPrio) {
+ int p = QueueItem::LAST - 1;
+
+ do {
+ QueueItem::UserListIter i = userQueue[p].find(aUser);
+ if(i != userQueue[p].end()) {
+ dcassert(!i->second.empty());
+ return i->second.front();
+ }
+ p--;
+ } while(p >= minPrio);
+
+ return NULL;
+}
+
+void QueueManager::UserQueue::setRunning(QueueItem* qi, const User::Ptr& aUser) {
+ dcassert(qi->getCurrent() == NULL);
+ dcassert(qi->getStatus() == QueueItem::STATUS_WAITING);
+
+ // Remove the download from the userQueue...
+ remove(qi);
+
+ // Set the flag to running...
+ qi->setStatus(QueueItem::STATUS_RUNNING);
+ qi->setCurrent(aUser);
+
+ // Move the download to the running list...
+ dcassert(running.find(aUser) == running.end());
+ running[aUser] = qi;
+
+}
+
+void QueueManager::UserQueue::setWaiting(QueueItem* qi) {
+ dcassert(qi->getCurrent() != NULL);
+ dcassert(qi->getStatus() == QueueItem::STATUS_RUNNING);
+
+ dcassert(running.find(qi->getCurrent()->getUser()) != running.end());
+ // Remove the download from running
+ running.erase(qi->getCurrent()->getUser());
+
+ // Set flag to waiting
+ qi->setCurrent(NULL);
+ qi->setStatus(QueueItem::STATUS_WAITING);
+
+ // Add to the userQueue
+ add(qi);
+}
+
+QueueItem* QueueManager::UserQueue::getRunning(const User::Ptr& aUser) {
+ QueueItem::UserIter i = running.find(aUser);
+ return (i == running.end()) ? NULL : i->second;
+}
+
+void QueueManager::UserQueue::remove(QueueItem* qi) {
+ if(qi->getStatus() == QueueItem::STATUS_RUNNING) {
+ dcassert(qi->getCurrent() != NULL);
+ remove(qi, qi->getCurrent()->getUser());
+ } else {
+ for(QueueItem::Source::Iter i = qi->getSources().begin(); i != qi->getSources().end(); ++i) {
+ remove(qi, (*i)->getUser());
+ }
+ }
+}
+
+void QueueManager::UserQueue::remove(QueueItem* qi, const User::Ptr& aUser) {
+ if(qi->getStatus() == QueueItem::STATUS_RUNNING) {
+ // Remove from running...
+ dcassert(qi->getCurrent() != NULL);
+ dcassert(running.find(aUser) != running.end());
+ running.erase(aUser);
+ } else {
+ dcassert(qi->isSource(aUser));
+ dcassert(qi->getCurrent() == NULL);
+ QueueItem::UserListMap& ulm = userQueue[qi->getPriority()];
+ QueueItem::UserListIter j = ulm.find(aUser);
+ dcassert(j != ulm.end());
+ QueueItem::List& l = j->second;
+ dcassert(find(l.begin(), l.end(), qi) != l.end());
+ l.erase(find(l.begin(), l.end(), qi));
+
+ if(l.empty()) {
+ ulm.erase(j);
+ }
+ }
+}
+
+QueueManager::QueueManager() : lastSave(0), queueFile(Util::getAppPath() + "Queue.xml"), dirty(true), nextSearch(0) {
+ TimerManager::getInstance()->addListener(this);
+ SearchManager::getInstance()->addListener(this);
+ ClientManager::getInstance()->addListener(this);
+
+ File::ensureDirectory(Util::getAppPath() + FILELISTS_DIR);
+}
+
+QueueManager::~QueueManager() throw() {
+ SearchManager::getInstance()->removeListener(this);
+ TimerManager::getInstance()->removeListener(this);
+ ClientManager::getInstance()->removeListener(this);
+
+ saveQueue();
+
+ if(!BOOLSETTING(KEEP_LISTS)) {
+ string path = Util::getAppPath() + FILELISTS_DIR;
+
+#ifdef _WIN32
+ WIN32_FIND_DATA data;
+ HANDLE hFind;
+
+ hFind = FindFirstFile(Text::toT(path + "\\*.xml.bz2").c_str(), &data);
+ if(hFind != INVALID_HANDLE_VALUE) {
+ do {
+ File::deleteFile(path + Text::fromT(data.cFileName));
+ } while(FindNextFile(hFind, &data));
+
+ FindClose(hFind);
+ }
+
+ hFind = FindFirstFile(Text::toT(path + "\\*.DcLst").c_str(), &data);
+ if(hFind != INVALID_HANDLE_VALUE) {
+ do {
+ File::deleteFile(path + Text::fromT(data.cFileName));
+ } while(FindNextFile(hFind, &data));
+
+ FindClose(hFind);
+ }
+
+#else
+ DIR* dir = opendir(path.c_str());
+ if (dir) {
+ while (struct dirent* ent = readdir(dir)) {
+ if (fnmatch("*.xml.bz2", ent->d_name, 0) == 0 ||
+ fnmatch("*.DcLst", ent->d_name, 0) == 0) {
+ File::deleteFile(path + ent->d_name);
+ }
+ }
+ closedir(dir);
+ }
+#endif
+ }
+}
+
+void QueueManager::on(TimerManagerListener::Minute, u_int32_t aTick) throw() {
+ string fn;
+ string searchString;
+ bool online = false;
+
+ {
+ Lock l(cs);
+ QueueItem::UserMap& um = userQueue.getRunning();
+
+ for(QueueItem::UserIter j = um.begin(); j != um.end(); ++j) {
+ QueueItem* q = j->second;
+ dcassert(q->getCurrentDownload() != NULL);
+ q->setDownloadedBytes(q->getCurrentDownload()->getPos());
+ }
+ if(!um.empty())
+ setDirty();
+
+ if(BOOLSETTING(AUTO_SEARCH) && (aTick >= nextSearch) && (fileQueue.getSize() > 0)) {
+ // We keep 30 recent searches to avoid duplicate searches
+ while((recent.size() > fileQueue.getSize()) || (recent.size() > 30)) {
+ recent.erase(recent.begin());
+ }
+
+ QueueItem* qi = fileQueue.findAutoSearch(recent);
+ if(qi != NULL) {
+ dcassert(qi->getTTH());
+ searchString = qi->getTTH()->toBase32();
+ online = qi->hasOnlineUsers();
+ recent.push_back(qi->getTarget());
+ nextSearch = aTick + (online ? 120000 : 300000);
+ }
+ }
+ }
+
+ if(!searchString.empty()) {
+ SearchManager::getInstance()->search(searchString, 0, SearchManager::TYPE_TTH, SearchManager::SIZE_DONTCARE, "auto");
+ }
+}
+
+void QueueManager::addPfs(const User::Ptr& aUser, const string& aDir) throw() {
+ if(!aUser->isOnline() || aUser->getCID().isZero())
+ return;
+
+ {
+ Lock l(cs);
+ pair<PfsIter, PfsIter> range = pfsQueue.equal_range(aUser->getCID());
+ if(find_if(range.first, range.second, CompareSecond<CID, string>(aDir)) == range.second) {
+
+ pfsQueue.insert(make_pair(aUser->getCID(), aDir));
+ }
+ }
+
+ ConnectionManager::getInstance()->getDownloadConnection(aUser);
+}
+
+void QueueManager::add(const string& aFile, int64_t aSize, User::Ptr aUser, const string& aTarget,
+ const TTHValue* root,
+ int aFlags /* = QueueItem::FLAG_RESUME */, QueueItem::Priority p /* = QueueItem::DEFAULT */,
+ bool addBad /* = true */) throw(QueueException, FileException)
+{
+
+cout << "****** QueueManager::add() **********" << endl;
+ cout << "aFile: " << aFile << endl;
+ cout << "aSize: " << aSize << endl;
+ cout << "aUser: " << aUser->getNick() << endl;
+ cout << "aTarget: " << aTarget << endl;
+ if(root) cout << "root: " << root->toBase32() << endl;
+ cout << "aFlags: " << aFlags << endl;
+ cout << "addBad: " << addBad << endl;
+
+ bool wantConnection = true;
+ dcassert((aFile != USER_LIST_NAME) || (aFlags &QueueItem::FLAG_USER_LIST));
+
+ // Check that we're not downloading from ourselves...
+ if(aUser->getClientNick() == aUser->getNick()) {
+ throw QueueException(STRING(NO_DOWNLOADS_FROM_SELF));
+ }
+
+ // Check if we're not downloading something already in our share
+ if (BOOLSETTING(DONT_DL_ALREADY_SHARED) && root != NULL){
+ if (ShareManager::getInstance()->isTTHShared(*root)){
+ throw QueueException(STRING(TTH_ALREADY_SHARED));
+ }
+ }
+
+ bool utf8 = (aFlags & QueueItem::FLAG_SOURCE_UTF8) > 0;
+ aFlags &= ~QueueItem::FLAG_SOURCE_UTF8;
+
+ string target = checkTarget(aTarget, aSize, aFlags);
+
+ // Check if it's a zero-byte file, if so, create and return...
+ if(aSize == 0) {
+ if(!BOOLSETTING(SKIP_ZERO_BYTE)) {
+ File f(target, File::WRITE, File::CREATE);
+ }
+ return;
+ }
+
+ {
+ Lock l(cs);
+
+ QueueItem* q = fileQueue.find(target);
+ if(q == NULL) {
+ q = fileQueue.add(target, aSize, aFlags, p, Util::emptyString, 0, GET_TIME(), root);
+ fire(QueueManagerListener::Added(), q);
+ } else {
+ if(q->getSize() != aSize) {
+ throw QueueException(STRING(FILE_WITH_DIFFERENT_SIZE));
+ }
+ if(root != NULL) {
+ if(q->getTTH() == NULL) {
+ q->setTTH(new TTHValue(*root));
+ } else if(!(*root == *q->getTTH())) {
+ throw QueueException(STRING(FILE_WITH_DIFFERENT_TTH));
+ }
+ }
+ q->setFlag(aFlags);
+
+ // We don't add any more sources to user list downloads...
+ if(q->isSet(QueueItem::FLAG_USER_LIST))
+ return;
+ }
+
+ wantConnection = addSource(q, aFile, aUser, addBad ? QueueItem::Source::FLAG_MASK : 0, utf8);
+ }
+
+ if(wantConnection && aUser->isOnline())
+ ConnectionManager::getInstance()->getDownloadConnection(aUser);
+}
+
+void QueueManager::readd(const string& target, User::Ptr& aUser) throw(QueueException) {
+ bool wantConnection = false;
+ {
+ Lock l(cs);
+ QueueItem* q = fileQueue.find(target);
+ if(q != NULL && q->isBadSource(aUser)) {
+ QueueItem::Source* s = *q->getBadSource(aUser);
+ wantConnection = addSource(q, s->getPath(), aUser, QueueItem::Source::FLAG_MASK, s->isSet(QueueItem::Source::FLAG_UTF8));
+ }
+ }
+ if(wantConnection && aUser->isOnline())
+ ConnectionManager::getInstance()->getDownloadConnection(aUser);
+}
+
+string QueueManager::checkTarget(const string& aTarget, int64_t aSize, int& flags) throw(QueueException, FileException) {
+#ifdef _WIN32
+ if(aTarget.length() > MAX_PATH) {
+ throw QueueException(STRING(TARGET_FILENAME_TOO_LONG));
+ }
+ // Check that target starts with a drive or is an UNC path
+ if( (aTarget[1] != ':' || aTarget[2] != '\\') &&
+ (aTarget[0] != '\\' && aTarget[1] != '\\') ) {
+ throw QueueException(STRING(INVALID_TARGET_FILE));
+ }
+#else
+ if(aTarget.length() > PATH_MAX) {
+ throw QueueException(STRING(TARGET_FILENAME_TOO_LONG));
+ }
+ // Check that target contains at least one directory...we don't want headless files...
+ if(aTarget[0] != '/') {
+ throw QueueException(STRING(INVALID_TARGET_FILE));
+ }
+#endif
+
+ string target = Util::validateFileName(aTarget);
+
+ // Check that the file doesn't already exist...
+ int64_t sz = File::getSize(target);
+ if( (aSize != -1) && (aSize <= sz) ) {
+ throw FileException(STRING(LARGER_TARGET_FILE_EXISTS));
+ }
+ if(sz > 0)
+ flags |= QueueItem::FLAG_EXISTS;
+
+ return target;
+}
+
+/** Add a source to an existing queue item */
+bool QueueManager::addSource(QueueItem* qi, const string& aFile, User::Ptr aUser, Flags::MaskType addBad, bool utf8) throw(QueueException, FileException) {
+ QueueItem::Source* s = NULL;
+ bool wantConnection = (qi->getPriority() != QueueItem::PAUSED);
+
+ if(qi->isSource(aUser, aFile)) {
+ throw QueueException(STRING(DUPLICATE_SOURCE));
+ }
+
+ if(qi->isBadSourceExcept(aUser, aFile, addBad)) {
+ throw QueueException(STRING(DUPLICATE_SOURCE));
+ }
+
+ s = qi->addSource(aUser, aFile);
+ if(utf8)
+ s->setFlag(QueueItem::Source::FLAG_UTF8);
+
+ if(aUser->isSet(User::PASSIVE) && (SETTING(CONNECTION_TYPE) != SettingsManager::CONNECTION_ACTIVE) ) {
+ qi->removeSource(aUser, QueueItem::Source::FLAG_PASSIVE);
+ wantConnection = false;
+ } else if(qi->getStatus() != QueueItem::STATUS_RUNNING) {
+ userQueue.add(qi, aUser);
+ } else {
+ wantConnection = false;
+ }
+
+ fire(QueueManagerListener::SourcesUpdated(), qi);
+ setDirty();
+
+ return wantConnection;
+}
+
+void QueueManager::addDirectory(const string& aDir, const User::Ptr& aUser, const string& aTarget, QueueItem::Priority p /* = QueueItem::DEFAULT */) throw() {
+ bool needList;
+ {
+ Lock l(cs);
+
+ DirectoryItem::DirectoryPair dp = directories.equal_range(aUser);
+
+ for(DirectoryItem::DirectoryIter i = dp.first; i != dp.second; ++i) {
+ if(Util::stricmp(aTarget.c_str(), i->second->getName().c_str()) == 0)
+ return;
+ }
+
+ // Unique directory, fine...
+ directories.insert(make_pair(aUser, new DirectoryItem(aUser, aDir, aTarget, p)));
+ needList = (dp.first == dp.second);
+ setDirty();
+ }
+
+ if(needList) {
+ try {
+ addList(aUser, QueueItem::FLAG_DIRECTORY_DOWNLOAD);
+ } catch(const Exception&) {
+ // Ignore, we don't really care...
+ }
+ }
+}
+
+#define isnum(c) (((c) >= '0') && ((c) <= '9'))
+
+static inline u_int32_t adjustSize(u_int32_t sz, const string& name) {
+ if(name.length() > 2) {
+ // filename.r32
+ u_int8_t c1 = (u_int8_t)name[name.length()-2];
+ u_int8_t c2 = (u_int8_t)name[name.length()-1];
+ if(isnum(c1) && isnum(c2)) {
+ return sz + (c1-'0')*10 + (c2-'0');
+ } else if(name.length() > 6) {
+ // filename.part32.rar
+ c1 = name[name.length() - 6];
+ c2 = name[name.length() - 5];
+ if(isnum(c1) && isnum(c2)) {
+ return sz + (c1-'0')*10 + (c2-'0');
+ }
+ }
+ }
+
+ return sz;
+}
+
+typedef HASH_MULTIMAP<u_int32_t, QueueItem*> SizeMap;
+typedef SizeMap::iterator SizeIter;
+typedef pair<SizeIter, SizeIter> SizePair;
+
+// *** WARNING ***
+// Lock(cs) makes sure that there's only one thread accessing these,
+// I put them here to avoid growing a huge stack...
+
+static DirectoryListing* curDl = NULL;
+static SizeMap sizeMap;
+
+int QueueManager::matchFiles(DirectoryListing::Directory* dir) throw() {
+ int matches = 0;
+ for(DirectoryListing::Directory::Iter j = dir->directories.begin(); j != dir->directories.end(); ++j) {
+ if(!(*j)->getAdls())
+ matches += matchFiles(*j);
+ }
+
+ for(DirectoryListing::File::Iter i = dir->files.begin(); i != dir->files.end(); ++i) {
+ DirectoryListing::File* df = *i;
+
+ SizePair files = sizeMap.equal_range(adjustSize((u_int32_t)df->getSize(), df->getName()));
+ for(SizeIter j = files.first; j != files.second; ++j) {
+ QueueItem* qi = j->second;
+ bool equal = false;
+ if(qi->getTTH() != NULL && df->getTTH() != NULL) {
+ equal = (*qi->getTTH() == *df->getTTH()) && (qi->getSize() == df->getSize());
+ }
+ if(equal) {
+ try {
+ addSource(qi, curDl->getPath(df) + df->getName(), curDl->getUser(), QueueItem::Source::FLAG_FILE_NOT_AVAILABLE, curDl->getUtf8());
+ matches++;
+ } catch(const Exception&) {
+ }
+ }
+ }
+ }
+ return matches;
+}
+
+int QueueManager::matchListing(DirectoryListing* dl) throw() {
+ int matches = 0;
+ {
+ Lock l(cs);
+ sizeMap.clear();
+ matches = 0;
+ curDl = dl;
+ for(QueueItem::StringIter i = fileQueue.getQueue().begin(); i != fileQueue.getQueue().end(); ++i) {
+ QueueItem* qi = i->second;
+ if(qi->getSize() != -1) {
+ sizeMap.insert(make_pair(adjustSize((u_int32_t)qi->getSize(), qi->getTarget()), qi));
+ }
+ }
+
+ matches = matchFiles(dl->getRoot());
+ }
+ if(matches > 0)
+ ConnectionManager::getInstance()->getDownloadConnection(dl->getUser());
+ return matches;
+}
+
+void QueueManager::move(const string& aSource, const string& aTarget) throw() {
+ string target = Util::validateFileName(aTarget);
+ if(Util::stricmp(aSource, target) == 0)
+ return;
+
+ bool delSource = false;
+
+ Lock l(cs);
+ QueueItem* qs = fileQueue.find(aSource);
+ if(qs != NULL) {
+ // Don't move running downloads
+ if(qs->getStatus() == QueueItem::STATUS_RUNNING) {
+ return;
+ }
+ // Don't move file lists
+ if(qs->isSet(QueueItem::FLAG_USER_LIST))
+ return;
+
+ // Let's see if the target exists...then things get complicated...
+ QueueItem* qt = fileQueue.find(target);
+ if(qt == NULL) {
+ // Good, update the target and move in the queue...
+ fileQueue.move(qs, target);
+ fire(QueueManagerListener::Moved(), qs);
+ setDirty();
+ } else {
+ // Don't move to target of different size
+ if(qs->getSize() != qt->getSize())
+ return;
+
+ try {
+ for(QueueItem::Source::Iter i = qs->getSources().begin(); i != qs->getSources().end(); ++i) {
+ QueueItem::Source* s = *i;
+ addSource(qt, s->getPath(), s->getUser(), QueueItem::Source::FLAG_MASK, s->isSet(QueueItem::Source::FLAG_UTF8));
+ }
+ } catch(const Exception&) {
+ }
+ delSource = true;
+ }
+ }
+
+ if(delSource) {
+ remove(aSource);
+ }
+}
+
+void QueueManager::getTargetsBySize(StringList& sl, int64_t aSize, const string& suffix) throw() {
+ Lock l(cs);
+ QueueItem::List ql;
+ fileQueue.find(ql, aSize, suffix);
+ for(QueueItem::Iter i = ql.begin(); i != ql.end(); ++i) {
+ sl.push_back((*i)->getTarget());
+ }
+}
+
+void QueueManager::getTargetsByRoot(StringList& sl, const TTHValue& tth) {
+ Lock l(cs);
+ QueueItem::List ql;
+ fileQueue.find(ql, tth);
+ for(QueueItem::Iter i = ql.begin(); i != ql.end(); ++i) {
+ sl.push_back((*i)->getTarget());
+ }
+}
+
+
+Download* QueueManager::getDownload(User::Ptr& aUser, bool supportsTrees) throw() {
+ Lock l(cs);
+
+ // First check PFS's...
+ PfsIter pi = pfsQueue.find(aUser->getCID());
+ if(pi != pfsQueue.end()) {
+ Download* d = new Download();
+ d->setFlag(Download::FLAG_PARTIAL_LIST);
+ d->setFlag(Download::FLAG_UTF8);
+ d->setSource(pi->second);
+ return d;
+ }
+
+ QueueItem* q = userQueue.getNext(aUser);
+
+ if(q == NULL)
+ return NULL;
+
+ userQueue.setRunning(q, aUser);
+
+ fire(QueueManagerListener::StatusUpdated(), q);
+
+ Download* d = new Download(q);
+
+ if(d->getSize() != -1 && d->getTTH()) {
+ if(HashManager::getInstance()->getTree(*d->getTTH(), d->getTigerTree())) {
+ d->setTreeValid(true);
+ } else if(supportsTrees && !q->getCurrent()->isSet(QueueItem::Source::FLAG_NO_TREE) && d->getSize() > HashManager::MIN_BLOCK_SIZE) {
+ // Get the tree unless the file is small (for small files, we'd probably only get the root anyway)
+ d->setFlag(Download::FLAG_TREE_DOWNLOAD);
+ d->getTigerTree().setFileSize(d->getSize());
+ d->setPos(0);
+ d->setSize(-1);
+ d->unsetFlag(Download::FLAG_RESUME);
+ } else {
+ // Use the root as tree to get some sort of validation at least...
+ d->getTigerTree() = TigerTree(d->getSize(), d->getSize(), *d->getTTH());
+ d->setTreeValid(true);
+ }
+ }
+
+ if(!d->isSet(Download::FLAG_TREE_DOWNLOAD) && BOOLSETTING(ANTI_FRAG) ) {
+ d->setStartPos(q->getDownloadedBytes());
+ }
+
+ q->setCurrentDownload(d);
+
+ return d;
+}
+
+
+void QueueManager::putDownload(Download* aDownload, bool finished) throw() {
+ User::List getConn;
+ string fname;
+ User::Ptr up;
+ int flag = 0;
+
+ {
+ Lock l(cs);
+
+ if(aDownload->isSet(Download::FLAG_PARTIAL_LIST)) {
+ pair<PfsIter, PfsIter> range = pfsQueue.equal_range(aDownload->getUserConnection()->getUser()->getCID());
+ PfsIter i = find_if(range.first, range.second, CompareSecond<CID, string>(aDownload->getSource()));
+ if(i != range.second) {
+ pfsQueue.erase(i);
+ fire(QueueManagerListener::PartialList(), aDownload->getUserConnection()->getUser(), aDownload->getPFS());
+ }
+ } else {
+
+ QueueItem* q = fileQueue.find(aDownload->getTarget());
+
+ if(q != NULL) {
+ if(aDownload->isSet(Download::FLAG_USER_LIST)) {
+ if(aDownload->getSource() == "files.xml.bz2") {
+ q->setFlag(QueueItem::FLAG_XML_BZLIST);
+ } else {
+ q->unsetFlag(QueueItem::FLAG_XML_BZLIST);
+ }
+ }
+
+
+ if(finished) {
+ dcassert(q->getStatus() == QueueItem::STATUS_RUNNING);
+ if(aDownload->isSet(Download::FLAG_TREE_DOWNLOAD)) {
+ // Got a full tree, now add it to the HashManager
+ dcassert(aDownload->getTreeValid());
+ HashManager::getInstance()->addTree(aDownload->getTigerTree());
+
+ q->setCurrentDownload(NULL);
+ userQueue.setWaiting(q);
+
+ fire(QueueManagerListener::StatusUpdated(), q);
+ } else {
+ userQueue.remove(q);
+ fire(QueueManagerListener::Finished(), q);
+ fire(QueueManagerListener::Removed(), q);
+ // Now, let's see if this was a directory download filelist...
+ if( (q->isSet(QueueItem::FLAG_DIRECTORY_DOWNLOAD) && directories.find(q->getCurrent()->getUser()) != directories.end()) ||
+ (q->isSet(QueueItem::FLAG_MATCH_QUEUE)) )
+ {
+ fname = q->getListName();
+ up = q->getCurrent()->getUser();
+ flag = (q->isSet(QueueItem::FLAG_DIRECTORY_DOWNLOAD) ? QueueItem::FLAG_DIRECTORY_DOWNLOAD : 0)
+ | (q->isSet(QueueItem::FLAG_MATCH_QUEUE) ? QueueItem::FLAG_MATCH_QUEUE : 0);
+ }
+ fileQueue.remove(q);
+ setDirty();
+ }
+ } else {
+ if(!aDownload->isSet(Download::FLAG_TREE_DOWNLOAD)) {
+ q->setDownloadedBytes(aDownload->getPos());
+
+ if(q->getDownloadedBytes() > 0) {
+ q->setFlag(QueueItem::FLAG_EXISTS);
+ } else {
+ q->setTempTarget(Util::emptyString);
+ }
+ if(q->isSet(QueueItem::FLAG_USER_LIST)) {
+ // Blah...no use keeping an unfinished file list...
+ File::deleteFile(q->getListName());
+ }
+ }
+
+ if(q->getPriority() != QueueItem::PAUSED) {
+ for(QueueItem::Source::Iter j = q->getSources().begin(); j != q->getSources().end(); ++j) {
+ if((*j)->getUser()->isOnline()) {
+ getConn.push_back((*j)->getUser());
+ }
+ }
+ }
+
+ q->setCurrentDownload(NULL);
+
+ // This might have been set to wait by removesource already...
+ if(q->getStatus() == QueueItem::STATUS_RUNNING) {
+ userQueue.setWaiting(q);
+ fire(QueueManagerListener::StatusUpdated(), q);
+ }
+ }
+ } else if(!aDownload->isSet(Download::FLAG_TREE_DOWNLOAD)) {
+ if(!aDownload->getTempTarget().empty() && aDownload->getTempTarget() != aDownload->getTarget()) {
+ File::deleteFile(aDownload->getTempTarget() + Download::ANTI_FRAG_EXT);
+ File::deleteFile(aDownload->getTempTarget());
+ }
+ }
+ }
+ aDownload->setUserConnection(NULL);
+ delete aDownload;
+ }
+
+ for(User::Iter i = getConn.begin(); i != getConn.end(); ++i) {
+ ConnectionManager::getInstance()->getDownloadConnection(*i);
+ }
+
+ if(!fname.empty()) {
+ processList(fname, up, flag);
+ }
+}
+
+void QueueManager::processList(const string& name, User::Ptr& user, int flags) {
+ DirectoryListing dirList(user);
+ try {
+ dirList.loadFile(name);
+ } catch(const Exception&) {
+ /** @todo Log this */
+ return;
+ }
+
+ if(flags & QueueItem::FLAG_DIRECTORY_DOWNLOAD) {
+ DirectoryItem::List dl;
+ {
+ Lock l(cs);
+ DirectoryItem::DirectoryPair dp = directories.equal_range(user);
+ for(DirectoryItem::DirectoryIter i = dp.first; i != dp.second; ++i) {
+ dl.push_back(i->second);
+ }
+ directories.erase(user);
+ }
+
+ for(DirectoryItem::Iter i = dl.begin(); i != dl.end(); ++i) {
+ DirectoryItem* di = *i;
+ dirList.download(di->getName(), di->getTarget(), false);
+ delete di;
+ }
+ }
+ if(flags & QueueItem::FLAG_MATCH_QUEUE) {
+ AutoArray<char> tmp(STRING(MATCHED_FILES).size() + 16);
+ sprintf(tmp, CSTRING(MATCHED_FILES), matchListing(&dirList));
+ LogManager::getInstance()->message(user->getNick() + ": " + string(tmp));
+ }
+}
+
+void QueueManager::remove(const string& aTarget) throw() {
+ string x;
+ {
+ Lock l(cs);
+
+ QueueItem* q = fileQueue.find(aTarget);
+ if(q != NULL) {
+ if(q->isSet(QueueItem::FLAG_DIRECTORY_DOWNLOAD)) {
+ dcassert(q->getSources().size() == 1);
+ DirectoryItem::DirectoryPair dp = directories.equal_range(q->getSources()[0]->getUser());
+ for(DirectoryItem::DirectoryIter i = dp.first; i != dp.second; ++i) {
+ delete i->second;
+ }
+ directories.erase(q->getSources()[0]->getUser());
+ }
+
+ if(q->getStatus() == QueueItem::STATUS_RUNNING) {
+ x = q->getTarget();
+ } else if(!q->getTempTarget().empty() && q->getTempTarget() != q->getTarget()) {
+ File::deleteFile(q->getTempTarget() + Download::ANTI_FRAG_EXT);
+ File::deleteFile(q->getTempTarget());
+ }
+
+ userQueue.remove(q);
+
+ fire(QueueManagerListener::Removed(), q);
+ fileQueue.remove(q);
+
+ setDirty();
+ }
+ }
+ if(!x.empty()) {
+ DownloadManager::getInstance()->abortDownload(x);
+ }
+}
+
+void QueueManager::removeSource(const string& aTarget, User::Ptr& aUser, int reason, bool removeConn /* = true */) throw() {
+ Lock l(cs);
+ QueueItem* q = fileQueue.find(aTarget);
+ string x;
+ if(q != NULL) {
+ dcassert(q->isSource(aUser));
+ if(q->isSet(QueueItem::FLAG_USER_LIST)) {
+ remove(q->getTarget());
+ return;
+ }
+
+ if(reason == QueueItem::Source::FLAG_NO_TREE) {
+ QueueItem::Source* s = *q->getSource(aUser);
+ s->setFlag(reason);
+ return;
+ }
+
+ if(reason == QueueItem::Source::FLAG_CRC_WARN) {
+ // Already flagged?
+ QueueItem::Source* s = *q->getSource(aUser);
+ if(s->isSet(QueueItem::Source::FLAG_CRC_WARN)) {
+ reason = QueueItem::Source::FLAG_CRC_FAILED;
+ } else {
+ s->setFlag(reason);
+ return;
+ }
+ }
+ if((q->getStatus() == QueueItem::STATUS_RUNNING) && q->getCurrent()->getUser() == aUser) {
+ if(removeConn)
+ x = q->getTarget();
+ userQueue.setWaiting(q);
+ userQueue.remove(q, aUser);
+ } else if(q->getStatus() == QueueItem::STATUS_WAITING) {
+ userQueue.remove(q, aUser);
+ }
+
+ q->removeSource(aUser, reason);
+
+ fire(QueueManagerListener::SourcesUpdated(), q);
+ setDirty();
+ }
+ if(!x.empty()) {
+ DownloadManager::getInstance()->abortDownload(x);
+ }
+}
+
+void QueueManager::removeSources(User::Ptr& aUser, int reason) throw() {
+ string x;
+ {
+ Lock l(cs);
+ QueueItem* qi = NULL;
+ while( (qi = userQueue.getNext(aUser, QueueItem::PAUSED)) != NULL) {
+ if(qi->isSet(QueueItem::FLAG_USER_LIST)) {
+ remove(qi->getTarget());
+ } else {
+ userQueue.remove(qi, aUser);
+ qi->removeSource(aUser, reason);
+ fire(QueueManagerListener::SourcesUpdated(), qi);
+ setDirty();
+ }
+ }
+
+ qi = userQueue.getRunning(aUser);
+ if(qi != NULL) {
+ if(qi->isSet(QueueItem::FLAG_USER_LIST)) {
+ remove(qi->getTarget());
+ } else {
+ userQueue.setWaiting(qi);
+ userQueue.remove(qi, aUser);
+ x = qi->getTarget();
+ qi->removeSource(aUser, reason);
+ fire(QueueManagerListener::SourcesUpdated(), qi);
+ setDirty();
+ }
+ }
+ }
+ if(!x.empty()) {
+ DownloadManager::getInstance()->abortDownload(x);
+ }
+}
+
+void QueueManager::setPriority(const string& aTarget, QueueItem::Priority p) throw() {
+ User::List ul;
+
+ {
+ Lock l(cs);
+
+ QueueItem* q = fileQueue.find(aTarget);
+ if( (q != NULL) && (q->getPriority() != p) ) {
+ if( q->getStatus() != QueueItem::STATUS_RUNNING ) {
+ if(q->getPriority() == QueueItem::PAUSED || p == QueueItem::HIGHEST) {
+ // Problem, we have to request connections to all these users...
+ q->getOnlineUsers(ul);
+ }
+
+ userQueue.remove(q);
+ q->setPriority(p);
+ userQueue.add(q);
+ } else {
+ q->setPriority(p);
+ }
+ setDirty();
+ fire(QueueManagerListener::StatusUpdated(), q);
+ }
+ }
+
+ for(User::Iter i = ul.begin(); i != ul.end(); ++i) {
+ ConnectionManager::getInstance()->getDownloadConnection(*i);
+ }
+}
+
+void QueueManager::saveQueue() throw() {
+ if(!dirty)
+ return;
+
+ Lock l(cs);
+
+ try {
+
+#define STRINGLEN(n) n, sizeof(n)-1
+#define CHECKESCAPE(n) SimpleXML::escape(n, tmp, true)
+
+ File ff(getQueueFile() + ".tmp", File::WRITE, File::CREATE | File::TRUNCATE);
+ BufferedOutputStream<false> f(&ff);
+
+ f.write(SimpleXML::utf8Header);
+ f.write(STRINGLEN("<Downloads Version=\"" VERSIONSTRING "\">\r\n"));
+ string tmp;
+ string b32tmp;
+ for(QueueItem::StringIter i = fileQueue.getQueue().begin(); i != fileQueue.getQueue().end(); ++i) {
+ QueueItem* qi = i->second;
+ if(!qi->isSet(QueueItem::FLAG_USER_LIST)) {
+ f.write(STRINGLEN("\t<Download Target=\""));
+ f.write(CHECKESCAPE(qi->getTarget()));
+ f.write(STRINGLEN("\" Size=\""));
+ f.write(Util::toString(qi->getSize()));
+ f.write(STRINGLEN("\" Priority=\""));
+ f.write(Util::toString((int)qi->getPriority()));
+ f.write(STRINGLEN("\" Added=\""));
+ f.write(Util::toString(qi->getAdded()));
+ if(qi->getTTH() != NULL) {
+ b32tmp.clear();
+ f.write(STRINGLEN("\" TTH=\""));
+ f.write(qi->getTTH()->toBase32(b32tmp));
+ }
+ if(qi->getDownloadedBytes() > 0) {
+ f.write(STRINGLEN("\" TempTarget=\""));
+ f.write(CHECKESCAPE(qi->getTempTarget()));
+ f.write(STRINGLEN("\" Downloaded=\""));
+ f.write(Util::toString(qi->getDownloadedBytes()));
+ }
+ f.write(STRINGLEN("\">\r\n"));
+
+ for(QueueItem::Source::List::const_iterator j = qi->sources.begin(); j != qi->sources.end(); ++j) {
+ QueueItem::Source* s = *j;
+ f.write(STRINGLEN("\t\t<Source Nick=\""));
+ f.write(CHECKESCAPE(s->getUser()->getNick()));
+ f.write(STRINGLEN("\" Path=\""));
+ f.write(CHECKESCAPE(s->getPath()));
+ f.write(STRINGLEN("\" Utf8=\""));
+ f.write(s->isSet(QueueItem::Source::FLAG_UTF8) ? "1" : "0", 1);
+ f.write(STRINGLEN("\"/>\r\n"));
+ }
+
+ f.write(STRINGLEN("\t</Download>\r\n"));
+ }
+ }
+
+ for(DirectoryItem::DirectoryIter j = directories.begin(); j != directories.end(); ++j) {
+ DirectoryItem::Ptr d = j->second;
+ f.write(STRINGLEN("\t<Directory Target=\""));
+ f.write(CHECKESCAPE(d->getTarget()));
+ f.write(STRINGLEN("\" Nick=\""));
+ f.write(CHECKESCAPE(d->getUser()->getNick()));
+ f.write(STRINGLEN("\" Priority=\""));
+ f.write(Util::toString((int)d->getPriority()));
+ f.write(STRINGLEN("\" Source=\""));
+ f.write(CHECKESCAPE(d->getName()));
+ f.write(STRINGLEN("\"/>\r\n"));
+ }
+
+ f.write("</Downloads>\r\n");
+ f.flush();
+ ff.close();
+ File::deleteFile(getQueueFile());
+ File::renameFile(getQueueFile() + ".tmp", getQueueFile());
+
+ dirty = false;
+ } catch(const FileException&) {
+ // ...
+ }
+ // Put this here to avoid very many saves tries when disk is full...
+ lastSave = GET_TICK();
+}
+
+class QueueLoader : public SimpleXMLReader::CallBack {
+public:
+ QueueLoader() : cur(NULL), inDownloads(false) { };
+ virtual ~QueueLoader() { }
+ virtual void startTag(const string& name, StringPairList& attribs, bool simple);
+ virtual void endTag(const string& name, const string& data);
+private:
+ string target;
+
+ QueueItem* cur;
+ bool inDownloads;
+};
+
+void QueueManager::loadQueue() throw() {
+ try {
+ QueueLoader l;
+ SimpleXMLReader(&l).fromXML(File(getQueueFile(), File::READ, File::OPEN).read());
+ dirty = false;
+ } catch(const Exception&) {
+ // ...
+ }
+}
+
+static const string sDownload = "Download";
+static const string sTempTarget = "TempTarget";
+static const string sTarget = "Target";
+static const string sSize = "Size";
+static const string sDownloaded = "Downloaded";
+static const string sPriority = "Priority";
+static const string sSource = "Source";
+static const string sNick = "Nick";
+static const string sPath = "Path";
+static const string sDirectory = "Directory";
+static const string sAdded = "Added";
+static const string sUtf8 = "Utf8";
+static const string sTTH = "TTH";
+
+void QueueLoader::startTag(const string& name, StringPairList& attribs, bool simple) {
+ QueueManager* qm = QueueManager::getInstance();
+ if(!inDownloads && name == "Downloads") {
+ inDownloads = true;
+ } else if(inDownloads) {
+ if(cur == NULL && name == sDownload) {
+ int flags = QueueItem::FLAG_RESUME;
+ int64_t size = Util::toInt64(getAttrib(attribs, sSize, 1));
+ if(size == 0)
+ return;
+ try {
+ const string& tgt = getAttrib(attribs, sTarget, 0);
+ target = QueueManager::checkTarget(tgt, size, flags);
+ if(target.empty())
+ return;
+ } catch(const Exception&) {
+ return;
+ }
+ QueueItem::Priority p = (QueueItem::Priority)Util::toInt(getAttrib(attribs, sPriority, 3));
+ u_int32_t added = (u_int32_t)Util::toInt(getAttrib(attribs, sAdded, 4));
+ const string& tthRoot = getAttrib(attribs, sTTH, 5);
+ string tempTarget = getAttrib(attribs, sTempTarget, 5);
+ int64_t downloaded = Util::toInt64(getAttrib(attribs, sDownloaded, 5));
+ if (downloaded > size || downloaded < 0)
+ downloaded = 0;
+
+ if(added == 0)
+ added = GET_TIME();
+
+ QueueItem* qi = qm->fileQueue.find(target);
+
+ if(qi == NULL) {
+ if(tthRoot.empty()) {
+ qi = qm->fileQueue.add(target, size, flags, p, tempTarget, downloaded, added, NULL);
+ } else {
+ TTHValue root(tthRoot);
+ qi = qm->fileQueue.add(target, size, flags, p, tempTarget, downloaded, added, &root);
+ }
+ qm->fire(QueueManagerListener::Added(), qi);
+ }
+ if(!simple)
+ cur = qi;
+ } else if(cur != NULL && name == sSource) {
+ const string& nick = getAttrib(attribs, sNick, 0);
+ if(nick.empty())
+ return;
+ const string& path = getAttrib(attribs, sPath, 1);
+ if(path.empty())
+ return;
+ const string& utf8 = getAttrib(attribs, sUtf8, 2);
+ bool isUtf8 = (utf8 == "1");
+ User::Ptr user = ClientManager::getInstance()->getUser(nick);
+ try {
+ if(qm->addSource(cur, path, user, 0, isUtf8) && user->isOnline())
+ ConnectionManager::getInstance()->getDownloadConnection(user);
+ } catch(const Exception&) {
+ return;
+ }
+ } else if(cur == NULL && name == sDirectory) {
+ const string& targetd = getAttrib(attribs, sTarget, 0);
+ if(targetd.empty())
+ return;
+ const string& nick = getAttrib(attribs, sNick, 1);
+ if(nick.empty())
+ return;
+ QueueItem::Priority p = (QueueItem::Priority)Util::toInt(getAttrib(attribs, sPriority, 2));
+ const string& source = getAttrib(attribs, sSource, 3);
+ if(source.empty())
+ return;
+
+ qm->addDirectory(source, ClientManager::getInstance()->getUser(nick), targetd, p);
+ }
+ }
+}
+
+void QueueLoader::endTag(const string& name, const string&) {
+ if(inDownloads) {
+ if(name == sDownload)
+ cur = NULL;
+ else if(name == "Downloads")
+ inDownloads = false;
+ }
+}
+
+// SearchManagerListener
+void QueueManager::on(SearchManagerListener::SR, SearchResult* sr) throw() {
+ bool added = false;
+ bool wantConnection = false;
+
+ if(BOOLSETTING(AUTO_SEARCH) && sr->getTTH() != NULL) {
+ Lock l(cs);
+ QueueItem::List matches;
+
+ fileQueue.find(matches, *sr->getTTH());
+
+ for(QueueItem::Iter i = matches.begin(); i != matches.end(); ++i) {
+ QueueItem* qi = *i;
+ dcassert(qi->getTTH());
+
+ // Size compare to avoid popular spoof
+ bool found = (*qi->getTTH() == *sr->getTTH()) && (qi->getSize() == sr->getSize());
+
+ if(found) {
+ try {
+ wantConnection = addSource(qi, sr->getFile(), sr->getUser(), 0, false);
+ added = true;
+ } catch(const Exception&) {
+ // ...
+ }
+ break;
+ }
+ }
+ }
+
+ if(added && BOOLSETTING(AUTO_SEARCH_AUTO_MATCH)) {
+ try {
+ addList(sr->getUser(), QueueItem::FLAG_MATCH_QUEUE);
+ } catch(const Exception&) {
+ // ...
+ }
+ }
+ if(added && sr->getUser()->isOnline() && wantConnection)
+ ConnectionManager::getInstance()->getDownloadConnection(sr->getUser());
+
+}
+
+// ClientManagerListener
+void QueueManager::on(ClientManagerListener::UserUpdated, const User::Ptr& aUser) throw() {
+ bool hasDown = false;
+ {
+ Lock l(cs);
+ for(int i = 0; i < QueueItem::LAST; ++i) {
+ QueueItem::UserListIter j = userQueue.getList(i).find(aUser);
+ if(j != userQueue.getList(i).end()) {
+ for(QueueItem::Iter m = j->second.begin(); m != j->second.end(); ++m)
+ fire(QueueManagerListener::StatusUpdated(), *m);
+ if(i != QueueItem::PAUSED)
+ hasDown = true;
+ }
+ }
+
+ if(pfsQueue.find(aUser->getCID()) != pfsQueue.end()) {
+ hasDown = true;
+ }
+ }
+
+ if(aUser->isOnline() && hasDown)
+ ConnectionManager::getInstance()->getDownloadConnection(aUser);
+}
+
+void QueueManager::on(TimerManagerListener::Second, u_int32_t aTick) throw() {
+ if(dirty && ((lastSave + 10000) < aTick)) {
+ saveQueue();
+ }
+}
+
+/**
+ * @file
+ * $Id: QueueManager.cpp,v 1.3 2006/01/06 01:09:01 olof Exp $
+ */
diff --git a/dcpp/QueueManager.h b/dcpp/QueueManager.h
new file mode 100644
index 0000000..50d86fd
--- /dev/null
+++ b/dcpp/QueueManager.h
@@ -0,0 +1,256 @@
+/*
+ * Copyright (C) 2001-2005 Jacek Sieka, arnetheduck on gmail point 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 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.
+ */
+
+#if !defined(AFX_QUEUEMANAGER_H__07D44A33_1277_482D_AFB4_05E3473B4379__INCLUDED_)
+#define AFX_QUEUEMANAGER_H__07D44A33_1277_482D_AFB4_05E3473B4379__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+#include "TimerManager.h"
+
+#include "CriticalSection.h"
+#include "Exception.h"
+#include "User.h"
+#include "File.h"
+#include "QueueItem.h"
+#include "Singleton.h"
+#include "DirectoryListing.h"
+#include "MerkleTree.h"
+
+#include "QueueManagerListener.h"
+#include "SearchManagerListener.h"
+#include "ClientManagerListener.h"
+
+STANDARD_EXCEPTION(QueueException);
+
+class UserConnection;
+
+class DirectoryItem {
+public:
+ typedef DirectoryItem* Ptr;
+ typedef HASH_MULTIMAP<User::Ptr, Ptr, User::HashFunction> DirectoryMap;
+ typedef DirectoryMap::iterator DirectoryIter;
+ typedef pair<DirectoryIter, DirectoryIter> DirectoryPair;
+
+ typedef vector<Ptr> List;
+ typedef List::iterator Iter;
+
+ DirectoryItem() : priority(QueueItem::DEFAULT) { };
+ DirectoryItem(const User::Ptr& aUser, const string& aName, const string& aTarget,
+ QueueItem::Priority p) : name(aName), target(aTarget), priority(p), user(aUser) { };
+ ~DirectoryItem() { };
+
+ User::Ptr& getUser() { return user; };
+ void setUser(const User::Ptr& aUser) { user = aUser; };
+
+ GETSET(string, name, Name);
+ GETSET(string, target, Target);
+ GETSET(QueueItem::Priority, priority, Priority);
+private:
+ User::Ptr user;
+};
+
+class ConnectionQueueItem;
+class QueueLoader;
+
+class QueueManager : public Singleton<QueueManager>, public Speaker<QueueManagerListener>, private TimerManagerListener,
+ private SearchManagerListener, private ClientManagerListener
+{
+public:
+
+ /** Add a file to the queue. */
+ void add(const string& aFile, int64_t aSize, User::Ptr aUser,
+ const string& aTarget, const TTHValue* root,
+ int aFlags = QueueItem::FLAG_RESUME, QueueItem::Priority p = QueueItem::DEFAULT,
+ bool addBad = true) throw(QueueException, FileException);
+
+ /** Add a user's filelist to the queue. */
+ void addList(const User::Ptr& aUser, int aFlags) throw(QueueException, FileException) {
+ string x = aUser->getNick();
+ string::size_type i = 0;
+ while((i = x.find(PATH_SEPARATOR), i) != string::npos)
+ x[i] = '_';
+ string file = Util::getAppPath() + "FileLists" + PATH_SEPARATOR_STR + x;
+ // We use the searchString to store the start viewing directory for file lists
+ add(USER_LIST_NAME, -1, aUser, file, NULL,
+ QueueItem::FLAG_USER_LIST | aFlags, QueueItem::DEFAULT,
+ true);
+ }
+
+ void addPfs(const User::Ptr& aUser, const string& aDir) throw();
+
+ /** Readd a source that was removed */
+ void readd(const string& target, User::Ptr& aUser) throw(QueueException);
+
+ /** Add a directory to the queue (downloads filelist and matches the directory). */
+ void addDirectory(const string& aDir, const User::Ptr& aUser, const string& aTarget, QueueItem::Priority p = QueueItem::DEFAULT) throw();
+
+ int matchListing(DirectoryListing* dl) throw();
+
+ /** Move the target location of a queued item. Running items are silently ignored */
+ void move(const string& aSource, const string& aTarget) throw();
+
+ void remove(const string& aTarget) throw();
+ void removeSource(const string& aTarget, User::Ptr& aUser, int reason, bool removeConn = true) throw();
+ void removeSources(User::Ptr& aUser, int reason) throw();
+
+ void setPriority(const string& aTarget, QueueItem::Priority p) throw();
+
+ void getTargetsBySize(StringList& sl, int64_t aSize, const string& suffix) throw();
+ void getTargetsByRoot(StringList& sl, const TTHValue& tth);
+ QueueItem::StringMap& lockQueue() throw() { cs.enter(); return fileQueue.getQueue(); } ;
+ void unlockQueue() throw() { cs.leave(); };
+
+ Download* getDownload(User::Ptr& aUser, bool supportsTrees) throw();
+ void putDownload(Download* aDownload, bool finished) throw();
+
+ bool hasDownload(const User::Ptr& aUser, QueueItem::Priority minPrio = QueueItem::LOWEST) throw() {
+ Lock l(cs);
+ return (pfsQueue.find(aUser->getCID()) != pfsQueue.end()) || (userQueue.getNext(aUser, minPrio) != NULL);
+ }
+
+ void loadQueue() throw();
+ void saveQueue() throw();
+
+ GETSET(u_int32_t, lastSave, LastSave);
+ GETSET(string, queueFile, QueueFile);
+private:
+
+ typedef HASH_MAP_X(CID, string, CID::Hash, equal_to<CID>, less<CID>) PfsQueue;
+ typedef PfsQueue::iterator PfsIter;
+
+ /** All queue items by target */
+ class FileQueue {
+ public:
+ FileQueue() : lastInsert(queue.end()) { };
+ ~FileQueue() {
+ for(QueueItem::StringIter i = queue.begin(); i != queue.end(); ++i)
+ delete i->second;
+ }
+ void add(QueueItem* qi);
+ QueueItem* add(const string& aTarget, int64_t aSize,
+ int aFlags, QueueItem::Priority p, const string& aTempTarget, int64_t aDownloaded,
+ u_int32_t aAdded, const TTHValue* root) throw(QueueException, FileException);
+
+ QueueItem* find(const string& target);
+ void find(QueueItem::List& sl, int64_t aSize, const string& ext);
+ void find(QueueItem::List& ql, const TTHValue& tth);
+
+ QueueItem* findAutoSearch(StringList& recent);
+ size_t getSize() { return queue.size(); };
+ QueueItem::StringMap& getQueue() { return queue; };
+ void move(QueueItem* qi, const string& aTarget);
+ void remove(QueueItem* qi) {
+ if(lastInsert != queue.end() && Util::stricmp(*lastInsert->first, qi->getTarget()) == 0)
+ lastInsert = queue.end();
+ queue.erase(const_cast<string*>(&qi->getTarget()));
+ delete qi;
+ }
+
+ private:
+ QueueItem::StringMap queue;
+ /** A hint where to insert an item... */
+ QueueItem::StringIter lastInsert;
+ };
+
+ /** All queue items indexed by user (this is a cache for the FileQueue really...) */
+ class UserQueue {
+ public:
+ void add(QueueItem* qi);
+ void add(QueueItem* qi, const User::Ptr& aUser);
+ QueueItem* getNext(const User::Ptr& aUser, QueueItem::Priority minPrio = QueueItem::LOWEST);
+ QueueItem* getRunning(const User::Ptr& aUser);
+ void setRunning(QueueItem* qi, const User::Ptr& aUser);
+ void setWaiting(QueueItem* qi);
+ QueueItem::UserListMap& getList(int p) { return userQueue[p]; };
+ void remove(QueueItem* qi);
+ void remove(QueueItem* qi, const User::Ptr& aUser);
+
+ QueueItem::UserMap& getRunning() { return running; };
+ bool isRunning(const User::Ptr& aUser) const {
+ return (running.find(aUser) != running.end());
+ };
+ private:
+ /** QueueItems by priority and user (this is where the download order is determined) */
+ QueueItem::UserListMap userQueue[QueueItem::LAST];
+ /** Currently running downloads, a QueueItem is always either here or in the userQueue */
+ QueueItem::UserMap running;
+ };
+
+ friend class QueueLoader;
+ friend class Singleton<QueueManager>;
+
+ QueueManager();
+ virtual ~QueueManager() throw();
+
+ CriticalSection cs;
+
+ /** Partial file list queue */
+ PfsQueue pfsQueue;
+ /** QueueItems by target */
+ FileQueue fileQueue;
+ /** QueueItems by user */
+ UserQueue userQueue;
+ /** Directories queued for downloading */
+ DirectoryItem::DirectoryMap directories;
+ /** Recent searches list, to avoid searching for the same thing too often */
+ StringList recent;
+ /** The queue needs to be saved */
+ bool dirty;
+ /** Next search */
+ u_int32_t nextSearch;
+
+ static const string USER_LIST_NAME;
+
+ /** Sanity check for the target filename */
+ static string checkTarget(const string& aTarget, int64_t aSize, int& flags) throw(QueueException, FileException);
+ /** Add a source to an existing queue item */
+ bool addSource(QueueItem* qi, const string& aFile, User::Ptr aUser, Flags::MaskType addBad, bool utf8) throw(QueueException, FileException);
+
+ int matchFiles(DirectoryListing::Directory* dir) throw();
+ void processList(const string& name, User::Ptr& user, int flags);
+
+ void load(SimpleXML* aXml);
+
+ void setDirty() {
+ if(!dirty) {
+ dirty = true;
+ lastSave = GET_TICK();
+ }
+ }
+
+ // TimerManagerListener
+ virtual void on(TimerManagerListener::Second, u_int32_t aTick) throw();
+ virtual void on(TimerManagerListener::Minute, u_int32_t aTick) throw();
+
+ // SearchManagerListener
+ virtual void on(SearchManagerListener::SR, SearchResult*) throw();
+
+ // ClientManagerListener
+ virtual void on(ClientManagerListener::UserUpdated, const User::Ptr& aUser) throw();
+};
+
+#endif // !defined(AFX_QUEUEMANAGER_H__07D44A33_1277_482D_AFB4_05E3473B4379__INCLUDED_)
+
+/**
+ * @file
+ * $Id: QueueManager.h,v 1.2 2005/08/21 14:03:43 olof Exp $
+ */
+
diff --git a/dcpp/QueueManagerListener.h b/dcpp/QueueManagerListener.h
new file mode 100644
index 0000000..e7da6a8
--- /dev/null
+++ b/dcpp/QueueManagerListener.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2001-2005 Jacek Sieka, arnetheduck on gmail point 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 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.
+ */
+
+#if !defined(AFX_QUEUEMANAGERLISTENER_H__07D44A33_1277_482D_AFB4_05E3473B4379__INCLUDED_)
+#define AFX_QUEUEMANAGERLISTENER_H__07D44A33_1277_482D_AFB4_05E3473B4379__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+class QueueItem;
+
+class QueueManagerListener {
+public:
+ template<int I> struct X { enum { TYPE = I }; };
+
+ typedef X<0> Added;
+ typedef X<1> Finished;
+ typedef X<2> Removed;
+ typedef X<3> Moved;
+ typedef X<4> SourcesUpdated;
+ typedef X<5> StatusUpdated;
+ typedef X<6> SearchStringUpdated;
+ typedef X<7> PartialList;
+
+ virtual void on(Added, QueueItem*) throw() { }
+ virtual void on(Finished, QueueItem*) throw() { }
+ virtual void on(Removed, QueueItem*) throw() { }
+ virtual void on(Moved, QueueItem*) throw() { }
+ virtual void on(SourcesUpdated, QueueItem*) throw() { }
+ virtual void on(StatusUpdated, QueueItem*) throw() { }
+ virtual void on(SearchStringUpdated, QueueItem*) throw() { }
+ virtual void on(PartialList, const User::Ptr&, const string&) throw() { }
+};
+
+#endif
+
+/**
+ * @file
+ * $Id: QueueManagerListener.h,v 1.2 2005/08/21 14:03:43 olof Exp $
+ */
diff --git a/dcpp/ResourceManager.cpp b/dcpp/ResourceManager.cpp
new file mode 100644
index 0000000..5b95a79
--- /dev/null
+++ b/dcpp/ResourceManager.cpp
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2001-2005 Jacek Sieka, arnetheduck on gmail point 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 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.
+ */
+
+#include "stdinc.h"
+#include "DCPlusPlus.h"
+
+#include "ResourceManager.h"
+
+#include "SimpleXML.h"
+#include "File.h"
+#include "Text.h"
+
+wstring ResourceManager::wstrings[ResourceManager::LAST];
+
+void ResourceManager::loadLanguage(const string& aFile) {
+ try {
+ File f(aFile, File::READ, File::OPEN);
+ SimpleXML xml;
+ xml.fromXML(f.read());
+
+ HASH_MAP<string, int> h;
+
+ for(int i = 0; i < LAST; ++i) {
+ h[names[i]] = i;
+ }
+
+ if(xml.findChild("Language")) {
+ xml.stepIn();
+ if(xml.findChild("Strings")) {
+ xml.stepIn();
+
+ while(xml.findChild("String")) {
+ HASH_MAP<string, int>::iterator j = h.find(xml.getChildAttrib("Name"));
+
+ if(j != h.end()) {
+ strings[j->second] = xml.getChildData();
+ }
+ }
+ createWide();
+ }
+ }
+ } catch(const Exception&) {
+ // ...
+ }
+}
+
+void ResourceManager::createWide() {
+ for(int i = 0; i < LAST; ++i) {
+ wstrings[i].clear();
+ Text::utf8ToWide(strings[i], wstrings[i]);
+ }
+}
+
+/**
+ * @file
+ * $Id: ResourceManager.cpp,v 1.2 2005/08/21 14:03:43 olof Exp $
+ */
+
diff --git a/dcpp/ResourceManager.h b/dcpp/ResourceManager.h
new file mode 100644
index 0000000..fd256c2
--- /dev/null
+++ b/dcpp/ResourceManager.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2001-2005 Jacek Sieka, arnetheduck on gmail point 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 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.
+ */
+
+#if !defined(AFX_RESOURCEMANAGER_H__AA978E1D_82F9_434B_8C3C_1D58B93F7582__INCLUDED_)
+#define AFX_RESOURCEMANAGER_H__AA978E1D_82F9_434B_8C3C_1D58B93F7582__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+#include "Singleton.h"
+
+class ResourceManager : public Singleton<ResourceManager> {
+public:
+
+#include "StringDefs.h"
+
+ void loadLanguage(const string& aFile);
+ const string& getString(Strings x) const { dcassert(x >= 0 && x < LAST); return strings[x]; };
+ const wstring& getStringW(Strings x) const { dcassert(x >= 0 && x < LAST); return wstrings[x]; };
+
+private:
+ friend class Singleton<ResourceManager>;
+
+ typedef HASH_MAP<string, Strings> NameMap;
+ typedef NameMap::iterator NameIter;
+
+ ResourceManager() {
+ createWide();
+ };
+
+ virtual ~ResourceManager() { };
+
+ static string strings[LAST];
+ static wstring wstrings[LAST];
+ static string names[LAST];
+
+ void createWide();
+};
+
+
+#define STRING(x) ResourceManager::getInstance()->getString(ResourceManager::x)
+#define CSTRING(x) ResourceManager::getInstance()->getString(ResourceManager::x).c_str()
+#define WSTRING(x) ResourceManager::getInstance()->getStringW(ResourceManager::x)
+#define CWSTRING(x) ResourceManager::getInstance()->getStringW(ResourceManager::x).c_str()
+
+#define STRING_I(x) ResourceManager::getInstance()->getString(x)
+#define CSTRING_I(x) ResourceManager::getInstance()->getString(x).c_str()
+#define WSTRING_I(x) ResourceManager::getInstance()->getStringW(x)
+#define CWSTRING_I(x) ResourceManager::getInstance()->getStringW(x).c_str()
+
+#ifdef UNICODE
+#define TSTRING WSTRING
+#define CTSTRING CWSTRING
+#define CTSTRING_I CWSTRING_I
+#else
+#define TSTRING STRING
+#define CTSTRING CSTRING
+#endif
+
+
+#endif // !defined(AFX_RESOURCEMANAGER_H__AA978E1D_82F9_434B_8C3C_1D58B93F7582__INCLUDED_)
+
+/**
+ * @file
+ * $Id: ResourceManager.h,v 1.2 2005/08/21 14:03:43 olof Exp $
+ */
diff --git a/dcpp/SConscript b/dcpp/SConscript
new file mode 100644
index 0000000..8eb8131
--- /dev/null
+++ b/dcpp/SConscript
@@ -0,0 +1,7 @@
+
+Import('env')
+
+# we should also define -DHAVE_ASM_ATOMIC_H if not a fedora system
+env.Append(CXXFLAGS=['-DXTHREADS', '-D_REENTRANT', '-DXUSE_MTSAFE_API', '-D_GNU_SOURCE', '-Wno-non-virtual-dtor'])
+
+env.Library('dcpp', Split('ADLSearch.cpp AdcHub.cpp AdcCommand.cpp BZUtils.cpp BufferedSocket.cpp Client.cpp ClientManager.cpp ConnectionManager.cpp CryptoManager.cpp DCPlusPlus.cpp DirectoryListing.cpp DownloadManager.cpp Encoder.cpp Exception.cpp FinishedManager.cpp HashManager.cpp HttpConnection.cpp HubManager.cpp LogManager.cpp NmdcHub.cpp QueueManager.cpp ResourceManager.cpp SFVReader.cpp SearchManager.cpp ServerSocket.cpp SettingsManager.cpp ShareManager.cpp SimpleXML.cpp Socket.cpp StringDefs.cpp StringTokenizer.cpp Text.cpp Thread.cpp TigerHash.cpp TimerManager.cpp User.cpp UserConnection.cpp Util.cpp ZUtils.cpp stdinc.cpp UploadManager.cpp'))
diff --git a/dcpp/SFVReader.cpp b/dcpp/SFVReader.cpp
new file mode 100644
index 0000000..13dfd9d
--- /dev/null
+++ b/dcpp/SFVReader.cpp
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2001-2005 Jacek Sieka, arnetheduck on gmail point 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 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.
+ */
+
+#include "stdinc.h"
+#include "DCPlusPlus.h"
+
+#include "SFVReader.h"
+
+#include "StringTokenizer.h"
+
+#ifndef _WIN32
+#include <sys/types.h>
+#include <dirent.h>
+#include <fnmatch.h>
+#endif
+
+bool SFVReader::tryFile(const string& sfvFile, const string& fileName) throw(FileException) {
+
+ string sfv = File(sfvFile, File::READ, File::OPEN).read();
+
+ string::size_type i = 0;
+ while( (i = Util::findSubString(sfv, fileName, i)) != string::npos) {
+ // Either we're at the beginning of the file or the line...otherwise skip...
+ if( (i == 0) || (sfv[i-1] == '\n') ) {
+ string::size_type j = i + fileName.length() + 1;
+ if(j < sfv.length() - 8) {
+ sscanf(sfv.c_str() + j, "%x", &crc32);
+ crcFound = true;
+ return true;
+ }
+ }
+ i += fileName.length();
+ }
+
+ return false;
+}
+
+void SFVReader::load(const string& fileName) throw() {
+#ifdef _WIN32
+ string path = Util::getFilePath(fileName);
+ string fname = Util::getFileName(fileName);
+
+ WIN32_FIND_DATA fd;
+ HANDLE hf = FindFirstFile(Text::toT(path + "*.sfv").c_str(), &fd);
+ if(hf == INVALID_HANDLE_VALUE) {
+ return;
+ }
+
+ do {
+ try {
+ if(tryFile(path + Text::fromT(fd.cFileName), fname)) {
+ FindClose(hf);
+ return;
+ }
+ } catch(const FileException&) {
+ // Ignore...
+ }
+ } while(FindNextFile(hf, &fd));
+
+ FindClose(hf);
+
+#else
+ string path = Util::getFilePath(fileName);
+ string fname = Util::getFileName(fileName);
+
+ DIR* dir = opendir(path.c_str());
+ if (!dir)
+ return;
+ while (struct dirent* ent = readdir(dir)) {
+ if (fnmatch("*.sfv", ent->d_name, 0) == 0) {
+ try {
+ if(tryFile(path + Text::fromT(ent->d_name), fname)) {
+ closedir(dir);
+ return;
+ }
+ } catch(const FileException&) {
+ // Ignore...
+ }
+ }
+ }
+
+ closedir(dir);
+
+#endif
+}
+
+/**
+ * @file
+ * $Id: SFVReader.cpp,v 1.2 2005/08/21 14:03:43 olof Exp $
+ */
diff --git a/dcpp/SFVReader.h b/dcpp/SFVReader.h
new file mode 100644
index 0000000..435310c
--- /dev/null
+++ b/dcpp/SFVReader.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2001-2005 Jacek Sieka, arnetheduck on gmail point 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 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.
+ */
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+#include "File.h"
+
+class SFVReader {
+public:
+ /** @see load */
+ SFVReader(const string& aFileName) : crc32(0), crcFound(false) { load(aFileName); };
+
+ /**
+ * Search for a CRC32 file in all .sfv files in the directory of fileName.
+ * Each SFV file has a number of lines containing a filename and its CRC32 value
+ * in the form:
+ * filename.ext xxxxxxxx
+ * where the x's represent the file's crc32 value. Lines starting with ';' are
+ * considered comments, and we throw away lines with ' ' or '#' as well
+ * (pdSFV 1.2 does this...).
+ */
+ void load(const string& fileName) throw();
+
+ bool hasCRC() const throw() { return crcFound; };
+ u_int32_t getCRC() const throw() { return crc32; };
+
+private:
+
+ u_int32_t crc32;
+ bool crcFound;
+
+ bool tryFile(const string& sfvFile, const string& fileName) throw(FileException);
+
+};
+
+/**
+ * @file
+ * $Id: SFVReader.h,v 1.2 2005/08/21 14:03:43 olof Exp $
+ */
diff --git a/dcpp/SearchManager.cpp b/dcpp/SearchManager.cpp
new file mode 100644
index 0000000..b3c4a1e
--- /dev/null
+++ b/dcpp/SearchManager.cpp
@@ -0,0 +1,344 @@
+/*
+ * Copyright (C) 2001-2005 Jacek Sieka, arnetheduck on gmail point 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 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.
+ */
+
+#include "stdinc.h"
+#include "DCPlusPlus.h"
+
+#include "SearchManager.h"
+#include "UploadManager.h"
+
+#include "ClientManager.h"
+#include "ShareManager.h"
+
+SearchResult::SearchResult(Client* aClient, Types aType, int64_t aSize, const string& aFile, const TTHValue* aTTH, bool aUtf8) :
+ file(aFile), hubName(aClient->getName()), hubIpPort(aClient->getIpPort()), user(aClient->getMe()),
+ size(aSize), type(aType), slots(SETTING(SLOTS)), freeSlots(UploadManager::getInstance()->getFreeSlots()),
+ tth(aTTH == NULL ? NULL : new TTHValue(*aTTH)), utf8(aUtf8), ref(1) { }
+
+SearchResult::SearchResult(Types aType, int64_t aSize, const string& aFile, const TTHValue* aTTH) :
+ file(aFile), size(aSize), type(aType), slots(SETTING(SLOTS)), freeSlots(UploadManager::getInstance()->getFreeSlots()),
+ tth(aTTH == NULL ? NULL : new TTHValue(*aTTH)), utf8(true), ref(1) { }
+
+string SearchResult::toSR() const {
+ // File: "$SR %s %s%c%s %d/%d%c%s (%s)|"
+ // Directory: "$SR %s %s %d/%d%c%s (%s)|"
+ string tmp;
+ tmp.reserve(128);
+ tmp.append("$SR ", 4);
+ tmp.append(Text::utf8ToAcp(user->getNick()));
+ tmp.append(1, ' ');
+ string acpFile = utf8 ? Text::utf8ToAcp(file) : file;
+ if(type == TYPE_FILE) {
+ tmp.append(acpFile);
+ tmp.append(1, '\x05');
+ tmp.append(Util::toString(size));
+ } else {
+ tmp.append(acpFile, 0, acpFile.length() - 1);
+ }
+ tmp.append(1, ' ');
+ tmp.append(Util::toString(freeSlots));
+ tmp.append(1, '/');
+ tmp.append(Util::toString(slots));
+ tmp.append(1, '\x05');
+ if(getTTH() == NULL) {
+ tmp.append(Text::utf8ToAcp(hubName));
+ } else {
+ tmp.append("TTH:" + getTTH()->toBase32());
+ }
+ tmp.append(" (", 2);
+ tmp.append(hubIpPort);
+ tmp.append(")|", 2);
+ return tmp;
+}
+
+AdcCommand SearchResult::toRES(char type) const {
+ AdcCommand cmd(AdcCommand::CMD_RES, type);
+ cmd.addParam("SI", Util::toString(size));
+ cmd.addParam("SL", Util::toString(freeSlots));
+ cmd.addParam("FN", Util::toAdcFile(utf8 ? file : Text::acpToUtf8(file)));
+ if(getTTH() != NULL) {
+ cmd.addParam("TR", getTTH()->toBase32());
+ }
+ return cmd;
+}
+
+void SearchManager::search(const string& aName, int64_t aSize, TypeModes aTypeMode /* = TYPE_ANY */, SizeModes aSizeMode /* = SIZE_ATLEAST */, const string& aToken /* = Util::emptyString */) {
+ if(okToSearch()) {
+ ClientManager::getInstance()->search(aSizeMode, aSize, aTypeMode, aName, aToken);
+ lastSearch = GET_TICK();
+ }
+}
+
+void SearchManager::search(StringList& who, const string& aName, int64_t aSize /* = 0 */, TypeModes aTypeMode /* = TYPE_ANY */, SizeModes aSizeMode /* = SIZE_ATLEAST */, const string& aToken /* = Util::emptyString */) {
+ if(okToSearch()) {
+ ClientManager::getInstance()->search(who, aSizeMode, aSize, aTypeMode, aName, aToken);
+ lastSearch = GET_TICK();
+ }
+}
+
+string SearchResult::getFileName() const {
+ if(getType() == TYPE_FILE)
+ return Util::getFileName(getFile());
+
+ if(getFile().size() < 2)
+ return getFile();
+
+ string::size_type i = getFile().rfind('\\', getFile().length() - 2);
+ if(i == string::npos)
+ return getFile();
+
+ return getFile().substr(i + 1);
+}
+
+void SearchManager::setPort(short aPort) throw(SocketException) {
+ port = aPort;
+ if(socket != NULL) {
+ disconnect();
+ } else {
+ socket = new Socket();
+ }
+
+ socket->create(Socket::TYPE_UDP, true);
+ socket->bind(aPort);
+ start();
+}
+
+void SearchManager::disconnect() throw() {
+ if(socket != NULL) {
+ stop = true;
+ socket->disconnect();
+#ifdef _WIN32
+ join();
+#endif
+ stop = false;
+ }
+}
+
+#define BUFSIZE 8192
+int SearchManager::run() {
+
+ AutoArray<u_int8_t> buf(BUFSIZE);
+ int len;
+
+ while(true) {
+
+ string remoteAddr;
+ try {
+ while( (len = socket->read((u_int8_t*)buf, BUFSIZE, remoteAddr)) != 0) {
+ onData(buf, len, remoteAddr);
+ }
+ } catch(const SocketException& e) {
+ dcdebug("SearchManager::run Error: %s\n", e.getError().c_str());
+ }
+ if(stop) {
+ return 0;
+ }
+
+ try {
+ socket->disconnect();
+ socket->create(Socket::TYPE_UDP, true);
+ socket->bind(port);
+ } catch(const SocketException& e) {
+ // Oops, fatal this time...
+ dcdebug("SearchManager::run Stopped listening: %s\n", e.getError().c_str());
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+void SearchManager::onData(const u_int8_t* buf, size_t aLen, const string& address) {
+ string x((char*)buf, aLen);
+ if(x.compare(0, 4, "$SR ") == 0) {
+ string::size_type i, j;
+ // Directories: $SR <nick><0x20><directory><0x20><free slots>/<total slots><0x05><Hubname><0x20>(<Hubip:port>)
+ // Files: $SR <nick><0x20><filename><0x05><filesize><0x20><free slots>/<total slots><0x05><Hubname><0x20>(<Hubip:port>)
+ i = 4;
+ if( (j = x.find(' ', i)) == string::npos) {
+ return;
+ }
+ string nick = Text::acpToUtf8(x.substr(i, j-i));
+ i = j + 1;
+
+ // A file has 2 0x05, a directory only one
+ size_t cnt = count(x.begin() + j, x.end(), 0x05);
+
+ SearchResult::Types type = SearchResult::TYPE_FILE;
+ string file;
+ int64_t size = 0;
+
+ if(cnt == 1) {
+ // We have a directory...find the first space beyond the first 0x05 from the back
+ // (dirs might contain spaces as well...clever protocol, eh?)
+ type = SearchResult::TYPE_DIRECTORY;
+ // Get past the hubname that might contain spaces
+ if((j = x.rfind(0x05)) == string::npos) {
+ return;
+ }
+ // Find the end of the directory info
+ if((j = x.rfind(' ', j-1)) == string::npos) {
+ return;
+ }
+ if(j < i + 1) {
+ return;
+ }
+ file = x.substr(i, j-i) + '\\';
+ } else if(cnt == 2) {
+ if( (j = x.find((char)5, i)) == string::npos) {
+ return;
+ }
+ file = x.substr(i, j-i);
+ i = j + 1;
+ if( (j = x.find(' ', i)) == string::npos) {
+ return;
+ }
+ size = Util::toInt64(x.substr(i, j-i));
+ }
+ i = j + 1;
+
+ if( (j = x.find('/', i)) == string::npos) {
+ return;
+ }
+ int freeSlots = Util::toInt(x.substr(i, j-i));
+ i = j + 1;
+ if( (j = x.find((char)5, i)) == string::npos) {
+ return;
+ }
+ int slots = Util::toInt(x.substr(i, j-i));
+ i = j + 1;
+ if( (j = x.rfind(" (")) == string::npos) {
+ return;
+ }
+ // the hub's name will get replaced later (with a UTF-8 version) if there's a TTH in the result
+ string hubName = Text::acpToUtf8(x.substr(i, j-i));
+ i = j + 2;
+ if( (j = x.rfind(')')) == string::npos) {
+ return;
+ }
+ string hubIpPort = x.substr(i, j-i);
+ User::Ptr user = ClientManager::getInstance()->getUser(nick, hubIpPort);
+
+ SearchResult* sr = new SearchResult(user, type, slots, freeSlots, size,
+ file, hubName, hubIpPort, address, false);
+ fire(SearchManagerListener::SR(), sr);
+ sr->decRef();
+ } else if(x.compare(1, 4, "RES ") == 0 && x[x.length() - 1] == 0x0a) {
+ AdcCommand c(x.substr(0, x.length()-1));
+ if(c.getParameters().empty())
+ return;
+
+ User::Ptr p = ClientManager::getInstance()->getUser(c.getFrom(), false);
+ if(!p)
+ return;
+
+ int freeSlots = -1;
+ int64_t size = -1;
+ string name;
+
+ for(StringIter i = c.getParameters().begin(); i != c.getParameters().end(); ++i) {
+ string& str = *i;
+ if(str.compare(0, 2, "FN") == 0) {
+ name = Util::toNmdcFile(str.substr(2));
+ } else if(str.compare(0, 2, "SL") == 0) {
+ freeSlots = Util::toInt(str.substr(2));
+ } else if(str.compare(0, 2, "SI") == 0) {
+ size = Util::toInt64(str.substr(2));
+ }
+ }
+
+ if(!name.empty() && freeSlots != -1 && size != -1) {
+ SearchResult::Types type = (name[name.length() - 1] == '\\' ? SearchResult::TYPE_DIRECTORY : SearchResult::TYPE_FILE);
+ SearchResult* sr = new SearchResult(p, type, p->getSlots(), freeSlots, size, name, p->getClientName(), "0.0.0.0", NULL, true);
+ fire(SearchManagerListener::SR(), sr);
+ sr->decRef();
+ }
+ } else if(x.compare(1, 4, "SCH ") == 0 && x[x.length() - 1] == 0x0a) {
+ try {
+ respond(AdcCommand(x.substr(0, x.length()-1)));
+ } catch(ParseException& ) {
+ }
+ }
+}
+
+void SearchManager::respond(const AdcCommand& adc) {
+ // Filter own searches
+ if(adc.getFrom().toBase32() == SETTING(CLIENT_ID))
+ return;
+
+ User::Ptr p = ClientManager::getInstance()->getUser(adc.getFrom(), false);
+ if(!p)
+ return;
+
+ SearchResult::List results;
+ ShareManager::getInstance()->search(results, adc.getParameters(), 10);
+
+ string token;
+
+ adc.getParam("TO", 0, token);
+
+ if(results.empty())
+ return;
+
+ if(!p->getIp().empty() && p->getUDPPort() != 0) {
+ Socket s;
+ try {
+ s.create(Socket::TYPE_UDP);
+ for(SearchResult::Iter i = results.begin(); i != results.end(); ++i) {
+ if(token.empty())
+ s.writeTo(p->getIp(), p->getUDPPort(), (*i)->toRES(AdcCommand::TYPE_UDP).toString());
+ else
+ s.writeTo(p->getIp(), p->getUDPPort(), (*i)->toRES(AdcCommand::TYPE_UDP).addParam("TO", token).toString());
+ (*i)->decRef();
+ }
+ } catch(const SocketException&) {
+ dcdebug("Search caught error\n");
+ }
+ } else {
+ for(SearchResult::Iter i = results.begin(); i != results.end(); ++i) {
+ AdcCommand cmd = (*i)->toRES(AdcCommand::TYPE_DIRECT);
+ cmd.setTo(adc.getFrom());
+ if(!token.empty())
+ cmd.addParam("TO", token);
+ p->send(cmd.toString());
+ (*i)->decRef();
+ }
+ }
+}
+
+string SearchManager::clean(const string& aSearchString) {
+ static const char* badChars = "$|.[]()-_+";
+ string::size_type i = aSearchString.find_first_of(badChars);
+ if(i == string::npos)
+ return aSearchString;
+
+ string tmp = aSearchString;
+ // Remove all strange characters from the search string
+ do {
+ tmp[i] = ' ';
+ } while ( (i = tmp.find_first_of(badChars, i)) != string::npos);
+
+ return tmp;
+}
+
+
+/**
+ * @file
+ * $Id: SearchManager.cpp,v 1.2 2005/08/21 14:03:43 olof Exp $
+ */
+
diff --git a/dcpp/SearchManager.h b/dcpp/SearchManager.h
new file mode 100644
index 0000000..672774d
--- /dev/null
+++ b/dcpp/SearchManager.h
@@ -0,0 +1,206 @@
+ /*
+ * Copyright (C) 2001-2005 Jacek Sieka, arnetheduck on gmail point 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 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.
+ */
+
+#if !defined(AFX_SEARCHMANAGER_H__E8F009DF_D216_4F8F_8C81_07D2FA0BFB7F__INCLUDED_)
+#define AFX_SEARCHMANAGER_H__E8F009DF_D216_4F8F_8C81_07D2FA0BFB7F__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+#include "SettingsManager.h"
+
+#include "Socket.h"
+#include "User.h"
+#include "Thread.h"
+#include "Client.h"
+#include "Singleton.h"
+#include "FastAlloc.h"
+#include "MerkleTree.h"
+
+#include "SearchManagerListener.h"
+#include "TimerManager.h"
+#include "AdcCommand.h"
+
+class SearchManager;
+
+class SearchResult : public FastAlloc<SearchResult> {
+public:
+
+ enum Types {
+ TYPE_FILE,
+ TYPE_DIRECTORY
+ };
+
+ typedef SearchResult* Ptr;
+ typedef vector<Ptr> List;
+ typedef List::iterator Iter;
+
+ SearchResult(Client* aClient, Types aType, int64_t aSize, const string& name, const TTHValue* aTTH, bool aUtf8);
+ SearchResult(Types aType, int64_t aSize, const string& name, const TTHValue* aTTH);
+
+ SearchResult(const User::Ptr& aUser, Types aType, int aSlots, int aFreeSlots,
+ int64_t aSize, const string& aFile, const string& aHubName,
+ const string& aHubIpPort, const string& aIp, bool aUtf8) :
+ file(aFile), hubName(isTTH(aHubName) ? Util::emptyString : aHubName), hubIpPort(aHubIpPort), user(aUser),
+ size(aSize), type(aType), slots(aSlots), freeSlots(aFreeSlots), IP(aIp),
+ tth(isTTH(aHubName) ? new TTHValue(aHubName.substr(4)) : NULL), utf8(aUtf8), ref(1) { }
+
+ SearchResult(const User::Ptr& aUser, Types aType, int aSlots, int aFreeSlots,
+ int64_t aSize, const string& aFile, const string& aHubName,
+ const string& aHubIpPort, TTHValue* aTTH, bool aUtf8) :
+ file(aFile), hubName(aHubName), hubIpPort(aHubIpPort), user(aUser),
+ size(aSize), type(aType), slots(aSlots), freeSlots(aFreeSlots),
+ tth((aTTH != NULL) ? new TTHValue(*aTTH) : NULL), utf8(aUtf8), ref(1) { }
+
+ string getFileName() const;
+ string toSR() const;
+ AdcCommand toRES(char type) const;
+
+ User::Ptr& getUser() { return user; }
+ string getSlotString() const { return Util::toString(getFreeSlots()) + '/' + Util::toString(getSlots()); }
+
+ const string& getFile() const { return file; }
+ const string& getHubIpPort() const { return hubIpPort; }
+ const string& getHubName() const { return hubName.empty() ? user->getClientName() : hubName; }
+ int64_t getSize() const { return size; }
+ Types getType() const { return type; }
+ int getSlots() const { return slots; }
+ int getFreeSlots() const { return freeSlots; }
+ const string& getIP() const { return IP; }
+ TTHValue* getTTH() const { return tth; }
+ bool getUtf8() const { return utf8; }
+
+ void incRef() { Thread::safeInc(ref); }
+ void decRef() {
+ if(Thread::safeDec(ref) == 0)
+ delete this;
+ };
+
+private:
+ friend class SearchManager;
+
+ SearchResult();
+ ~SearchResult() { delete tth; };
+
+ SearchResult(const SearchResult& rhs);
+
+ string file;
+ string hubName;
+ string hubIpPort;
+ User::Ptr user;
+ int64_t size;
+ Types type;
+ int slots;
+ int freeSlots;
+ string IP;
+ TTHValue* tth;
+
+ bool utf8;
+ volatile long ref;
+
+ bool isTTH(const string& str) const {
+ return str.compare(0, 4, "TTH:") == 0;
+ }
+};
+
+class SearchManager : public Speaker<SearchManagerListener>, public Singleton<SearchManager>, public Thread
+{
+public:
+ enum SizeModes {
+ SIZE_DONTCARE = 0x00,
+ SIZE_ATLEAST = 0x01,
+ SIZE_ATMOST = 0x02
+ };
+
+ enum TypeModes {
+ TYPE_ANY = 0,
+ TYPE_AUDIO,
+ TYPE_COMPRESSED,
+ TYPE_DOCUMENT,
+ TYPE_EXECUTABLE,
+ TYPE_PICTURE,
+ TYPE_VIDEO,
+ TYPE_DIRECTORY,
+ TYPE_TTH
+ };
+
+ void search(const string& aName, int64_t aSize, TypeModes aTypeMode, SizeModes aSizeMode, const string& aToken);
+ void search(const string& aName, const string& aSize, TypeModes aTypeMode, SizeModes aSizeMode, const string& aToken) {
+ search(aName, Util::toInt64(aSize), aTypeMode, aSizeMode, aToken);
+ }
+
+ void search(StringList& who, const string& aName, int64_t aSize, TypeModes aTypeMode, SizeModes aSizeMode, const string& aToken);
+ void search(StringList& who, const string& aName, const string& aSize, TypeModes aTypeMode, SizeModes aSizeMode, const string& aToken) {
+ search(who, aName, Util::toInt64(aSize), aTypeMode, aSizeMode, aToken);
+ }
+ static string clean(const string& aSearchString);
+
+ void respond(const AdcCommand& cmd);
+
+ short getPort()
+ {
+ return port;
+ }
+
+ void setPort(short aPort) throw(SocketException);
+ void disconnect() throw();
+ void onSearchResult(const string& aLine) {
+ onData((const u_int8_t*)aLine.data(), aLine.length(), Util::emptyString);
+ }
+
+ int32_t timeToSearch() {
+ return (int32_t)(((((int64_t)lastSearch) + 5000) - GET_TICK() ) / 1000);
+ }
+
+ bool okToSearch() {
+ return timeToSearch() <= 0;
+ }
+
+private:
+
+ Socket* socket;
+ short port;
+ bool stop;
+ u_int32_t lastSearch;
+ friend class Singleton<SearchManager>;
+
+ SearchManager() : socket(NULL), port(0), stop(false), lastSearch(0) { };
+
+ virtual int run();
+
+ virtual ~SearchManager() throw() {
+ if(socket) {
+ stop = true;
+ socket->disconnect();
+#ifdef _WIN32
+ join();
+#endif
+ delete socket;
+ }
+ };
+
+ void onData(const u_int8_t* buf, size_t aLen, const string& address);
+};
+
+#endif // !defined(AFX_SEARCHMANAGER_H__E8F009DF_D216_4F8F_8C81_07D2FA0BFB7F__INCLUDED_)
+
+/**
+ * @file
+ * $Id: SearchManager.h,v 1.2 2005/08/21 14:03:43 olof Exp $
+ */
diff --git a/dcpp/SearchManagerListener.h b/dcpp/SearchManagerListener.h
new file mode 100644
index 0000000..35f07b7
--- /dev/null
+++ b/dcpp/SearchManagerListener.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2001-2005 Jacek Sieka, arnetheduck on gmail point 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 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.
+ */
+
+#if !defined(AFX_SEARCHMANAGERLISTENER_H__E8F009DF_D216_4F8F_8C81_07D2FA0BFB7F__INCLUDED_)
+#define AFX_SEARCHMANAGERLISTENER_H__E8F009DF_D216_4F8F_8C81_07D2FA0BFB7F__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+class SearchResult;
+
+class SearchManagerListener {
+public:
+ template<int I> struct X { enum { TYPE = I }; };
+
+ typedef X<0> SR;
+ virtual void on(SR, SearchResult*) throw() = 0;
+};
+
+#endif
+
+/**
+ * @file
+ * $Id: SearchManagerListener.h,v 1.2 2005/08/21 14:03:43 olof Exp $
+ */
diff --git a/dcpp/Semaphore.h b/dcpp/Semaphore.h
new file mode 100644
index 0000000..46291e3
--- /dev/null
+++ b/dcpp/Semaphore.h
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2001-2005 Jacek Sieka, arnetheduck on gmail point 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 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.
+ */
+
+#if !defined(AFX_SEMAPHORE_H__99141DD0_FECE_4131_BC9B_7BE4CF216874__INCLUDED_)
+#define AFX_SEMAPHORE_H__99141DD0_FECE_4131_BC9B_7BE4CF216874__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+#ifndef _WIN32
+#include "CriticalSection.h"
+#include <sys/time.h>
+#endif
+
+class Semaphore
+{
+#ifdef _WIN32
+public:
+ Semaphore() throw() {
+ h = CreateSemaphore(NULL, 0, MAXLONG, NULL);
+ };
+
+ void signal() throw() {
+ ReleaseSemaphore(h, 1, NULL);
+ }
+
+ bool wait() throw() { return WaitForSingleObject(h, INFINITE) == WAIT_OBJECT_0; };
+ bool wait(u_int32_t millis) throw() { return WaitForSingleObject(h, millis) == WAIT_OBJECT_0; };
+
+ ~Semaphore() throw() {
+ CloseHandle(h);
+ };
+
+private:
+ HANDLE h;
+#else
+public:
+ Semaphore() throw() : count(0) { pthread_cond_init(&cond, NULL); };
+ ~Semaphore() throw() { pthread_cond_destroy(&cond); };
+ void signal() throw() {
+ Lock l(cs);
+ count++;
+ pthread_cond_signal(&cond);
+ };
+
+ bool wait() throw() {
+ Lock l(cs);
+ if(count == 0) {
+ pthread_cond_wait(&cond, &cs.getMutex());
+ }
+ count--;
+ return true;
+ };
+ bool wait(u_int32_t millis) throw() {
+ Lock l(cs);
+ if(count == 0) {
+ timeval timev;
+ timespec t;
+ gettimeofday(&timev, NULL);
+ millis+=timev.tv_usec/1000;
+ t.tv_sec = timev.tv_sec + (millis/1000);
+ t.tv_nsec = (millis%1000)*1000*1000;
+ int ret = pthread_cond_timedwait(&cond, &cs.getMutex(), &t);
+ if(ret != 0) {
+ return false;
+ }
+ }
+ count--;
+ return true;
+ };
+
+private:
+ pthread_cond_t cond;
+ CriticalSection cs;
+ int count;
+#endif
+ Semaphore(const Semaphore&);
+ Semaphore& operator=(const Semaphore&);
+
+};
+
+#endif // !defined(AFX_SEMAPHORE_H__99141DD0_FECE_4131_BC9B_7BE4CF216874__INCLUDED_)
+
+/**
+ * @file
+ * $Id: Semaphore.h,v 1.2 2005/08/21 14:03:43 olof Exp $
+ */
diff --git a/dcpp/ServerSocket.cpp b/dcpp/ServerSocket.cpp
new file mode 100644
index 0000000..d608881
--- /dev/null
+++ b/dcpp/ServerSocket.cpp
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2001-2005 Jacek Sieka, arnetheduck on gmail point 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 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.
+ */
+
+#include "stdinc.h"
+#include "DCPlusPlus.h"
+
+#include "ServerSocket.h"
+#include "SettingsManager.h"
+
+#define MAX_CONNECTIONS 20
+
+void ServerSocket::waitForConnections(short aPort) throw(SocketException) {
+ disconnect();
+
+ sockaddr_in tcpaddr;
+ sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if(sock == -1) {
+ throw SocketException(errno);
+ }
+
+ // Set reuse address option...
+ int x = 1;
+ setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char*)&x, sizeof(x));
+
+ tcpaddr.sin_family = AF_INET;
+ tcpaddr.sin_port = htons(aPort);
+
+ // Multiple interfaces fix
+ tcpaddr.sin_addr.s_addr = inet_addr(SETTING(BIND_ADDRESS).c_str());
+ if(::bind(sock, (sockaddr *)&tcpaddr, sizeof(tcpaddr)) == SOCKET_ERROR) {
+ tcpaddr.sin_addr.s_addr = htonl(INADDR_ANY);
+ if(::bind(sock, (sockaddr *)&tcpaddr, sizeof(tcpaddr)) == SOCKET_ERROR)
+ throw SocketException(errno);
+ }
+ if(listen(sock, MAX_CONNECTIONS) == SOCKET_ERROR) {
+ throw SocketException(errno);
+ }
+}
+
+/**
+ * @file
+ * $Id: ServerSocket.cpp,v 1.2 2005/08/21 14:03:43 olof Exp $
+ */
+
diff --git a/dcpp/ServerSocket.h b/dcpp/ServerSocket.h
new file mode 100644
index 0000000..6ae2484
--- /dev/null
+++ b/dcpp/ServerSocket.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2001-2005 Jacek Sieka, arnetheduck on gmail point 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 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.
+ */
+
+#if !defined(AFX_SERVERSOCKET_H__789A5170_2834_4B7B_9E44_A22566439C9F__INCLUDED_)
+#define AFX_SERVERSOCKET_H__789A5170_2834_4B7B_9E44_A22566439C9F__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+#include "Socket.h"
+#include "Speaker.h"
+
+class ServerSocketListener {
+public:
+ template<int I> struct X { enum { TYPE = I }; };
+
+ typedef X<0> IncomingConnection;
+ virtual void on(IncomingConnection) throw() = 0;
+};
+
+class ServerSocket : public Speaker<ServerSocketListener> {
+public:
+ void waitForConnections(short aPort) throw(SocketException);
+ ServerSocket() : sock(INVALID_SOCKET) { };
+
+ virtual ~ServerSocket() throw() {
+ disconnect();
+ }
+
+ void disconnect() throw() {
+ if(sock != INVALID_SOCKET) {
+ closesocket(sock);
+ sock = INVALID_SOCKET;
+ }
+ }
+
+ socket_t getSocket() const { return sock; }
+
+ /** This is called by windows whenever an "FD_ACCEPT" is sent...doesn't work with unix... */
+ void incoming() {
+ fire(ServerSocketListener::IncomingConnection());
+ }
+
+private:
+ ServerSocket(const ServerSocket&);
+ ServerSocket& operator=(const ServerSocket&);
+
+ socket_t sock;
+};
+
+#endif // !defined(AFX_SERVERSOCKET_H__789A5170_2834_4B7B_9E44_A22566439C9F__INCLUDED_)
+
+/**
+ * @file
+ * $Id: ServerSocket.h,v 1.2 2005/08/21 14:03:43 olof Exp $
+ */
+
diff --git a/dcpp/SettingsManager.cpp b/dcpp/SettingsManager.cpp
new file mode 100644
index 0000000..b2197a4
--- /dev/null
+++ b/dcpp/SettingsManager.cpp
@@ -0,0 +1,374 @@
+/*
+ * Copyright (C) 2001-2005 Jacek Sieka, arnetheduck on gmail point 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 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.
+ */
+
+#include "stdinc.h"
+#include "DCPlusPlus.h"
+
+#include "SettingsManager.h"
+#include "ResourceManager.h"
+
+#include "SimpleXML.h"
+#include "Util.h"
+#include "File.h"
+#include "version.h"
+#include "CID.h"
+
+const string SettingsManager::settingTags[] =
+{
+ // Strings
+ "Connection", "Description", "DownloadDirectory", "EMail", "Nick", "Server",
+ "Font", "MainFrameOrder", "MainFrameWidths", "HubFrameOrder", "HubFrameWidths",
+ "LanguageFile", "SearchFrameOrder", "SearchFrameWidths", "FavoritesFrameOrder", "FavoritesFrameWidths",
+ "HublistServers", "QueueFrameOrder", "QueueFrameWidths", "PublicHubsFrameOrder", "PublicHubsFrameWidths",
+ "UsersFrameOrder", "UsersFrameWidths", "HttpProxy", "LogDirectory", "NotepadText", "LogFormatPostDownload",
+ "LogFormatPostUpload", "LogFormatMainChat", "LogFormatPrivateChat", "FinishedOrder", "FinishedWidths",
+ "TempDownloadDirectory", "BindAddress", "SocksServer", "SocksUser", "SocksPassword", "ConfigVersion",
+ "DefaultAwayMessage", "TimeStampsFormat", "ADLSearchFrameOrder", "ADLSearchFrameWidths",
+ "FinishedULWidths", "FinishedULOrder", "CID", "SpyFrameWidths", "SpyFrameOrder", "LogFileMainChat",
+ "LogFilePrivateChat", "LogFileStatus", "LogFileUpload", "LogFileDownload", "LogFileSystem",
+ "LogFormatSystem", "LogFormatStatus", "DirectoryListingFrameOrder", "DirectoryListingFrameWidths",
+ "SENTRY",
+ // Ints
+ "ConnectionType", "InPort", "Slots", "Rollback", "AutoFollow", "ClearSearch",
+ "BackgroundColor", "TextColor", "UseOemMonoFont", "ShareHidden", "FilterMessages", "MinimizeToTray",
+ "AutoSearch", "TimeStamps", "ConfirmExit", "IgnoreOffline", "PopupOffline",
+ "ListDuplicates", "BufferSize", "DownloadSlots", "MaxDownloadSpeed", "LogMainChat", "LogPrivateChat",
+ "LogDownloads", "LogUploads", "StatusInChat", "ShowJoins", "PrivateMessageBeep", "PrivateMessageBeepOpen",
+ "UseSystemIcons", "PopupPMs", "MinUploadSpeed", "GetUserInfo", "UrlHandler", "MainWindowState",
+ "MainWindowSizeX", "MainWindowSizeY", "MainWindowPosX", "MainWindowPosY", "AutoAway",
+ "SmallSendBuffer", "SocksPort", "SocksResolve", "KeepLists", "AutoKick", "QueueFrameShowTree",
+ "CompressTransfers", "ShowProgressBars", "SFVCheck", "MaxTabRows", "AutoUpdateList",
+ "MaxCompression", "FinishedDirty", "QueueDirty", "TabDirty", "AntiFrag", "MDIMaxmimized", "NoAwayMsgToBots",
+ "SkipZeroByte", "AdlsBreakOnFirst", "TabCompletion",
+ "HubUserCommands", "AutoSearchAutoMatch", "DownloadBarColor", "UploadBarColor", "LogSystem",
+ "LogFilelistTransfers", "SendUnknownCommands", "MaxHashSpeed", "OpenUserCmdHelp",
+ "GetUserCountry", "FavShowJoins", "LogStatusMessages", "ShowStatusbar",
+ "ShowToolbar", "ShowTransferview", "PopunderPm", "PopunderFilelist", "MagnetAsk", "MagnetAction", "MagnetRegister",
+ "AddFinishedInstantly", "UseUPnP", "DontDLAlreadyShared", "UseCTRLForLineHistory", "ConfirmHubRemoval",
+ "OpenNewWindow", "UDPPort", "SearchOnlyTTH", "ShowLastLinesLog", "ConfirmItemRemoval",
+ "AdvancedResume", "AdcDebug", "ToggleActiveWindow", "SearchHistory", "SetMinislotSize",
+ "HighestPrioSize", "HighPrioSize", "NormalPrioSize", "LowPrioSize", "LowestPrio",
+ "OpenPublic", "OpenFavoriteHubs", "OpenFavoriteUsers", "OpenQueue", "OpenFinishedDownloads",
+ "OpenFinishedUploads", "OpenSearchSpy", "OpenNetworkStatistics", "OpenNotepad",
+ "SENTRY",
+ // Int64
+ "TotalUpload", "TotalDownload",
+ "SENTRY"
+};
+
+const string SettingsManager::connectionSpeeds[] = { "28.8Kbps", "33.6Kbps", "56Kbps", "ISDN",
+"Satellite", "Cable", "DSL", "LAN(T1)", "LAN(T3)" };
+
+SettingsManager::SettingsManager()
+{
+ for(int i=0; i<SETTINGS_LAST; i++)
+ isSet[i] = false;
+
+ for(int j=0; j<INT_LAST-INT_FIRST; j++) {
+ intDefaults[j] = 0;
+ intSettings[j] = 0;
+ }
+ for(int k=0; k<INT64_LAST-INT64_FIRST; k++) {
+ int64Defaults[k] = 0;
+ int64Settings[k] = 0;
+ }
+
+ setDefault(DOWNLOAD_DIRECTORY, Util::getAppPath() + "Downloads" PATH_SEPARATOR_STR);
+ setDefault(TEMP_DOWNLOAD_DIRECTORY, Util::getAppPath() + "Incomplete" PATH_SEPARATOR_STR);
+ setDefault(SLOTS, 1);
+ //setDefault(SERVER, Util::getLocalIp());
+ setDefault(IN_PORT, Util::rand(1025, 32000));
+ setDefault(UDP_PORT, Util::rand(1025, 32000));
+ setDefault(ROLLBACK, 4096);
+ setDefault(AUTO_FOLLOW, true);
+ setDefault(CLEAR_SEARCH, true);
+ setDefault(SHARE_HIDDEN, false);
+ setDefault(FILTER_MESSAGES, true);
+ setDefault(MINIMIZE_TRAY, false);
+ setDefault(AUTO_SEARCH, false);
+ setDefault(TIME_STAMPS, false);
+ setDefault(CONFIRM_EXIT, false);
+ setDefault(IGNORE_OFFLINE, false);
+ setDefault(POPUP_OFFLINE, false);
+ setDefault(LIST_DUPES, true);
+ setDefault(BUFFER_SIZE, 64);
+ setDefault(HUBLIST_SERVERS, "http://www.hublist.org/PublicHubList.xml.bz2;http://dc.selwerd.nl/hublist.xml.bz2");
+ setDefault(DOWNLOAD_SLOTS, 3);
+ setDefault(MAX_DOWNLOAD_SPEED, 0);
+ setDefault(LOG_DIRECTORY, Util::getAppPath() + "Logs" PATH_SEPARATOR_STR);
+ setDefault(LOG_UPLOADS, false);
+ setDefault(LOG_DOWNLOADS, false);
+ setDefault(LOG_PRIVATE_CHAT, false);
+ setDefault(LOG_MAIN_CHAT, false);
+ setDefault(STATUS_IN_CHAT, true);
+ setDefault(SHOW_JOINS, false);
+ setDefault(CONNECTION, connectionSpeeds[0]);
+ setDefault(PRIVATE_MESSAGE_BEEP, false);
+ setDefault(PRIVATE_MESSAGE_BEEP_OPEN, false);
+ setDefault(USE_SYSTEM_ICONS, true);
+ setDefault(USE_OEM_MONOFONT, false);
+ setDefault(POPUP_PMS, true);
+ setDefault(MIN_UPLOAD_SPEED, 0);
+ setDefault(LOG_FORMAT_POST_DOWNLOAD, "%Y-%m-%d %H:%M: %[target]" + STRING(DOWNLOADED_FROM) + "%[user], %[size] (%[chunksize]), %[speed], %[time]");
+ setDefault(LOG_FORMAT_POST_UPLOAD, "%Y-%m-%d %H:%M: %[source]" + STRING(UPLOADED_TO) + "%[user], %[size] (%[chunksize]), %[speed], %[time]");
+ setDefault(LOG_FORMAT_MAIN_CHAT, "[%Y-%m-%d %H:%M] %[message]");
+ setDefault(LOG_FORMAT_PRIVATE_CHAT, "[%Y-%m-%d %H:%M] %[message]");
+ setDefault(LOG_FORMAT_STATUS, "[%Y-%m-%d %H:%M] %[message]");
+ setDefault(LOG_FORMAT_SYSTEM, "[%Y-%m-%d %H:%M] %[message]");
+ setDefault(LOG_FILE_MAIN_CHAT, "%[hubaddr].log");
+ setDefault(LOG_FILE_STATUS, "%[hubaddr]_status.log");
+ setDefault(LOG_FILE_PRIVATE_CHAT, "%[user].log");
+ setDefault(LOG_FILE_UPLOAD, "Uploads.log");
+ setDefault(LOG_FILE_DOWNLOAD, "Downloads.log");
+ setDefault(LOG_FILE_SYSTEM, "system.log");
+ setDefault(GET_USER_INFO, true);
+ setDefault(URL_HANDLER, false);
+ setDefault(AUTO_AWAY, false);
+ setDefault(SMALL_SEND_BUFFER, false);
+ setDefault(BIND_ADDRESS, "0.0.0.0");
+ setDefault(SOCKS_PORT, 1080);
+ setDefault(SOCKS_RESOLVE, 1);
+ setDefault(CONFIG_VERSION, "0.181"); // 0.181 is the last version missing configversion
+ setDefault(KEEP_LISTS, false);
+ setDefault(AUTO_KICK, false);
+ setDefault(QUEUEFRAME_SHOW_TREE, true);
+ setDefault(COMPRESS_TRANSFERS, true);
+ setDefault(SHOW_PROGRESS_BARS, true);
+ setDefault(SFV_CHECK, false);
+ setDefault(DEFAULT_AWAY_MESSAGE, "I'm away. State your business and I might answer later if you're lucky.");
+ setDefault(TIME_STAMPS_FORMAT, "%H:%M");
+ setDefault(MAX_TAB_ROWS, 2);
+ setDefault(AUTO_UPDATE_LIST, true);
+ setDefault(MAX_COMPRESSION, 6);
+ setDefault(FINISHED_DIRTY, true);
+ setDefault(QUEUE_DIRTY, true);
+ setDefault(TAB_DIRTY, true);
+ setDefault(ANTI_FRAG, false);
+ setDefault(NO_AWAYMSG_TO_BOTS, true);
+ setDefault(SKIP_ZERO_BYTE, false);
+ setDefault(ADLS_BREAK_ON_FIRST, false);
+ setDefault(TAB_COMPLETION, true);
+ setDefault(HUB_USER_COMMANDS, true);
+ setDefault(AUTO_SEARCH_AUTO_MATCH, true);
+ setDefault(LOG_FILELIST_TRANSFERS, true);
+ setDefault(LOG_SYSTEM, false);
+ setDefault(SEND_UNKNOWN_COMMANDS, true);
+ setDefault(MAX_HASH_SPEED, 0);
+ setDefault(OPEN_USER_CMD_HELP, true);
+ setDefault(GET_USER_COUNTRY, true);
+ setDefault(FAV_SHOW_JOINS, false);
+ setDefault(LOG_STATUS_MESSAGES, false);
+ setDefault(SHOW_TRANSFERVIEW, true);
+ setDefault(SHOW_STATUSBAR, true);
+ setDefault(SHOW_TOOLBAR, true);
+ setDefault(POPUNDER_PM, false);
+ setDefault(POPUNDER_FILELIST, false);
+ setDefault(MAGNET_REGISTER, true);
+ setDefault(MAGNET_ASK, true);
+ setDefault(MAGNET_ACTION, MAGNET_AUTO_SEARCH);
+ setDefault(ADD_FINISHED_INSTANTLY, false);
+ setDefault(SETTINGS_USE_UPNP, false);
+ setDefault(DONT_DL_ALREADY_SHARED, false);
+ setDefault(CONFIRM_HUB_REMOVAL, false);
+ setDefault(SETTINGS_USE_CTRL_FOR_LINE_HISTORY, true);
+ setDefault(SETTINGS_OPEN_NEW_WINDOW, false);
+ setDefault(SEARCH_ONLY_TTH, false);
+ setDefault(SHOW_LAST_LINES_LOG, 0);
+ setDefault(CONFIRM_ITEM_REMOVAL, 0);
+ setDefault(ADVANCED_RESUME, true);
+ setDefault(ADC_DEBUG, false);
+ setDefault(TOGGLE_ACTIVE_WINDOW, true);
+ setDefault(SEARCH_HISTORY, 10);
+ setDefault(SET_MINISLOT_SIZE, 64);
+ setDefault(PRIO_HIGHEST_SIZE, 64);
+ setDefault(PRIO_HIGH_SIZE, 0);
+ setDefault(PRIO_NORMAL_SIZE, 0);
+ setDefault(PRIO_LOW_SIZE, 0);
+ setDefault(PRIO_LOWEST, 0);
+ setDefault(OPEN_PUBLIC, false);
+ setDefault(OPEN_FAVORITE_HUBS, false);
+ setDefault(OPEN_FAVORITE_USERS, false);
+ setDefault(OPEN_QUEUE, false);
+ setDefault(OPEN_FINISHED_DOWNLOADS, false);
+ setDefault(OPEN_FINISHED_UPLOADS, false);
+ setDefault(OPEN_SEARCH_SPY, false);
+ setDefault(OPEN_NETWORK_STATISTICS, false);
+ setDefault(OPEN_NOTEPAD, false);
+
+#ifdef _WIN32
+ setDefault(MAIN_WINDOW_STATE, SW_SHOWNORMAL);
+ setDefault(MAIN_WINDOW_SIZE_X, CW_USEDEFAULT);
+ setDefault(MAIN_WINDOW_SIZE_Y, CW_USEDEFAULT);
+ setDefault(MAIN_WINDOW_POS_X, CW_USEDEFAULT);
+ setDefault(MAIN_WINDOW_POS_Y, CW_USEDEFAULT);
+ setDefault(MDI_MAXIMIZED, true);
+ setDefault(UPLOAD_BAR_COLOR, RGB(205, 60, 55));
+ setDefault(DOWNLOAD_BAR_COLOR, RGB(55, 170, 85));
+
+#endif
+}
+
+void SettingsManager::load(string const& aFileName)
+{
+ try {
+ SimpleXML xml;
+
+ xml.fromXML(File(aFileName, File::READ, File::OPEN).read());
+
+ xml.resetCurrentChild();
+
+ xml.stepIn();
+
+ if(xml.findChild("Settings"))
+ {
+ xml.stepIn();
+
+ int i;
+
+ for(i=STR_FIRST; i<STR_LAST; i++)
+ {
+ const string& attr = settingTags[i];
+ dcassert(attr.find("SENTRY") == string::npos);
+
+ if(xml.findChild(attr))
+ set(StrSetting(i), xml.getChildData());
+ xml.resetCurrentChild();
+ }
+ for(i=INT_FIRST; i<INT_LAST; i++)
+ {
+ const string& attr = settingTags[i];
+ dcassert(attr.find("SENTRY") == string::npos);
+
+ if(xml.findChild(attr))
+ set(IntSetting(i), Util::toInt(xml.getChildData()));
+ xml.resetCurrentChild();
+ }
+ for(i=INT64_FIRST; i<INT64_LAST; i++)
+ {
+ const string& attr = settingTags[i];
+ dcassert(attr.find("SENTRY") == string::npos);
+
+ if(xml.findChild(attr))
+ set(Int64Setting(i), Util::toInt64(xml.getChildData()));
+ xml.resetCurrentChild();
+ }
+
+ xml.stepOut();
+ }
+
+ double v = Util::toDouble(SETTING(CONFIG_VERSION));
+ // if(v < 0.x) { // Fix old settings here }
+ if(v < 0.668 && isSet[IN_PORT]) {
+ set(UDP_PORT, SETTING(IN_PORT));
+ }
+
+ if(CID(SETTING(CLIENT_ID)).isZero())
+ set(CLIENT_ID, CID::generate().toBase32());
+
+ if(SETTING(SET_MINISLOT_SIZE) < 64)
+ set(SET_MINISLOT_SIZE, 64);
+ if(SETTING(PRIO_HIGHEST_SIZE) < 16)
+ set(PRIO_HIGHEST_SIZE, 16);
+ if(SETTING(PRIO_HIGH_SIZE) < 0)
+ set(PRIO_HIGH_SIZE, 0);
+ if(SETTING(PRIO_NORMAL_SIZE) < 0)
+ set(PRIO_NORMAL_SIZE, 0);
+ if(SETTING(PRIO_LOW_SIZE) < 0)
+ set(PRIO_LOW_SIZE, 0);
+ if(SETTING(PRIO_LOWEST) < 0)
+ set(PRIO_LOWEST, 0);
+ if(SETTING(PRIO_LOWEST) > 1)
+ set(PRIO_LOWEST, 1);
+
+#ifdef _DEBUG
+ set(CLIENT_ID, CID::generate().toBase32());
+#endif
+ setDefault(UDP_PORT, SETTING(IN_PORT));
+
+ fire(SettingsManagerListener::Load(), &xml);
+
+ xml.stepOut();
+
+ } catch(const Exception&) {
+ if(CID(SETTING(CLIENT_ID)).isZero())
+ set(CLIENT_ID, CID::generate().toBase32());
+ }
+}
+
+void SettingsManager::save(string const& aFileName) {
+
+ SimpleXML xml;
+ xml.addTag("DCPlusPlus");
+ xml.stepIn();
+ xml.addTag("Settings");
+ xml.stepIn();
+
+ int i;
+ string type("type"), curType("string");
+
+ for(i=STR_FIRST; i<STR_LAST; i++)
+ {
+ if(i == CONFIG_VERSION) {
+ xml.addTag(settingTags[i], VERSIONSTRING);
+ xml.addChildAttrib(type, curType);
+ } else if(isSet[i]) {
+ xml.addTag(settingTags[i], get(StrSetting(i), false));
+ xml.addChildAttrib(type, curType);
+ }
+ }
+
+ curType = "int";
+ for(i=INT_FIRST; i<INT_LAST; i++)
+ {
+ if(isSet[i]) {
+ xml.addTag(settingTags[i], get(IntSetting(i), false));
+ xml.addChildAttrib(type, curType);
+ }
+ }
+ curType = "int64";
+ for(i=INT64_FIRST; i<INT64_LAST; i++)
+ {
+ if(isSet[i])
+ {
+ xml.addTag(settingTags[i], get(Int64Setting(i), false));
+ xml.addChildAttrib(type, curType);
+ }
+ }
+ xml.stepOut();
+
+ fire(SettingsManagerListener::Save(), &xml);
+
+ try {
+ File out(aFileName + ".tmp", File::WRITE, File::CREATE | File::TRUNCATE);
+ BufferedOutputStream<false> f(&out);
+ f.write(SimpleXML::utf8Header);
+ xml.toXML(&f);
+ f.flush();
+ out.close();
+ File::deleteFile(aFileName);
+ File::renameFile(aFileName + ".tmp", aFileName);
+ } catch(const FileException&) {
+ // ...
+ }
+}
+
+/**
+ * @file
+ * $Id: SettingsManager.cpp,v 1.2 2005/08/21 14:03:43 olof Exp $
+ */
+
diff --git a/dcpp/SettingsManager.h b/dcpp/SettingsManager.h
new file mode 100644
index 0000000..c54206a
--- /dev/null
+++ b/dcpp/SettingsManager.h
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2001-2005 Jacek Sieka, arnetheduck on gmail point 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 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.
+ */
+
+#if !defined(SETTINGSMANAGER_H)
+#define SETTINGSMANAGER_H
+
+#include "Util.h"
+#include "Speaker.h"
+#include "Singleton.h"
+
+class SimpleXML;
+
+class SettingsManagerListener {
+public:
+ template<int I> struct X { enum { TYPE = I }; };
+
+ typedef X<0> Load;
+ typedef X<1> Save;
+
+ virtual void on(Load, SimpleXML*) throw() { }
+ virtual void on(Save, SimpleXML*) throw() { }
+};
+
+class SettingsManager : public Singleton<SettingsManager>, public Speaker<SettingsManagerListener>
+{
+public:
+
+ static const string connectionSpeeds[];
+
+ enum StrSetting { STR_FIRST,
+ CONNECTION = STR_FIRST, DESCRIPTION, DOWNLOAD_DIRECTORY, EMAIL, NICK, SERVER,
+ TEXT_FONT, MAINFRAME_ORDER, MAINFRAME_WIDTHS, HUBFRAME_ORDER, HUBFRAME_WIDTHS,
+ LANGUAGE_FILE, SEARCHFRAME_ORDER, SEARCHFRAME_WIDTHS, FAVORITESFRAME_ORDER, FAVORITESFRAME_WIDTHS,
+ HUBLIST_SERVERS, QUEUEFRAME_ORDER, QUEUEFRAME_WIDTHS, PUBLICHUBSFRAME_ORDER, PUBLICHUBSFRAME_WIDTHS,
+ USERSFRAME_ORDER, USERSFRAME_WIDTHS, HTTP_PROXY, LOG_DIRECTORY, NOTEPAD_TEXT, LOG_FORMAT_POST_DOWNLOAD,
+ LOG_FORMAT_POST_UPLOAD, LOG_FORMAT_MAIN_CHAT, LOG_FORMAT_PRIVATE_CHAT, FINISHED_ORDER, FINISHED_WIDTHS,
+ TEMP_DOWNLOAD_DIRECTORY, BIND_ADDRESS, SOCKS_SERVER, SOCKS_USER, SOCKS_PASSWORD, CONFIG_VERSION,
+ DEFAULT_AWAY_MESSAGE, TIME_STAMPS_FORMAT, ADLSEARCHFRAME_ORDER, ADLSEARCHFRAME_WIDTHS,
+ FINISHED_UL_WIDTHS, FINISHED_UL_ORDER, CLIENT_ID, SPYFRAME_WIDTHS, SPYFRAME_ORDER, LOG_FILE_MAIN_CHAT,
+ LOG_FILE_PRIVATE_CHAT, LOG_FILE_STATUS, LOG_FILE_UPLOAD, LOG_FILE_DOWNLOAD, LOG_FILE_SYSTEM,
+ LOG_FORMAT_SYSTEM, LOG_FORMAT_STATUS, DIRECTORLISTINGFRAME_ORDER, DIRECTORLISTINGFRAME_WIDTHS,
+ STR_LAST };
+
+ enum IntSetting { INT_FIRST = STR_LAST + 1,
+ CONNECTION_TYPE = INT_FIRST, IN_PORT, SLOTS, ROLLBACK, AUTO_FOLLOW, CLEAR_SEARCH,
+ BACKGROUND_COLOR, TEXT_COLOR, USE_OEM_MONOFONT, SHARE_HIDDEN, FILTER_MESSAGES, MINIMIZE_TRAY,
+ AUTO_SEARCH, TIME_STAMPS, CONFIRM_EXIT, IGNORE_OFFLINE, POPUP_OFFLINE,
+ LIST_DUPES, BUFFER_SIZE, DOWNLOAD_SLOTS, MAX_DOWNLOAD_SPEED, LOG_MAIN_CHAT, LOG_PRIVATE_CHAT,
+ LOG_DOWNLOADS, LOG_UPLOADS, STATUS_IN_CHAT, SHOW_JOINS, PRIVATE_MESSAGE_BEEP, PRIVATE_MESSAGE_BEEP_OPEN,
+ USE_SYSTEM_ICONS, POPUP_PMS, MIN_UPLOAD_SPEED, GET_USER_INFO, URL_HANDLER, MAIN_WINDOW_STATE,
+ MAIN_WINDOW_SIZE_X, MAIN_WINDOW_SIZE_Y, MAIN_WINDOW_POS_X, MAIN_WINDOW_POS_Y, AUTO_AWAY,
+ SMALL_SEND_BUFFER, SOCKS_PORT, SOCKS_RESOLVE, KEEP_LISTS, AUTO_KICK, QUEUEFRAME_SHOW_TREE,
+ COMPRESS_TRANSFERS, SHOW_PROGRESS_BARS, SFV_CHECK, MAX_TAB_ROWS, AUTO_UPDATE_LIST,
+ MAX_COMPRESSION, FINISHED_DIRTY, QUEUE_DIRTY, TAB_DIRTY, ANTI_FRAG, MDI_MAXIMIZED, NO_AWAYMSG_TO_BOTS,
+ SKIP_ZERO_BYTE, ADLS_BREAK_ON_FIRST, TAB_COMPLETION,
+ HUB_USER_COMMANDS, AUTO_SEARCH_AUTO_MATCH, UPLOAD_BAR_COLOR, DOWNLOAD_BAR_COLOR, LOG_SYSTEM,
+ LOG_FILELIST_TRANSFERS, SEND_UNKNOWN_COMMANDS, MAX_HASH_SPEED, OPEN_USER_CMD_HELP,
+ GET_USER_COUNTRY, FAV_SHOW_JOINS, LOG_STATUS_MESSAGES, SHOW_STATUSBAR,
+ SHOW_TOOLBAR, SHOW_TRANSFERVIEW, POPUNDER_PM, POPUNDER_FILELIST, MAGNET_ASK, MAGNET_ACTION, MAGNET_REGISTER,
+ ADD_FINISHED_INSTANTLY, SETTINGS_USE_UPNP, DONT_DL_ALREADY_SHARED, SETTINGS_USE_CTRL_FOR_LINE_HISTORY, CONFIRM_HUB_REMOVAL,
+ SETTINGS_OPEN_NEW_WINDOW, UDP_PORT, SEARCH_ONLY_TTH, SHOW_LAST_LINES_LOG, CONFIRM_ITEM_REMOVAL,
+ ADVANCED_RESUME, ADC_DEBUG, TOGGLE_ACTIVE_WINDOW, SEARCH_HISTORY, SET_MINISLOT_SIZE,
+ PRIO_HIGHEST_SIZE, PRIO_HIGH_SIZE, PRIO_NORMAL_SIZE, PRIO_LOW_SIZE, PRIO_LOWEST,
+ OPEN_PUBLIC, OPEN_FAVORITE_HUBS, OPEN_FAVORITE_USERS, OPEN_QUEUE, OPEN_FINISHED_DOWNLOADS,
+ OPEN_FINISHED_UPLOADS, OPEN_SEARCH_SPY, OPEN_NETWORK_STATISTICS, OPEN_NOTEPAD,
+ INT_LAST };
+
+ enum Int64Setting { INT64_FIRST = INT_LAST + 1,
+ TOTAL_UPLOAD = INT64_FIRST, TOTAL_DOWNLOAD, INT64_LAST, SETTINGS_LAST = INT64_LAST };
+
+ enum { SPEED_288K, SPEED_336K, SPEED_576K, SPEED_ISDN, SPEED_SATELLITE, SPEED_CABLE,
+ SPEED_DSL, SPEED_T1, SPEED_T3, SPEED_LAST };
+
+ enum { CONNECTION_ACTIVE, CONNECTION_PASSIVE, CONNECTION_SOCKS5 };
+
+ enum { MAGNET_AUTO_SEARCH, MAGNET_AUTO_DOWNLOAD };
+
+ const string& get(StrSetting key, bool useDefault = true) const {
+ return (isSet[key] || !useDefault) ? strSettings[key - STR_FIRST] : strDefaults[key - STR_FIRST];
+ }
+
+ int get(IntSetting key, bool useDefault = true) const {
+ return (isSet[key] || !useDefault) ? intSettings[key - INT_FIRST] : intDefaults[key - INT_FIRST];
+ }
+ int64_t get(Int64Setting key, bool useDefault = true) const {
+ return (isSet[key] || !useDefault) ? int64Settings[key - INT64_FIRST] : int64Defaults[key - INT64_FIRST];
+ }
+
+ bool getBool(IntSetting key, bool useDefault = true) const {
+ return (get(key, useDefault) != 0);
+ }
+
+ void set(StrSetting key, string const& value) {
+ if(((key == DESCRIPTION) || (key == NICK)) && (value.size() > 35)) {
+ strSettings[key - STR_FIRST] = value.substr(0, 35);
+ } else {
+ strSettings[key - STR_FIRST] = value;
+ }
+ isSet[key] = !value.empty();
+ }
+
+ void set(IntSetting key, int value) {
+ if((key == SLOTS) && (value <= 0)) {
+ value = 1;
+ }
+ intSettings[key - INT_FIRST] = value;
+ isSet[key] = true;
+ }
+
+ void set(IntSetting key, const string& value) {
+ if(value.empty()) {
+ intSettings[key - INT_FIRST] = 0;
+ isSet[key] = false;
+ } else {
+ intSettings[key - INT_FIRST] = Util::toInt(value);
+ isSet[key] = true;
+ }
+ }
+
+ void set(Int64Setting key, int64_t value) {
+ int64Settings[key - INT64_FIRST] = value;
+ isSet[key] = true;
+ }
+
+ void set(Int64Setting key, const string& value) {
+ if(value.empty()) {
+ int64Settings[key - INT64_FIRST] = 0;
+ isSet[key] = false;
+ } else {
+ int64Settings[key - INT64_FIRST] = Util::toInt64(value);
+ isSet[key] = true;
+ }
+ }
+
+ void set(IntSetting key, bool value) { set(key, (int)value); }
+
+ void setDefault(StrSetting key, string const& value) {
+ strDefaults[key - STR_FIRST] = value;
+ }
+
+ void setDefault(IntSetting key, int value) {
+ intDefaults[key - INT_FIRST] = value;
+ }
+ void setDefault(Int64Setting key, int64_t value) {
+ int64Defaults[key - INT64_FIRST] = value;
+ }
+
+ bool isDefault(int aSet) { return !isSet[aSet]; };
+
+ void load() {
+ load(Util::getAppPath() + "DCPlusPlus.xml");
+ }
+ void save() {
+ save(Util::getAppPath() + "DCPlusPlus.xml");
+ }
+
+ void load(const string& aFileName);
+ void save(const string& aFileName);
+
+private:
+ friend class Singleton<SettingsManager>;
+ SettingsManager();
+ virtual ~SettingsManager() throw() { }
+
+ static const string settingTags[SETTINGS_LAST+1];
+
+ string strSettings[STR_LAST - STR_FIRST];
+ int intSettings[INT_LAST - INT_FIRST];
+ int64_t int64Settings[INT64_LAST - INT64_FIRST];
+ string strDefaults[STR_LAST - STR_FIRST];
+ int intDefaults[INT_LAST - INT_FIRST];
+ int64_t int64Defaults[INT64_LAST - INT64_FIRST];
+ bool isSet[SETTINGS_LAST];
+};
+
+// Shorthand accessor macros
+#define SETTING(k) (SettingsManager::getInstance()->get(SettingsManager::k, true))
+#define BOOLSETTING(k) (SettingsManager::getInstance()->getBool(SettingsManager::k, true))
+
+#endif // SETTINGSMANAGER_H
+
+/**
+ * @file
+ * $Id: SettingsManager.h,v 1.2 2005/08/21 14:03:43 olof Exp $
+ */
+
diff --git a/dcpp/ShareManager.cpp b/dcpp/ShareManager.cpp
new file mode 100644
index 0000000..c4d7158
--- /dev/null
+++ b/dcpp/ShareManager.cpp
@@ -0,0 +1,1484 @@
+/*
+ * Copyright (C) 2001-2005 Jacek Sieka, arnetheduck on gmail point 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 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.
+ */
+
+#include "stdinc.h"
+#include "DCPlusPlus.h"
+
+#include "ShareManager.h"
+#include "ResourceManager.h"
+
+#include "CryptoManager.h"
+#include "ClientManager.h"
+#include "LogManager.h"
+#include "HashManager.h"
+
+#include "SimpleXML.h"
+#include "StringTokenizer.h"
+#include "File.h"
+#include "FilteredFile.h"
+#include "BZUtils.h"
+
+#ifndef _WIN32
+#include <sys/types.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fnmatch.h>
+#endif
+
+#include <limits>
+
+ShareManager::ShareManager() : hits(0), listLen(0), bzXmlListLen(0),
+ xmlDirty(true), nmdcDirty(false), refreshDirs(false), update(false), initial(true), listN(0), lFile(NULL),
+ xFile(NULL), lastXmlUpdate(0), lastNmdcUpdate(0), lastFullUpdate(GET_TICK()), bloom(1<<20)
+{
+ SettingsManager::getInstance()->addListener(this);
+ TimerManager::getInstance()->addListener(this);
+ DownloadManager::getInstance()->addListener(this);
+ HashManager::getInstance()->addListener(this);
+}
+
+ShareManager::~ShareManager() {
+ SettingsManager::getInstance()->removeListener(this);
+ TimerManager::getInstance()->removeListener(this);
+ DownloadManager::getInstance()->removeListener(this);
+ HashManager::getInstance()->removeListener(this);
+
+ join();
+
+ delete lFile;
+ lFile = NULL;
+ delete xFile;
+ xFile = NULL;
+
+#ifdef _WIN32
+ WIN32_FIND_DATA data;
+ HANDLE hFind;
+
+ hFind = FindFirstFile(Text::toT(Util::getAppPath() + "files*.xml.bz2").c_str(), &data);
+ if(hFind != INVALID_HANDLE_VALUE) {
+ do {
+ if(_tcslen(data.cFileName) > 13) // length of "files.xml.bz2"
+ File::deleteFile(Util::getAppPath() + Text::fromT(data.cFileName));
+ } while(FindNextFile(hFind, &data));
+
+ FindClose(hFind);
+ }
+
+ hFind = FindFirstFile(Text::toT(Util::getAppPath() + "MyList*.DcLst").c_str(), &data);
+ if(hFind != INVALID_HANDLE_VALUE) {
+ do {
+ File::deleteFile(Util::getAppPath() + Text::fromT(data.cFileName));
+ } while(FindNextFile(hFind, &data));
+
+ FindClose(hFind);
+ }
+
+#else
+ DIR* dir = opendir(Util::getAppName().c_str());
+ if (dir) {
+ while (struct dirent* ent = readdir(dir)) {
+ if (fnmatch("files*.xml.bz2", ent->d_name, 0) == 0 ||
+ fnmatch("MyList*.DcLst", ent->d_name, 0) == 0) {
+ File::deleteFile(Util::getAppPath() + ent->d_name);
+ }
+ }
+ closedir(dir);
+ }
+#endif
+
+ for(Directory::MapIter j = directories.begin(); j != directories.end(); ++j) {
+ delete j->second;
+ }
+}
+
+ShareManager::Directory::~Directory() {
+ for(MapIter i = directories.begin(); i != directories.end(); ++i)
+ delete i->second;
+ for(File::Iter i = files.begin(); i != files.end(); ++i) {
+ ShareManager::getInstance()->removeTTH(i->getTTH(), i);
+ }
+}
+
+string ShareManager::translateFileName(const string& aFile) throw(ShareException) {
+ RLock<> l(cs);
+ if(aFile == "MyList.DcLst") {
+ generateNmdcList();
+ return getListFile();
+ } else if(aFile == "files.xml.bz2") {
+ generateXmlList();
+ return getBZXmlFile();
+ } else {
+ if(aFile.length() < 3)
+ throw ShareException("File Not Available");
+
+ string file;
+
+ // Check for tth root identifier
+ if(aFile.compare(0, 4, "TTH/") == 0) {
+ TTHValue v(aFile.substr(4));
+ HashFileIter i = tthIndex.find(&v);
+ if(i != tthIndex.end()) {
+ file = i->second->getADCPath();
+ } else {
+ throw ShareException("File Not Available");
+ }
+ } else if(aFile[0] != '/') {
+ throw ShareException("File Not Available");
+ } else {
+ file = aFile;
+ }
+
+ string::size_type i = file.find('/', 1);
+ if(i == string::npos)
+ throw ShareException("File Not Available");
+
+ RLock<> l(cs);
+ StringPairIter j = lookupVirtual(file.substr(1, i-1));
+ if(j == virtualMap.end()) {
+ throw ShareException("File Not Available");
+ }
+
+ file = file.substr(i + 1);
+ Directory::File::Iter it;
+ if(!checkFile(j->second, file, it)) {
+ throw ShareException("File Not Available");
+ }
+
+#ifdef _WIN32
+ for(i = 0; i < file.length(); ++i) {
+ if(file[i] == '/')
+ file[i] = '\\';
+ }
+#endif
+ return j->second + file;
+ }
+}
+
+/** @todo Fix for file list */
+AdcCommand ShareManager::getFileInfo(const string& aFile) throw(ShareException) {
+ if(aFile.compare(0, 4, "TTH/") != 0)
+ throw ShareException("File Not Available");
+
+ RLock<> l(cs);
+ TTHValue val(aFile.substr(4));
+ HashFileIter i = tthIndex.find(&val);
+ if(i == tthIndex.end()) {
+ throw ShareException("File Not Available");
+ }
+
+ Directory::File::Iter f = i->second;
+ AdcCommand cmd(AdcCommand::CMD_RES);
+ cmd.addParam("FN", f->getADCPath());
+ cmd.addParam("SI", Util::toString(f->getSize()));
+ cmd.addParam("TR", f->getTTH().toBase32());
+ return cmd;
+}
+
+StringPairIter ShareManager::findVirtual(const string& name) {
+ for(StringPairIter i = virtualMap.begin(); i != virtualMap. end(); ++i) {
+ if(Util::stricmp(name, i->second) == 0)
+ return i;
+ }
+ return virtualMap.end();
+}
+
+StringPairIter ShareManager::lookupVirtual(const string& name) {
+ for(StringPairIter i = virtualMap.begin(); i != virtualMap. end(); ++i) {
+ if(Util::stricmp(name, i->first) == 0)
+ return i;
+ }
+ return virtualMap.end();
+}
+
+bool ShareManager::checkFile(const string& dir, const string& aFile, Directory::File::Iter& it) {
+ Directory::MapIter mi = directories.find(dir);
+ if(mi == directories.end())
+ return false;
+ Directory* d = mi->second;
+
+ string::size_type i;
+ string::size_type j = 0;
+ while( (i = aFile.find('/', j)) != string::npos) {
+ mi = d->directories.find(aFile.substr(j, i-j));
+ j = i + 1;
+ if(mi == d->directories.end())
+ return false;
+ d = mi->second;
+ }
+
+ it = find_if(d->files.begin(), d->files.end(), Directory::File::StringComp(aFile.substr(j)));
+ if(it == d->files.end())
+ return false;
+
+ return true;
+}
+
+string ShareManager::validateVirtual(const string& aVirt) {
+ string tmp = aVirt;
+ string::size_type idx;
+
+ while( (idx = tmp.find_first_of("$|:\\/")) != string::npos) {
+ tmp[idx] = '_';
+ }
+ return tmp;
+}
+
+void ShareManager::load(SimpleXML* aXml) {
+ WLock<> l(cs);
+
+ if(aXml->findChild("Share")) {
+ aXml->stepIn();
+ while(aXml->findChild("Directory")) {
+ const string& virt = aXml->getChildAttrib("Virtual");
+ string d(aXml->getChildData()), newVirt;
+
+ if(d[d.length() - 1] != PATH_SEPARATOR)
+ d += PATH_SEPARATOR;
+ if(!virt.empty()) {
+ newVirt = virt;
+ if(newVirt[newVirt.length() - 1] == PATH_SEPARATOR) {
+ newVirt.erase(newVirt.length() -1, 1);
+ }
+ } else {
+ newVirt = Util::getLastDir(d);
+ }
+
+ // get rid of bad characters in virtual names
+ newVirt = validateVirtual(newVirt);
+
+ // add only unique directories
+ if(lookupVirtual(newVirt) == virtualMap.end()) {
+ Directory* dp = new Directory(newVirt);
+ directories[d] = dp;
+ virtualMap.push_back(make_pair(newVirt, d));
+ }
+ }
+ aXml->stepOut();
+ }
+}
+
+struct ShareLoader : public SimpleXMLReader::CallBack {
+ ShareLoader(ShareManager::Directory::Map& aDirs, StringPairList& aVirts) : dirs(aDirs), virts(aVirts), cur(NULL), depth(0) { }
+ virtual void startTag(const string& name, StringPairList& attribs, bool simple) {
+ if(name == "Directory") {
+ if(depth == 0) {
+ const string& name = getAttrib(attribs, "Name", 0);
+ for(StringPairIter i = virts.begin(); i != virts.end(); ++i) {
+ if(i->first == name) {
+ cur = dirs[i->second];
+ break;
+ }
+ }
+ } else if(cur != NULL) {
+ cur = new ShareManager::Directory(getAttrib(attribs, "Name", 0), cur);
+ cur->addType(SearchManager::TYPE_DIRECTORY); // needed since we match our own name in directory searches
+ cur->getParent()->directories[cur->getName()] = cur;
+ }
+
+ if(simple)
+ cur = cur->getParent();
+ else
+ depth++;
+ } else if(cur != NULL && name == "File") {
+ const string& fname = getAttrib(attribs, "Name", 0);
+ int64_t size = Util::toInt64(getAttrib(attribs, "Size", 1));
+ const string& root = getAttrib(attribs, "TTH", 2);
+ cur->files.insert(ShareManager::Directory::File(fname, size, cur, TTHValue(root)));
+ }
+ }
+ virtual void endTag(const string& name, const string&) {
+ if(name == "Directory") {
+ depth--;
+ if(cur) {
+ cur = cur->getParent();
+ }
+ }
+ }
+
+private:
+ ShareManager::Directory::Map& dirs;
+ StringPairList& virts;
+
+ ShareManager::Directory* cur;
+ size_t depth;
+};
+
+bool ShareManager::loadCache() {
+ try {
+ ShareLoader loader(directories, virtualMap);
+ string txt;
+ ::File ff(Util::getAppPath() + "files.xml.bz2", ::File::READ, ::File::OPEN);
+ FilteredInputStream<UnBZFilter, false> f(&ff);
+ const size_t BUF_SIZE = 64*1024;
+ char buf[BUF_SIZE];
+ size_t len;
+ for(;;) {
+ size_t n = BUF_SIZE;
+ len = f.read(buf, n);
+ txt.append(buf, len);
+ if(len < BUF_SIZE)
+ break;
+ }
+
+ SimpleXMLReader(&loader).fromXML(txt);
+
+ for(Directory::MapIter i = directories.begin(); i != directories.end(); ++i) {
+ addTree(i->second);
+ }
+
+ return true;
+ } catch(const Exception& e) {
+ dcdebug("%s\n", e.getError().c_str());
+ }
+ return false;
+}
+
+void ShareManager::save(SimpleXML* aXml) {
+ RLock<> l(cs);
+
+ aXml->addTag("Share");
+ aXml->stepIn();
+ for(StringPairIter i = virtualMap.begin(); i != virtualMap.end(); ++i) {
+ aXml->addTag("Directory", i->second);
+ aXml->addChildAttrib("Virtual", i->first);
+ }
+ aXml->stepOut();
+}
+
+void ShareManager::addDirectory(const string& aDirectory, const string& aName) throw(ShareException) {
+ if(aDirectory.empty() || aName.empty()) {
+ throw ShareException(STRING(NO_DIRECTORY_SPECIFIED));
+ }
+
+ if(Util::stricmp(SETTING(TEMP_DOWNLOAD_DIRECTORY), aDirectory) == 0) {
+ throw ShareException(STRING(DONT_SHARE_TEMP_DIRECTORY));
+ }
+
+ string d(aDirectory);
+
+ if(d[d.length() - 1] != PATH_SEPARATOR)
+ d += PATH_SEPARATOR;
+
+ string vName = validateVirtual(aName);
+
+ Directory* dp = NULL;
+ {
+ RLock<> l(cs);
+
+ for(Directory::MapIter i = directories.begin(); i != directories.end(); ++i) {
+ if(Util::strnicmp(d, i->first, i->first.length()) == 0) {
+ // Trying to share an already shared directory
+ throw ShareException(STRING(DIRECTORY_ALREADY_SHARED));
+ } else if(Util::strnicmp(d, i->first, d.length()) == 0) {
+ // Trying to share a parent directory
+ throw ShareException(STRING(REMOVE_ALL_SUBDIRECTORIES));
+ }
+ }
+
+ if(lookupVirtual(vName) != virtualMap.end()) {
+ throw ShareException(STRING(VIRTUAL_NAME_EXISTS));
+ }
+ }
+
+ dp = buildTree(d, NULL);
+ dp->setName(vName);
+
+ {
+ WLock<> l(cs);
+ addTree(dp);
+
+ directories[d] = dp;
+ virtualMap.push_back(make_pair(vName, d));
+ setDirty();
+ }
+}
+
+void ShareManager::removeDirectory(const string& aDirectory, bool duringRefresh) {
+ WLock<> l(cs);
+
+ string d(aDirectory);
+
+ if(d[d.length() - 1] != PATH_SEPARATOR)
+ d += PATH_SEPARATOR;
+
+ Directory::MapIter i = directories.find(d);
+ if(i != directories.end()) {
+ delete i->second;
+ directories.erase(i);
+ }
+
+ for(StringPairIter j = virtualMap.begin(); j != virtualMap.end(); ++j) {
+ if(Util::stricmp(j->second.c_str(), d.c_str()) == 0) {
+ virtualMap.erase(j);
+ break;
+ }
+ }
+
+ if(!duringRefresh)
+ HashManager::getInstance()->stopHashing(d);
+
+ setDirty();
+}
+
+void ShareManager::renameDirectory(const string& oName, const string& nName) throw(ShareException) {
+ StringPairIter i;
+ WLock<> l(cs);
+ //Find the virtual name
+ i = lookupVirtual(oName);
+ if (lookupVirtual(nName) != virtualMap.end()) {
+ throw ShareException(STRING(VIRTUAL_NAME_EXISTS));
+ } else {
+ // Valid newName, lets rename
+ i->first = nName;
+
+ //rename the directory, so it will be updated once
+ //a new list is generated.
+ if( directories.find(i->second) != directories.end() ) {
+ directories.find(i->second)->second->setName(nName);
+ }
+ }
+
+ //Do not call setDirty here since there might be more
+ //dirs that should be renamed, this is to avoid creating
+ //a new list during renaming.
+}
+
+int64_t ShareManager::getShareSize(const string& aDir) throw() {
+ RLock<> l(cs);
+ dcassert(aDir.size()>0);
+ Directory::MapIter i = directories.find(aDir);
+
+ if(i != directories.end()) {
+ return i->second->getSize();
+ }
+
+ return -1;
+}
+
+int64_t ShareManager::getShareSize() throw() {
+ RLock<> l(cs);
+ int64_t tmp = 0;
+ for(Directory::MapIter i = directories.begin(); i != directories.end(); ++i) {
+ tmp += i->second->getSize();
+ }
+ return tmp;
+}
+
+size_t ShareManager::getSharedFiles() throw() {
+ RLock<> l(cs);
+ size_t tmp = 0;
+ for(Directory::MapIter i = directories.begin(); i != directories.end(); ++i) {
+ tmp += i->second->countFiles();
+ }
+ return tmp;
+}
+
+
+string ShareManager::Directory::getADCPath() const throw() {
+ if(parent == NULL)
+ return '/' + name + '/';
+ return parent->getADCPath() + name + '/';
+}
+string ShareManager::Directory::getFullName() const throw() {
+ if(parent == NULL)
+ return getName() + '\\';
+ return parent->getFullName() + getName() + '\\';
+}
+
+void ShareManager::Directory::addType(u_int32_t type) throw() {
+ if(!hasType(type)) {
+ fileTypes |= (1 << type);
+ if(getParent() != NULL)
+ getParent()->addType(type);
+ }
+}
+
+class FileFindIter {
+#ifdef _WIN32
+public:
+ /** End iterator constructor */
+ FileFindIter() : handle(INVALID_HANDLE_VALUE) { }
+ /** Begin iterator constructor, path in utf-8 */
+ FileFindIter(const string& path) : handle(INVALID_HANDLE_VALUE) {
+ handle = ::FindFirstFile(Text::toT(path).c_str(), &data);
+ }
+
+ ~FileFindIter() {
+ if(handle != INVALID_HANDLE_VALUE) {
+ ::FindClose(handle);
+ }
+ }
+
+ FileFindIter& operator++() {
+ if(!::FindNextFile(handle, &data)) {
+ ::FindClose(handle);
+ handle = INVALID_HANDLE_VALUE;
+ }
+ return *this;
+ }
+ bool operator!=(const FileFindIter& rhs) const { return handle != rhs.handle; }
+
+ struct DirData : public WIN32_FIND_DATA {
+ string getFileName() {
+ return Text::fromT(cFileName);
+ }
+
+ bool isDirectory() {
+ return (dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) > 0;
+ }
+
+ bool isHidden() {
+ return ((dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) || (cFileName[0] == L'.'));
+ }
+
+ int64_t getSize() {
+ return (int64_t)nFileSizeLow | ((int64_t)nFileSizeHigh)<<32;
+ }
+
+ u_int32_t getLastWriteTime() {
+ return File::convertTime(&ftLastWriteTime);
+ }
+ };
+
+
+private:
+ HANDLE handle;
+#else
+// This code has been cleaned up/fixed a little.
+public:
+ FileFindIter() {
+ dir = NULL;
+ data.ent = NULL;
+ }
+
+ ~FileFindIter() {
+ if (dir) closedir(dir);
+ }
+
+ FileFindIter(const string& name) {
+ dir = opendir(name.c_str());
+ if (!dir)
+ return;
+ data.base = name;
+ data.ent = readdir(dir);
+ if (!data.ent) {
+ closedir(dir);
+ dir = NULL;
+ }
+ }
+
+ FileFindIter& operator++() {
+ if (!dir)
+ return *this;
+ data.ent = readdir(dir);
+ if (!data.ent) {
+ closedir(dir);
+ dir = NULL;
+ }
+ return *this;
+ }
+
+ // good enough to to say if it's null
+ bool operator !=(const FileFindIter& rhs) const {
+ return dir != rhs.dir;
+ }
+
+ struct DirData {
+ DirData() : ent(NULL) {}
+ string getFileName() {
+ if (!ent) return Util::emptyString;
+ return string(ent->d_name);
+ }
+ bool isDirectory() {
+ struct stat inode;
+ if (!ent) return false;
+ if (stat((base + PATH_SEPARATOR + ent->d_name).c_str(), &inode) == -1) return false;
+ return S_ISDIR(inode.st_mode);
+ }
+ bool isHidden() {
+ if (!ent) return false;
+ return ent->d_name[0] == '.';
+ }
+ int64_t getSize() {
+ struct stat inode;
+ if (!ent) return false;
+ if (stat((base + PATH_SEPARATOR + ent->d_name).c_str(), &inode) == -1) return 0;
+ return inode.st_size;
+ }
+ u_int32_t getLastWriteTime() {
+ struct stat inode;
+ if (!ent) return false;
+ if (stat((base + PATH_SEPARATOR + ent->d_name).c_str(), &inode) == -1) return 0;
+ return inode.st_mtime;
+ }
+ struct dirent* ent;
+ string base;
+ };
+private:
+ DIR* dir;
+#endif
+
+public:
+
+ DirData& operator*() { return data; }
+ DirData* operator->() { return &data; }
+
+private:
+ DirData data;
+
+};
+
+ShareManager::Directory* ShareManager::buildTree(const string& aName, Directory* aParent) {
+ Directory* dir = new Directory(Util::getLastDir(aName), aParent);
+ dir->addType(SearchManager::TYPE_DIRECTORY); // needed since we match our own name in directory searches
+
+ Directory::File::Iter lastFileIter = dir->files.begin();
+
+ FileFindIter end;
+#ifdef _WIN32
+ for(FileFindIter i(aName + "*"); i != end; ++i) {
+#else
+ //the fileiter just searches directorys for now, not sure if more
+ //will be needed later
+ //for(FileFindIter i(aName + "*"); i != end; ++i) {
+ for(FileFindIter i(aName); i != end; ++i) {
+#endif
+ string name = i->getFileName();
+
+ if(name == "." || name == "..")
+ continue;
+ if(name.find('$') != string::npos) {
+ LogManager::getInstance()->message(STRING(FORBIDDEN_DOLLAR_FILE) + name + " (" + STRING(SIZE) + ": " + Util::toString(File::getSize(name)) + " " + STRING(B) + ") (" + STRING(DIRECTORY) + ": \"" + aName + "\")");
+ continue;
+ }
+ if(!BOOLSETTING(SHARE_HIDDEN) && i->isHidden() )
+ continue;
+
+ if(i->isDirectory()) {
+ string newName = aName + name + PATH_SEPARATOR;
+ if(Util::stricmp(newName + PATH_SEPARATOR, SETTING(TEMP_DOWNLOAD_DIRECTORY)) != 0) {
+ dir->directories[name] = buildTree(newName, dir);
+ }
+ } else {
+ // Not a directory, assume it's a file...make sure we're not sharing the settings file...
+ if( (Util::stricmp(name.c_str(), "DCPlusPlus.xml") != 0) &&
+ (Util::stricmp(name.c_str(), "Favorites.xml") != 0)) {
+
+ int64_t size = i->getSize();
+ string fileName = aName + name;
+ try {
+ if(HashManager::getInstance()->checkTTH(fileName, size, i->getLastWriteTime()))
+ lastFileIter = dir->files.insert(lastFileIter, Directory::File(name, size, dir, HashManager::getInstance()->getTTH(fileName, size)));
+ } catch(const HashException&) {
+ }
+ }
+ }
+ }
+
+ return dir;
+}
+
+void ShareManager::addTree(Directory* dir) {
+ bloom.add(Text::toLower(dir->getName()));
+
+ for(Directory::MapIter i = dir->directories.begin(); i != dir->directories.end(); ++i) {
+ Directory* d = i->second;
+ addTree(d);
+ }
+
+ for(Directory::File::Iter i = dir->files.begin(); i != dir->files.end(); ) {
+ addFile(dir, i++);
+ }
+}
+
+void ShareManager::addFile(Directory* dir, Directory::File::Iter i) {
+ const Directory::File& f = *i;
+
+ HashFileIter j = tthIndex.find(const_cast<TTHValue*>(&f.getTTH()));
+ if(j == tthIndex.end()) {
+ dir->size+=f.getSize();
+ } else {
+ if(!SETTING(LIST_DUPES)) {
+ LogManager::getInstance()->message(STRING(DUPLICATE_FILE_NOT_SHARED) + dir->getFullName() + f.getName() + " (" + STRING(SIZE) + ": " + Util::toString(f.getSize()) + " " + STRING(B) + ") " + STRING(DUPLICATE_MATCH) + j->second->getParent()->getFullName() + j->second->getName() );
+ dir->files.erase(i);
+ return;
+ }
+ }
+
+ dir->addType(getType(f.getName()));
+
+ tthIndex.insert(make_pair(const_cast<TTHValue*>(&f.getTTH()), i));
+ bloom.add(Text::toLower(f.getName()));
+}
+
+void ShareManager::removeTTH(const TTHValue& tth, const Directory::File::Iter& iter) {
+ pair<HashFileIter, HashFileIter> range = tthIndex.equal_range(const_cast<TTHValue*>(&tth));
+ for(HashFileIter j = range.first; j != range.second; ++j) {
+ if(j->second == iter) {
+ tthIndex.erase(j);
+ break;
+ }
+ }
+}
+
+void ShareManager::refresh(bool dirs /* = false */, bool aUpdate /* = true */, bool block /* = false */) throw(ShareException) {
+ update = aUpdate;
+ refreshDirs = dirs;
+ join();
+ bool cached = false;
+ if(initial) {
+ cached = loadCache();
+ initial = false;
+ }
+ start();
+ if(block && !cached) {
+ join();
+ } else {
+ setThreadPriority(Thread::LOW);
+ }
+}
+
+int ShareManager::run() {
+ LogManager::getInstance()->message(STRING(FILE_LIST_REFRESH_INITIATED));
+ {
+ if(refreshDirs) {
+ lastFullUpdate = GET_TICK();
+ StringPairList dirs;
+ Directory::Map newDirs;
+ {
+ RLock<> l(cs);
+ dirs = virtualMap;
+ }
+
+ for(StringPairIter i = dirs.begin(); i != dirs.end(); ++i) {
+ Directory* dp = buildTree(i->second, NULL);
+ dp->setName(i->first);
+ newDirs.insert(make_pair(i->second, dp));
+ }
+
+ {
+ WLock<> l(cs);
+ StringPairList dirs = virtualMap;
+ for(StringPairIter i = dirs.begin(); i != dirs.end(); ++i) {
+ removeDirectory(i->second, true);
+ }
+ bloom.clear();
+
+ virtualMap = dirs;
+
+ for(Directory::MapIter i = newDirs.begin(); i != newDirs.end(); ++i) {
+ addTree(i->second);
+ directories.insert(*i);
+ }
+ }
+ refreshDirs = false;
+ }
+ }
+
+ LogManager::getInstance()->message(STRING(FILE_LIST_REFRESH_FINISHED));
+ if(update) {
+ ClientManager::getInstance()->infoUpdated();
+ }
+ return 0;
+}
+
+void ShareManager::generateXmlList() {
+ Lock l(listGenLock);
+ if(xmlDirty && (lastXmlUpdate + 15 * 60 * 1000 < GET_TICK() || lastXmlUpdate < lastFullUpdate)) {
+ listN++;
+
+ try {
+ string tmp2;
+ string indent;
+
+ string newXmlName = Util::getAppPath() + "files" + Util::toString(listN) + ".xml.bz2";
+ {
+ FilteredOutputStream<BZFilter, true> newXmlFile(new File(newXmlName, File::WRITE, File::TRUNCATE | File::CREATE));
+ newXmlFile.write(SimpleXML::utf8Header);
+ newXmlFile.write("<FileListing Version=\"1\" CID=\"" + SETTING(CLIENT_ID) + "\" Base=\"/\" Generator=\"" APPNAME " " VERSIONSTRING "\">\r\n");
+ for(Directory::MapIter i = directories.begin(); i != directories.end(); ++i) {
+ i->second->toXml(newXmlFile, indent, tmp2, true);
+ }
+ newXmlFile.write("</FileListing>");
+ newXmlFile.flush();
+ }
+
+ if(xFile != NULL) {
+ delete xFile;
+ xFile = NULL;
+ File::deleteFile(getBZXmlFile());
+ }
+ try {
+ File::renameFile(newXmlName, Util::getAppPath() + "files.xml.bz2");
+ newXmlName = Util::getAppPath() + "files.xml.bz2";
+ } catch(const FileException&) {
+ // Ignore, this is for caching only...
+ }
+ xFile = new File(newXmlName, File::READ, File::OPEN);
+ setBZXmlFile(newXmlName);
+ bzXmlListLen = File::getSize(newXmlName);
+ } catch(const Exception&) {
+ // No new file lists...
+ }
+
+ xmlDirty = false;
+ lastXmlUpdate = GET_TICK();
+ }
+}
+void ShareManager::generateNmdcList() {
+ Lock l(listGenLock);
+ if(nmdcDirty && (lastNmdcUpdate + 15 * 60 * 1000 < GET_TICK() || lastNmdcUpdate < lastFullUpdate)) {
+ listN++;
+
+ try {
+ string tmp;
+ string tmp2;
+ string indent;
+
+ for(Directory::MapIter i = directories.begin(); i != directories.end(); ++i) {
+ i->second->toNmdc(tmp, indent, tmp2);
+ }
+
+ string newName = Util::getAppPath() + "MyList" + Util::toString(listN) + ".DcLst";
+ tmp2.clear();
+ CryptoManager::getInstance()->encodeHuffman(tmp, tmp2);
+ File(newName, File::WRITE, File::CREATE | File::TRUNCATE).write(tmp2);
+
+ if(lFile != NULL) {
+ delete lFile;
+ lFile = NULL;
+ File::deleteFile(getListFile());
+ }
+ try {
+ File::renameFile(newName, Util::getAppPath() + "MyList.DcLst");
+ newName = Util::getAppPath() + "MyList.DcLst";
+ } catch(const FileException&) {
+ }
+ lFile = new File(newName, File::READ, File::OPEN);
+ setListFile(newName);
+ listLen = File::getSize(newName);
+ } catch(const Exception&) {
+ // No new file lists...
+ }
+
+ nmdcDirty = false;
+ lastNmdcUpdate = GET_TICK();
+ }
+}
+
+MemoryInputStream* ShareManager::generatePartialList(const string& dir, bool recurse) {
+ if(dir[0] != '/' || dir[dir.size()-1] != '/')
+ return NULL;
+
+ string xml = SimpleXML::utf8Header;
+ string tmp;
+ xml += "<FileListing Version=\"1\" CID=\"" + SETTING(CLIENT_ID) + "\" Base=\"" + SimpleXML::escape(dir, tmp, false) + "\" Generator=\"" APPNAME " " VERSIONSTRING "\">\r\n";
+ StringOutputStream sos(xml);
+ string indent = "\t";
+
+ RLock<> l(cs);
+ if(dir == "/") {
+ for(ShareManager::Directory::MapIter i = directories.begin(); i != directories.end(); ++i) {
+ tmp.clear();
+ i->second->toXml(sos, indent, tmp, recurse);
+ }
+ } else {
+ string::size_type i = 1, j = 1;
+ ShareManager::Directory::MapIter it = directories.end();
+ bool first = true;
+ while( (i = dir.find('/', j)) != string::npos) {
+ if(i == j) {
+ j++;
+ continue;
+ }
+
+ if(first) {
+ first = false;
+ StringPairIter k = lookupVirtual(dir.substr(j, i-j));
+ if(k == virtualMap.end())
+ return NULL;
+ it = directories.find(k->second);
+ if(it == directories.end())
+ return NULL;
+ } else {
+ ShareManager::Directory::MapIter it2 = it->second->directories.find(dir.substr(j, i-j));
+ if(it2 == it->second->directories.end()) {
+ return NULL;
+ }
+ it = it2;
+ }
+ j = i + 1;
+ }
+ for(ShareManager::Directory::MapIter it2 = it->second->directories.begin(); it2 != it->second->directories.end(); ++it2) {
+ it2->second->toXml(sos, indent, tmp, recurse);
+ }
+ it->second->filesToXml(sos, indent, tmp);
+ }
+
+ xml += "</FileListing>";
+ return new MemoryInputStream(xml);
+}
+
+bool ShareManager::getTTH(const string& aFile, TTHValue& tth) throw() {
+ if(aFile.length() < 3 || aFile[0] != '/')
+ return false;
+
+ string::size_type i = aFile.find('/', 1);
+ if(i == string::npos)
+ return false;
+
+ RLock<> l(cs);
+ StringPairIter j = lookupVirtual(aFile.substr(1, i-1));
+ if(j == virtualMap.end()) {
+ return false;
+ }
+
+ Directory::File::Iter it;
+ if(!checkFile(j->second, aFile.substr(i + 1), it))
+ return false;
+
+ tth = it->getTTH();
+ return true;
+}
+
+MemoryInputStream* ShareManager::getTree(const string& aFile) {
+ TigerTree tree;
+ if(aFile.compare(0, 4, "TTH/") == 0) {
+ if(!HashManager::getInstance()->getTree(TTHValue(aFile.substr(4)), tree))
+ return NULL;
+ } else {
+ try {
+ TTHValue tth;
+ if(getTTH(aFile, tth))
+ HashManager::getInstance()->getTree(tth, tree);
+ } catch(const Exception&) {
+ return NULL;
+ }
+ }
+
+ vector<u_int8_t> buf = tree.getLeafData();
+ return new MemoryInputStream(&buf[0], buf.size());
+}
+
+static const string& escaper(const string& n, string& tmp) {
+ if(SimpleXML::needsEscape(n, false, false)) {
+ tmp.clear();
+ tmp.append(n);
+ return SimpleXML::escape(tmp, false, false);
+ }
+ return n;
+}
+
+#define LITERAL(n) n, sizeof(n)-1
+void ShareManager::Directory::toNmdc(string& nmdc, string& indent, string& tmp2) {
+ tmp2.clear();
+ nmdc.append(indent);
+ nmdc.append(Text::utf8ToAcp(name, tmp2));
+ nmdc.append(LITERAL("\r\n"));
+
+ indent += '\t';
+ for(MapIter i = directories.begin(); i != directories.end(); ++i) {
+ i->second->toNmdc(nmdc, indent, tmp2);
+ }
+
+ Directory::File::Iter j = files.begin();
+ for(Directory::File::Iter i = files.begin(); i != files.end(); ++i) {
+ const Directory::File& f = *i;
+ nmdc.append(indent);
+ tmp2.clear();
+ nmdc.append(Text::utf8ToAcp(f.getName(), tmp2));
+ nmdc.append(LITERAL("|"));
+ nmdc.append(Util::toString(f.getSize()));
+ nmdc.append(LITERAL("\r\n"));
+ }
+ indent.erase(indent.length()-1);
+}
+
+void ShareManager::Directory::toXml(OutputStream& xmlFile, string& indent, string& tmp2, bool fullList) {
+ xmlFile.write(indent);
+ xmlFile.write(LITERAL("<Directory Name=\""));
+ xmlFile.write(escaper(name, tmp2));
+
+ if(fullList) {
+ xmlFile.write(LITERAL("\">\r\n"));
+
+ indent += '\t';
+ for(MapIter i = directories.begin(); i != directories.end(); ++i) {
+ i->second->toXml(xmlFile, indent, tmp2, fullList);
+ }
+
+ filesToXml(xmlFile, indent, tmp2);
+
+ indent.erase(indent.length()-1);
+ xmlFile.write(indent);
+ xmlFile.write(LITERAL("</Directory>\r\n"));
+ } else {
+ if(directories.empty() && files.empty()) {
+ xmlFile.write(LITERAL("\" />\r\n"));
+ } else {
+ xmlFile.write(LITERAL("\" Incomplete=\"1\" />\r\n"));
+ }
+ }
+}
+
+void ShareManager::Directory::filesToXml(OutputStream& xmlFile, string& indent, string& tmp2) {
+ for(Directory::File::Iter i = files.begin(); i != files.end(); ++i) {
+ const Directory::File& f = *i;
+
+ xmlFile.write(indent);
+ xmlFile.write(LITERAL("<File Name=\""));
+ xmlFile.write(escaper(f.getName(), tmp2));
+ xmlFile.write(LITERAL("\" Size=\""));
+ xmlFile.write(Util::toString(f.getSize()));
+ xmlFile.write(LITERAL("\" TTH=\""));
+ tmp2.clear();
+ xmlFile.write(f.getTTH().toBase32(tmp2));
+ xmlFile.write(LITERAL("\"/>\r\n"));
+ }
+}
+
+// These ones we can look up as ints (4 bytes...)...
+
+static const char* typeAudio[] = { ".mp3", ".mp2", ".mid", ".wav", ".ogg", ".wma" };
+static const char* typeCompressed[] = { ".zip", ".ace", ".rar" };
+static const char* typeDocument[] = { ".htm", ".doc", ".txt", ".nfo" };
+static const char* typeExecutable[] = { ".exe" };
+static const char* typePicture[] = { ".jpg", ".gif", ".png", ".eps", ".img", ".pct", ".psp", ".pic", ".tif", ".rle", ".bmp", ".pcx" };
+static const char* typeVideo[] = { ".mpg", ".mov", ".asf", ".avi", ".pxp", ".wmv", ".ogm", ".mkv" };
+
+static const string type2Audio[] = { ".au", ".aiff", ".flac" };
+static const string type2Picture[] = { ".ai", ".ps", ".pict" };
+static const string type2Video[] = { ".rm", ".divx", ".mpeg" };
+
+#define IS_TYPE(x) ( type == (*((u_int32_t*)x)) )
+#define IS_TYPE2(x) (Util::stricmp(aString.c_str() + aString.length() - x.length(), x.c_str()) == 0)
+
+static bool checkType(const string& aString, int aType) {
+ if(aType == SearchManager::TYPE_ANY)
+ return true;
+
+ if(aString.length() < 5)
+ return false;
+
+ const char* c = aString.c_str() + aString.length() - 3;
+ if(!Text::isAscii(c))
+ return false;
+
+ u_int32_t type = '.' | (Text::asciiToLower(c[0]) << 8) | (Text::asciiToLower(c[1]) << 16) | (((u_int32_t)Text::asciiToLower(c[2])) << 24);
+
+ switch(aType) {
+ case SearchManager::TYPE_AUDIO:
+ {
+ for(size_t i = 0; i < (sizeof(typeAudio) / sizeof(typeAudio[0])); i++) {
+ if(IS_TYPE(typeAudio[i])) {
+ return true;
+ }
+ }
+ if( IS_TYPE2(type2Audio[0]) || IS_TYPE2(type2Audio[1]) ) {
+ return true;
+ }
+ }
+ break;
+ case SearchManager::TYPE_COMPRESSED:
+ if( IS_TYPE(typeCompressed[0]) || IS_TYPE(typeCompressed[1]) || IS_TYPE(typeCompressed[2]) ) {
+ return true;
+ }
+ break;
+ case SearchManager::TYPE_DOCUMENT:
+ if( IS_TYPE(typeDocument[0]) || IS_TYPE(typeDocument[1]) ||
+ IS_TYPE(typeDocument[2]) || IS_TYPE(typeDocument[3]) ) {
+ return true;
+ }
+ break;
+ case SearchManager::TYPE_EXECUTABLE:
+ if(IS_TYPE(typeExecutable[0]) ) {
+ return true;
+ }
+ break;
+ case SearchManager::TYPE_PICTURE:
+ {
+ for(size_t i = 0; i < (sizeof(typePicture) / sizeof(typePicture[0])); i++) {
+ if(IS_TYPE(typePicture[i])) {
+ return true;
+ }
+ }
+ if( IS_TYPE2(type2Picture[0]) || IS_TYPE2(type2Picture[1]) || IS_TYPE2(type2Picture[2]) ) {
+ return true;
+ }
+ }
+ break;
+ case SearchManager::TYPE_VIDEO:
+ {
+ for(size_t i = 0; i < (sizeof(typeVideo) / sizeof(typeVideo[0])); i++) {
+ if(IS_TYPE(typeVideo[i])) {
+ return true;
+ }
+ }
+ if( IS_TYPE2(type2Video[0]) || IS_TYPE2(type2Video[1]) || IS_TYPE2(type2Video[2]) ) {
+ return true;
+ }
+ }
+ break;
+ default:
+ dcasserta(0);
+ break;
+ }
+ return false;
+}
+
+SearchManager::TypeModes ShareManager::getType(const string& aFileName) {
+ if(aFileName[aFileName.length() - 1] == PATH_SEPARATOR) {
+ return SearchManager::TYPE_DIRECTORY;
+ }
+
+ if(checkType(aFileName, SearchManager::TYPE_VIDEO))
+ return SearchManager::TYPE_VIDEO;
+ else if(checkType(aFileName, SearchManager::TYPE_AUDIO))
+ return SearchManager::TYPE_AUDIO;
+ else if(checkType(aFileName, SearchManager::TYPE_COMPRESSED))
+ return SearchManager::TYPE_COMPRESSED;
+ else if(checkType(aFileName, SearchManager::TYPE_DOCUMENT))
+ return SearchManager::TYPE_DOCUMENT;
+ else if(checkType(aFileName, SearchManager::TYPE_EXECUTABLE))
+ return SearchManager::TYPE_EXECUTABLE;
+ else if(checkType(aFileName, SearchManager::TYPE_PICTURE))
+ return SearchManager::TYPE_PICTURE;
+
+ return SearchManager::TYPE_ANY;
+}
+
+/**
+ * Alright, the main point here is that when searching, a search string is most often found in
+ * the filename, not directory name, so we want to make that case faster. Also, we want to
+ * avoid changing StringLists unless we absolutely have to --> this should only be done if a string
+ * has been matched in the directory name. This new stringlist should also be used in all descendants,
+ * but not the parents...
+ */
+void ShareManager::Directory::search(SearchResult::List& aResults, StringSearch::List& aStrings, int aSearchType, int64_t aSize, int aFileType, Client* aClient, StringList::size_type maxResults) throw() {
+ // Skip everything if there's nothing to find here (doh! =)
+ if(!hasType(aFileType))
+ return;
+
+ StringSearch::List* cur = &aStrings;
+ auto_ptr<StringSearch::List> newStr;
+
+ // Find any matches in the directory name
+ for(StringSearch::Iter k = aStrings.begin(); k != aStrings.end(); ++k) {
+ if(k->match(name)) {
+ if(!newStr.get()) {
+ newStr = auto_ptr<StringSearch::List>(new StringSearch::List(aStrings));
+ }
+ dcassert(find(newStr->begin(), newStr->end(), *k) != newStr->end());
+ newStr->erase(find(newStr->begin(), newStr->end(), *k));
+ }
+ }
+
+ if(newStr.get() != 0) {
+ cur = newStr.get();
+ }
+
+ bool sizeOk = (aSearchType != SearchManager::SIZE_ATLEAST) || (aSize == 0);
+ if( (cur->empty()) &&
+ (((aFileType == SearchManager::TYPE_ANY) && sizeOk) || (aFileType == SearchManager::TYPE_DIRECTORY)) ) {
+ // We satisfied all the search words! Add the directory...
+ SearchResult* sr = new SearchResult(aClient, SearchResult::TYPE_DIRECTORY,
+ 0, getFullName(), NULL, true);
+ aResults.push_back(sr);
+ ShareManager::getInstance()->setHits(ShareManager::getInstance()->getHits()+1);
+ }
+
+ if(aFileType != SearchManager::TYPE_DIRECTORY) {
+ for(File::Iter i = files.begin(); i != files.end(); ++i) {
+
+ if(aSearchType == SearchManager::SIZE_ATLEAST && aSize > i->getSize()) {
+ continue;
+ } else if(aSearchType == SearchManager::SIZE_ATMOST && aSize < i->getSize()) {
+ continue;
+ }
+ StringSearch::Iter j = cur->begin();
+ for(; j != cur->end() && j->match(i->getName()); ++j)
+ ; // Empty
+
+ if(j != cur->end())
+ continue;
+
+ // Check file type...
+ if(checkType(i->getName(), aFileType)) {
+
+ SearchResult* sr = new SearchResult(aClient, SearchResult::TYPE_FILE,
+ i->getSize(), getFullName() + i->getName(), &i->getTTH(), true);
+ aResults.push_back(sr);
+ ShareManager::getInstance()->setHits(ShareManager::getInstance()->getHits()+1);
+ if(aResults.size() >= maxResults) {
+ break;
+ }
+ }
+ }
+ }
+
+ for(Directory::MapIter l = directories.begin(); (l != directories.end()) && (aResults.size() < maxResults); ++l) {
+ l->second->search(aResults, *cur, aSearchType, aSize, aFileType, aClient, maxResults);
+ }
+}
+
+void ShareManager::search(SearchResult::List& results, const string& aString, int aSearchType, int64_t aSize, int aFileType, Client* aClient, StringList::size_type maxResults) {
+ RLock<> l(cs);
+ if(aFileType == SearchManager::TYPE_TTH) {
+ if(aString.compare(0, 4, "TTH:") == 0) {
+ TTHValue tth(aString.substr(4));
+ HashFileIter i = tthIndex.find(&tth);
+ if(i != tthIndex.end()) {
+ SearchResult* sr = new SearchResult(aClient, SearchResult::TYPE_FILE,
+ i->second->getSize(), i->second->getParent()->getFullName() + i->second->getName(),
+ &i->second->getTTH(), true);
+
+ results.push_back(sr);
+ ShareManager::getInstance()->setHits(ShareManager::getInstance()->getHits()+1);
+ }
+ }
+ return;
+ }
+ StringTokenizer<string> t(Text::toLower(aString), '$');
+ StringList& sl = t.getTokens();
+ if(!bloom.match(sl))
+ return;
+
+ StringSearch::List ssl;
+ for(StringList::iterator i = sl.begin(); i != sl.end(); ++i) {
+ if(!i->empty()) {
+ ssl.push_back(StringSearch(*i));
+ }
+ }
+ if(ssl.empty())
+ return;
+
+ for(Directory::MapIter j = directories.begin(); (j != directories.end()) && (results.size() < maxResults); ++j) {
+ j->second->search(results, ssl, aSearchType, aSize, aFileType, aClient, maxResults);
+ }
+}
+
+namespace {
+ inline u_int16_t toCode(char a, char b) { return (u_int16_t)a | ((u_int16_t)b)<<8; }
+}
+
+ShareManager::AdcSearch::AdcSearch(const StringList& params) : include(&includeX), gt(0),
+ lt(numeric_limits<int64_t>::max()), hasRoot(false), isDirectory(false)
+{
+ for(StringIterC i = params.begin(); i != params.end(); ++i) {
+ const string& p = *i;
+ if(p.length() <= 2)
+ continue;
+
+ u_int16_t cmd = toCode(p[0], p[1]);
+ if(toCode('T', 'R') == cmd) {
+ hasRoot = true;
+ root = TTHValue(p.substr(2));
+ return;
+ } else if(toCode('+', '+') == cmd) {
+ includeX.push_back(StringSearch(p.substr(2)));
+ } else if(toCode('-', '-') == cmd) {
+ exclude.push_back(StringSearch(p.substr(2)));
+ } else if(toCode('E', 'X') == cmd) {
+ ext.push_back(p.substr(2));
+ } else if(toCode('>', '=') == cmd) {
+ gt = Util::toInt64(p.substr(2));
+ } else if(toCode('<', '=') == cmd) {
+ lt = Util::toInt64(p.substr(2));
+ } else if(toCode('=', '=') == cmd) {
+ lt = gt = Util::toInt64(p.substr(2));
+ } else if(toCode('D', 'O') == cmd) {
+ isDirectory = (p[2] != '0');
+ }
+ }
+}
+
+void ShareManager::Directory::search(SearchResult::List& aResults, AdcSearch& aStrings, StringList::size_type maxResults) throw() {
+ StringSearch::List* cur = aStrings.include;
+ StringSearch::List* old = aStrings.include;
+
+ auto_ptr<StringSearch::List> newStr;
+
+ // Find any matches in the directory name
+ for(StringSearch::Iter k = cur->begin(); k != cur->end(); ++k) {
+ if(k->match(name) && !aStrings.isExcluded(name)) {
+ if(!newStr.get()) {
+ newStr = auto_ptr<StringSearch::List>(new StringSearch::List(*cur));
+ }
+ dcassert(find(newStr->begin(), newStr->end(), *k) != newStr->end());
+ newStr->erase(find(newStr->begin(), newStr->end(), *k));
+ }
+ }
+
+ if(newStr.get() != 0) {
+ cur = newStr.get();
+ }
+
+ bool sizeOk = (aStrings.gt == 0);
+ if( cur->empty() && aStrings.ext.empty() && sizeOk ) {
+ // We satisfied all the search words! Add the directory...
+ SearchResult* sr = new SearchResult(SearchResult::TYPE_DIRECTORY,
+ 0, getFullName(), NULL);
+ aResults.push_back(sr);
+ ShareManager::getInstance()->setHits(ShareManager::getInstance()->getHits()+1);
+ }
+
+ if(!aStrings.isDirectory) {
+ for(File::Iter i = files.begin(); i != files.end(); ++i) {
+
+ if(!(i->getSize() >= aStrings.gt)) {
+ continue;
+ } else if(!(i->getSize() <= aStrings.lt)) {
+ continue;
+ }
+
+ if(aStrings.isExcluded(i->getName()))
+ continue;
+
+ StringSearch::Iter j = cur->begin();
+ for(; j != cur->end() && j->match(i->getName()); ++j)
+ ; // Empty
+
+ if(j != cur->end())
+ continue;
+
+ // Check file type...
+ if(aStrings.hasExt(i->getName())) {
+
+ SearchResult* sr = new SearchResult(SearchResult::TYPE_FILE,
+ i->getSize(), getFullName() + i->getName(), &i->getTTH());
+ aResults.push_back(sr);
+ ShareManager::getInstance()->addHits(1);
+ if(aResults.size() >= maxResults) {
+ return;
+ }
+ }
+ }
+ }
+
+ for(Directory::MapIter l = directories.begin(); (l != directories.end()) && (aResults.size() < maxResults); ++l) {
+ l->second->search(aResults, aStrings, maxResults);
+ }
+ aStrings.include = old;
+}
+
+void ShareManager::search(SearchResult::List& results, const StringList& params, StringList::size_type maxResults) {
+ AdcSearch srch(params);
+
+ RLock<> l(cs);
+
+ if(srch.hasRoot) {
+ HashFileIter i = tthIndex.find(&srch.root);
+ if(i != tthIndex.end()) {
+ SearchResult* sr = new SearchResult(SearchResult::TYPE_FILE,
+ i->second->getSize(), i->second->getParent()->getFullName() + i->second->getName(),
+ &i->second->getTTH());
+ results.push_back(sr);
+ ShareManager::getInstance()->addHits(1);
+ }
+ return;
+ }
+
+ for(StringSearch::Iter i = srch.includeX.begin(); i != srch.includeX.end(); ++i) {
+ if(!bloom.match(i->getPattern()))
+ return;
+ }
+
+ for(Directory::MapIter j = directories.begin(); (j != directories.end()) && (results.size() < maxResults); ++j) {
+ j->second->search(results, srch, maxResults);
+ }
+}
+
+ShareManager::Directory* ShareManager::getDirectory(const string& fname) {
+ for(Directory::MapIter mi = directories.begin(); mi != directories.end(); ++mi) {
+ if(Util::strnicmp(fname, mi->first, mi->first.length()) == 0) {
+ Directory* d = mi->second;
+
+ string::size_type i;
+ string::size_type j = mi->first.length();
+ while( (i = fname.find(PATH_SEPARATOR, j)) != string::npos) {
+ mi = d->directories.find(fname.substr(j, i-j));
+ j = i + 1;
+ if(mi == d->directories.end())
+ return NULL;
+ d = mi->second;
+ }
+ return d;
+ }
+ }
+ return NULL;
+}
+
+void ShareManager::on(DownloadManagerListener::Complete, Download* d) throw() {
+ if(BOOLSETTING(ADD_FINISHED_INSTANTLY)) {
+ // Check if finished download is supposed to be shared
+ WLock<> l(cs);
+ const string& n = d->getTarget();
+ for(Directory::MapIter i = directories.begin(); i != directories.end(); i++) {
+ if(Util::strnicmp(i->first, n, i->first.size()) == 0 && n[i->first.size()] == PATH_SEPARATOR) {
+ string s = n.substr(i->first.size()+1);
+ try {
+ // Schedule for hashing, it'll be added automatically later on...
+ HashManager::getInstance()->checkTTH(n, d->getSize(), 0);
+ } catch(const Exception&) {
+ // Not a vital feature...
+ }
+ break;
+ }
+ }
+ }
+}
+
+void ShareManager::on(HashManagerListener::TTHDone, const string& fname, const TTHValue& root) throw() {
+ WLock<> l(cs);
+ Directory* d = getDirectory(fname);
+ if(d != NULL) {
+ Directory::File::Iter i = d->findFile(Util::getFileName(fname));
+ if(i != d->files.end()) {
+ if(root != i->getTTH())
+ removeTTH(i->getTTH(), i);
+ // Get rid of false constness...
+ Directory::File* f = const_cast<Directory::File*>(&(*i));
+ f->setTTH(root);
+ tthIndex.insert(make_pair(const_cast<TTHValue*>(&f->getTTH()), i));
+ } else {
+ string name = Util::getFileName(fname);
+ int64_t size = File::getSize(fname);
+ Directory::File::Iter it = d->files.insert(Directory::File(name, size, d, root)).first;
+ addFile(d, it);
+ }
+ setDirty();
+ }
+}
+
+void ShareManager::on(TimerManagerListener::Minute, u_int32_t tick) throw() {
+ if(BOOLSETTING(AUTO_UPDATE_LIST)) {
+ if(lastFullUpdate + 60 * 60 * 1000 < tick) {
+ try {
+ refresh(true, true);
+ } catch(const ShareException&) {
+ }
+ }
+ }
+}
+
+/**
+ * @file
+ * $Id: ShareManager.cpp,v 1.2 2005/08/21 14:03:43 olof Exp $
+ */
+
diff --git a/dcpp/ShareManager.h b/dcpp/ShareManager.h
new file mode 100644
index 0000000..82150b2
--- /dev/null
+++ b/dcpp/ShareManager.h
@@ -0,0 +1,327 @@
+/*
+ * Copyright (C) 2001-2005 Jacek Sieka, arnetheduck on gmail point 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 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.
+ */
+
+#if !defined(AFX_SHAREMANAGER_H__6CD5D87C_D13F_46E2_8C1E_5F116107C118__INCLUDED_)
+#define AFX_SHAREMANAGER_H__6CD5D87C_D13F_46E2_8C1E_5F116107C118__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+#include "TimerManager.h"
+#include "SearchManager.h"
+#include "SettingsManager.h"
+#include "HashManager.h"
+#include "DownloadManager.h"
+
+#include "Exception.h"
+#include "CriticalSection.h"
+#include "StringSearch.h"
+#include "Singleton.h"
+#include "BloomFilter.h"
+#include "FastAlloc.h"
+#include "MerkleTree.h"
+
+STANDARD_EXCEPTION(ShareException);
+
+class SimpleXML;
+class Client;
+class File;
+class OutputStream;
+class MemoryInputStream;
+
+struct ShareLoader;
+class ShareManager : public Singleton<ShareManager>, private SettingsManagerListener, private Thread, private TimerManagerListener,
+ private HashManagerListener, private DownloadManagerListener
+{
+public:
+ /**
+ * @param aDirectory Physical directory localtion
+ * @param aName Virtual name
+ */
+ void addDirectory(const string& aDirectory, const string & aName) throw(ShareException);
+ void removeDirectory(const string& aName, bool duringRefresh = false);
+ void renameDirectory(const string& oName, const string& nName) throw(ShareException);
+ string translateFileName(const string& aFile) throw(ShareException);
+ bool getTTH(const string& aFile, TTHValue& tth) throw();
+ void refresh(bool dirs = false, bool aUpdate = true, bool block = false) throw(ShareException);
+ void setDirty() { xmlDirty = nmdcDirty = true; };
+
+ void search(SearchResult::List& l, const string& aString, int aSearchType, int64_t aSize, int aFileType, Client* aClient, StringList::size_type maxResults);
+ void search(SearchResult::List& l, const StringList& params, StringList::size_type maxResults);
+
+ StringPairList getDirectories() const { RLock<> l(cs); return virtualMap; }
+
+ MemoryInputStream* generatePartialList(const string& dir, bool recurse);
+ MemoryInputStream* getTree(const string& aFile);
+
+ AdcCommand getFileInfo(const string& aFile) throw(ShareException);
+
+ int64_t getShareSize() throw();
+ int64_t getShareSize(const string& aDir) throw();
+
+ size_t getSharedFiles() throw();
+
+ string getShareSizeString() { return Util::toString(getShareSize()); };
+ string getShareSizeString(const string& aDir) { return Util::toString(getShareSize(aDir)); };
+
+ int64_t getListLen() { return generateNmdcList(), listLen; };
+ string getListLenString() { return Util::toString(getListLen()); };
+
+ SearchManager::TypeModes getType(const string& fileName);
+
+ string validateVirtual(const string& /*aVirt*/);
+
+ void addHits(u_int32_t aHits) {
+ hits += aHits;
+ }
+
+ string getOwnListFile() {
+ generateXmlList();
+ return getBZXmlFile();
+ }
+
+ bool isTTHShared(const TTHValue& tth){
+ HashFileIter i = tthIndex.find(const_cast<TTHValue*>(&tth));
+ return (i != tthIndex.end());
+ }
+
+ GETSET(u_int32_t, hits, Hits);
+ GETSET(string, listFile, ListFile);
+ GETSET(string, bzXmlFile, BZXmlFile);
+
+private:
+ struct AdcSearch;
+ class Directory : public FastAlloc<Directory> {
+ public:
+ struct File {
+ struct StringComp {
+ StringComp(const string& s) : a(s) { }
+ bool operator()(const File& b) const { return Util::stricmp(a, b.getName()) == 0; }
+ const string& a;
+ private:
+ StringComp& operator=(const StringComp&);
+ };
+ struct FileLess {
+ int operator()(const File& a, const File& b) const { return Util::stricmp(a.getName(), b.getName()); }
+ };
+ typedef set<File, FileLess> Set;
+ typedef Set::iterator Iter;
+
+ File() : size(0), parent(NULL) { };
+ File(const string& aName, int64_t aSize, Directory* aParent, const TTHValue& aRoot) :
+ name(aName), tth(aRoot), size(aSize), parent(aParent) { };
+ File(const File& rhs) :
+ name(rhs.getName()), tth(rhs.getTTH()), size(rhs.getSize()), parent(rhs.getParent()) { };
+
+ ~File() { }
+
+ File& operator=(const File& rhs) {
+ name = rhs.name; size = rhs.size; parent = rhs.parent; tth = rhs.tth;
+ return *this;
+ }
+
+ string getADCPath() const { return parent->getADCPath() + name; }
+ string getFullName() const { return parent->getFullName() + getName(); }
+
+ GETSET(string, name, Name);
+ GETSET(TTHValue, tth, TTH);
+ GETSET(int64_t, size, Size);
+ GETSET(Directory*, parent, Parent);
+ };
+
+ typedef Directory* Ptr;
+ typedef HASH_MAP<string, Ptr> Map;
+ typedef Map::iterator MapIter;
+
+ int64_t size;
+ Map directories;
+ File::Set files;
+
+ Directory(const string& aName = Util::emptyString, Directory* aParent = NULL) :
+ size(0), name(aName), parent(aParent), fileTypes(0) {
+ };
+
+ ~Directory();
+
+ bool hasType(u_int32_t type) const throw() {
+ return ( (type == SearchManager::TYPE_ANY) || (fileTypes & (1 << type)) );
+ }
+ void addType(u_int32_t type) throw();
+
+ string getADCPath() const throw();
+ string getFullName() const throw();
+
+ int64_t getSize() {
+ int64_t tmp = size;
+ for(MapIter i = directories.begin(); i != directories.end(); ++i)
+ tmp+=i->second->getSize();
+ return tmp;
+ }
+
+ size_t countFiles() {
+ size_t tmp = files.size();
+ for(MapIter i = directories.begin(); i != directories.end(); ++i)
+ tmp+=i->second->countFiles();
+ return tmp;
+ }
+
+ void search(SearchResult::List& aResults, StringSearch::List& aStrings, int aSearchType, int64_t aSize, int aFileType, Client* aClient, StringList::size_type maxResults) throw();
+ void search(SearchResult::List& aResults, AdcSearch& aStrings, StringList::size_type maxResults) throw();
+
+ void toNmdc(string& nmdc, string& indent, string& tmp2);
+ void toXml(OutputStream& xmlFile, string& indent, string& tmp2, bool fullList);
+ void filesToXml(OutputStream& xmlFile, string& indent, string& tmp2);
+
+ File::Iter findFile(const string& aFile) { return find_if(files.begin(), files.end(), Directory::File::StringComp(aFile)); }
+
+ GETSET(string, name, Name);
+ GETSET(Directory*, parent, Parent);
+ private:
+ Directory(const Directory&);
+ Directory& operator=(const Directory&);
+
+ /** Set of flags that say which SearchManager::TYPE_* a directory contains */
+ u_int32_t fileTypes;
+
+ };
+
+ friend class Directory;
+ friend struct ShareLoader;
+
+ friend class Singleton<ShareManager>;
+ ShareManager();
+
+ virtual ~ShareManager();
+
+ struct AdcSearch {
+ AdcSearch(const StringList& params);
+
+ bool isExcluded(const string& str) {
+ for(StringSearch::Iter i = exclude.begin(); i != exclude.end(); ++i) {
+ if(i->match(str))
+ return true;
+ }
+ return false;
+ }
+
+ bool hasExt(const string& name) {
+ if(ext.empty())
+ return true;
+ for(StringIter i = ext.begin(); i != ext.end(); ++i) {
+ if(name.length() >= i->length() && Util::stricmp(name.c_str() + name.length() - i->length(), i->c_str()) == 0)
+ return true;
+ }
+ return false;
+ }
+
+ StringSearch::List* include;
+ StringSearch::List includeX;
+ StringSearch::List exclude;
+ StringList ext;
+
+ int64_t gt;
+ int64_t lt;
+
+ TTHValue root;
+ bool hasRoot;
+
+ bool isDirectory;
+ };
+
+ typedef HASH_MULTIMAP_X(TTHValue*, Directory::File::Iter, TTHValue::PtrHash, TTHValue::PtrHash, TTHValue::PtrLess) HashFileMap;
+ typedef HashFileMap::iterator HashFileIter;
+
+ HashFileMap tthIndex;
+
+ int64_t listLen;
+ int64_t bzXmlListLen;
+ bool xmlDirty;
+ bool nmdcDirty;
+ bool refreshDirs;
+ bool update;
+ bool initial;
+
+ int listN;
+
+ File* lFile;
+ File* xFile;
+
+ u_int32_t lastXmlUpdate;
+ u_int32_t lastNmdcUpdate;
+ u_int32_t lastFullUpdate;
+
+ mutable RWLock<> cs;
+ CriticalSection listGenLock;
+
+ // Map real name to directory structure
+ Directory::Map directories;
+
+ // Map virtual to real dir name
+ StringPairList virtualMap;
+
+ BloomFilter<5> bloom;
+
+ /** Find virtual name from real name */
+ StringPairIter findVirtual(const string& name);
+ /** Find real name from virtual name */
+ StringPairIter lookupVirtual(const string& name);
+
+ bool checkFile(const string& aDir, const string& aFile, Directory::File::Iter& it);
+
+ Directory* buildTree(const string& aName, Directory* aParent);
+ void addTree(Directory* aDirectory);
+ void addFile(Directory* dir, Directory::File::Iter i);
+ void generateNmdcList();
+ void generateXmlList();
+ bool loadCache();
+
+ void removeTTH(const TTHValue& tth, const Directory::File::Iter&);
+
+ Directory* getDirectory(const string& fname);
+
+ virtual int run();
+
+ // DownloadManagerListener
+ virtual void on(DownloadManagerListener::Complete, Download* d) throw();
+
+ // HashManagerListener
+ virtual void on(HashManagerListener::TTHDone, const string& fname, const TTHValue& root) throw();
+
+ // SettingsManagerListener
+ virtual void on(SettingsManagerListener::Save, SimpleXML* xml) throw() {
+ save(xml);
+ }
+ virtual void on(SettingsManagerListener::Load, SimpleXML* xml) throw() {
+ load(xml);
+ }
+
+ // TimerManagerListener
+ virtual void on(TimerManagerListener::Minute, u_int32_t tick) throw();
+ void load(SimpleXML* aXml);
+ void save(SimpleXML* aXml);
+
+};
+
+#endif // !defined(AFX_SHAREMANAGER_H__6CD5D87C_D13F_46E2_8C1E_5F116107C118__INCLUDED_)
+
+/**
+ * @file
+ * $Id: ShareManager.h,v 1.2 2005/08/21 14:03:43 olof Exp $
+ */
+
diff --git a/dcpp/SimpleXML.cpp b/dcpp/SimpleXML.cpp
new file mode 100644
index 0000000..c56b2b6
--- /dev/null
+++ b/dcpp/SimpleXML.cpp
@@ -0,0 +1,338 @@
+/*
+ * Copyright (C) 2001-2005 Jacek Sieka, arnetheduck on gmail point 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 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.
+ */
+
+#include "stdinc.h"
+#include "DCPlusPlus.h"
+
+#include "SimpleXML.h"
+
+const string SimpleXML::utf8Header = "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\"?>\r\n";
+
+string& SimpleXML::escape(string& aString, bool aAttrib, bool aLoading /* = false */, bool utf8 /* = true */) {
+ string::size_type i = 0;
+ const char* chars = aAttrib ? "<&>'\"" : "<&>";
+
+ if(aLoading) {
+ while((i = aString.find('&', i)) != string::npos) {
+ if(aString.compare(i+1, 3, "lt;") == 0) {
+ aString.replace(i, 4, 1, '<');
+ } else if(aString.compare(i+1, 4, "amp;") == 0) {
+ aString.replace(i, 5, 1, '&');
+ } else if(aString.compare(i+1, 3, "gt;") == 0) {
+ aString.replace(i, 4, 1, '>');
+ } else if(aAttrib) {
+ if(aString.compare(i+1, 5, "apos;") == 0) {
+ aString.replace(i, 6, 1, '\'');
+ } else if(aString.compare(i+1, 5, "quot;") == 0) {
+ aString.replace(i, 6, 1, '"');
+ }
+ }
+ i++;
+ }
+ i = 0;
+ if( (i = aString.find('\n')) != string::npos) {
+ if(i > 0 && aString[i-1] != '\r') {
+ // This is a unix \n thing...convert it...
+ i = 0;
+ while( (i = aString.find('\n', i) ) != string::npos) {
+ if(aString[i-1] != '\r')
+ aString.insert(i, 1, '\r');
+
+ i+=2;
+ }
+ }
+ }
+ if(!utf8) {
+ // Not very performant, but shouldn't happen very often
+ aString = Text::acpToUtf8(aString);
+ }
+ } else {
+ while( (i = aString.find_first_of(chars, i)) != string::npos) {
+ switch(aString[i]) {
+ case '<': aString.replace(i, 1, "&lt;"); i+=4; break;
+ case '&': aString.replace(i, 1, "&amp;"); i+=5; break;
+ case '>': aString.replace(i, 1, "&gt;"); i+=4; break;
+ case '\'': aString.replace(i, 1, "&apos;"); i+=6; break;
+ case '"': aString.replace(i, 1, "&quot;"); i+=6; break;
+ default: dcasserta(0);
+ }
+ }
+ if(!utf8) {
+ aString = Text::utf8ToAcp(aString);
+ }
+ }
+ return aString;
+}
+
+void SimpleXML::Tag::appendAttribString(string& tmp) {
+ for(StringPairIter i = attribs.begin(); i!= attribs.end(); ++i) {
+ tmp.append(i->first);
+ tmp.append("=\"", 2);
+ if(needsEscape(i->second, true)) {
+ string tmp2(i->second);
+ escape(tmp2, true);
+ tmp.append(tmp2);
+ } else {
+ tmp.append(i->second);
+ }
+ tmp.append("\" ", 2);
+ }
+ tmp.erase(tmp.size()-1);
+}
+
+/**
+ * The same as the version above, but writes to a file instead...yes, this could be made
+ * with streams and only one code set but streams are slow...the file f should be a buffered
+ * file, otherwise things will be very slow (I assume write is not expensive and call it a lot
+ */
+void SimpleXML::Tag::toXML(int indent, OutputStream* f) {
+ if(children.empty() && data.empty()) {
+ string tmp;
+ tmp.reserve(indent + name.length() + 30);
+ tmp.append(indent, '\t');
+ tmp.append(1, '<');
+ tmp.append(name);
+ tmp.append(1, ' ');
+ appendAttribString(tmp);
+ tmp.append("/>\r\n", 4);
+ f->write(tmp);
+ } else {
+ string tmp;
+ tmp.append(indent, '\t');
+ tmp.append(1, '<');
+ tmp.append(name);
+ tmp.append(1, ' ');
+ appendAttribString(tmp);
+ if(children.empty()) {
+ tmp.append(1, '>');
+ if(needsEscape(data, false)) {
+ string tmp2(data);
+ escape(tmp2, false);
+ tmp.append(tmp2);
+ } else {
+ tmp.append(data);
+ }
+ } else {
+ tmp.append(">\r\n", 3);
+ f->write(tmp);
+ tmp.clear();
+ for(Iter i = children.begin(); i!=children.end(); ++i) {
+ (*i)->toXML(indent + 1, f);
+ }
+ tmp.append(indent, '\t');
+ }
+ tmp.append("</", 2);
+ tmp.append(name);
+ tmp.append(">\r\n", 3);
+ f->write(tmp);
+ }
+}
+
+bool SimpleXML::findChild(const string& aName) throw() {
+ dcassert(current != NULL);
+
+ if(found && currentChild != current->children.end())
+ currentChild++;
+
+ while(currentChild!=current->children.end()) {
+ if((*currentChild)->name == aName) {
+ found = true;
+ return true;
+ } else
+ currentChild++;
+ }
+ return false;
+}
+
+string::size_type SimpleXMLReader::loadAttribs(const string& name, const string& tmp, string::size_type start) throw(SimpleXMLException) {
+ string::size_type i = start;
+ string::size_type j;
+
+ for(;;) {
+ if((j = tmp.find('=', i)) == string::npos) {
+ throw SimpleXMLException("Missing '=' in " + name);
+ }
+
+ if(tmp[j+1] != '"' && tmp[j+1] != '\'') {
+ throw SimpleXMLException("Invalid character after '=' in " + name);
+ }
+
+ string::size_type x = j + 2;
+ string::size_type y;
+ if((y = tmp.find(tmp[j+1], x)) == string::npos) {
+ throw SimpleXMLException("Missing '" + string(1, tmp[j+1]) + "' in " + name);
+ }
+ // Ok, we have an attribute...
+ attribs.push_back(make_pair(tmp.substr(i, j-i), tmp.substr(x, y-x)));
+ SimpleXML::escape(attribs.back().second, true, true, utf8);
+
+ i = tmp.find_first_not_of(' ', y + 1);
+ if(tmp[i] == '/' || tmp[i] == '>') {
+ return i;
+ }
+ }
+}
+
+string::size_type SimpleXMLReader::fromXML(const string& tmp, const string& n, string::size_type start, int depth) throw(SimpleXMLException) {
+ string::size_type i = start;
+ string::size_type j;
+
+ bool hasChildren = false;
+
+ for(;;) {
+ if((j = tmp.find('<', i)) == string::npos) {
+ if(depth > 0) {
+ throw SimpleXMLException("Missing end tag in " + n);
+ }
+ return tmp.size();
+ } else if(depth > maxNesting) {
+ throw SimpleXMLException("Too many nested tags (depth >" + Util::toString(maxNesting) + ")");
+ }
+
+ // Check that we have at least 3 more characters as the shortest valid xml tag is <a/>...
+ if((j + 3) > tmp.size()) {
+ throw SimpleXMLException("Missing end tag in " + n);
+ }
+
+ i = j + 1;
+
+ if(tmp[i] == '?') {
+ // <? processing instruction ?>, check encoding...
+ if((j = tmp.find("?>", i)) == string::npos) {
+ throw SimpleXMLException("Missing '?>' in " + n);
+ }
+
+ string str = tmp.substr(i, j - i);
+ if(str.find("encoding=\"utf-8\"") == string::npos) {
+ // Ugly pass to convert from some other codepage to utf-8; note that we convert from the ACP, not the one specified in the xml...
+ utf8 = false;
+ }
+
+ i = j + 2;
+ continue;
+ }
+
+ if(tmp[i] == '!' && tmp[i+1] == '-' && tmp[i+2] == '-') {
+ // <!-- comment -->, ignore...
+ if((j = tmp.find("-->", i)) == string::npos) {
+ throw SimpleXMLException("Missing '-->' in " + n);
+ }
+ i = j + 3;
+ continue;
+ }
+
+ // Check if we reached the end tag
+ if(tmp[i] == '/') {
+ i++;
+
+ if( (tmp.compare(i, n.length(), n) == 0) &&
+ (tmp[i + n.length()] == '>') )
+ {
+ if(!hasChildren) {
+ data = tmp.substr(start, i - start - 2);
+ SimpleXML::escape(data, false, true, utf8);
+ } else {
+ data.clear();
+ }
+ return i + n.length() + 1;
+ } else {
+ throw SimpleXMLException("Missing end tag in " + n);
+ }
+ }
+
+ // Alright, we have a real tag for sure...now get the name of it.
+ if((j = tmp.find_first_of(" />", i)) == string::npos) {
+ throw SimpleXMLException("Missing '>' in " + n);
+ }
+
+ string name = tmp.substr(i, j-i);
+ hasChildren = true;
+ if(tmp[j] == ' ') {
+ if((j = tmp.find_first_not_of(' ', j+1)) == string::npos) {
+ throw SimpleXMLException("Missing '>' in " + name);
+ }
+ }
+
+ attribs.clear();
+ if(tmp[j] != '/' && tmp[j] != '>') {
+ // We have attribs...
+ j = loadAttribs(name, tmp, j);
+ }
+
+ if(tmp[j] == '>') {
+ // This is a real tag with data etc...
+ cb->startTag(name, attribs, false);
+ j = fromXML(tmp, name, j+1, depth+1);
+ cb->endTag(name, data);
+ } else {
+ // A simple tag (<xxx/>
+ cb->startTag(name, attribs, true);
+ j++;
+ }
+ i = j;
+ }
+}
+
+void SimpleXML::addTag(const string& aName, const string& aData /* = "" */) throw(SimpleXMLException) {
+ if(aName.empty()) {
+ throw SimpleXMLException("Empty tag names not allowed");
+ }
+
+ if(current == &root && !current->children.empty()) {
+ throw SimpleXMLException("Only one root tag allowed");
+ } else {
+ current->children.push_back(new Tag(aName, aData, current));
+ currentChild = current->children.end() - 1;
+ }
+}
+
+void SimpleXML::addAttrib(const string& aName, const string& aData) throw(SimpleXMLException) {
+ if(current == &root)
+ throw SimpleXMLException("No tag is currently selected");
+
+ current->attribs.push_back(make_pair(aName, aData));
+}
+
+void SimpleXML::addChildAttrib(const string& aName, const string& aData) throw(SimpleXMLException) {
+ checkChildSelected();
+
+ (*currentChild)->attribs.push_back(make_pair(aName, aData));
+}
+
+void SimpleXML::fromXML(const string& aXML) throw(SimpleXMLException) {
+ if(!root.children.empty()) {
+ delete root.children[0];
+ root.children.clear();
+ }
+
+ TagReader t(&root);
+ SimpleXMLReader(&t).fromXML(aXML);
+
+ if(root.children.size() != 1) {
+ throw SimpleXMLException("Invalid XML file, missing or multiple root tags");
+ }
+
+ current = &root;
+ resetCurrentChild();
+}
+
+/**
+ * @file
+ * $Id: SimpleXML.cpp,v 1.2 2005/08/21 14:03:43 olof Exp $
+ */
+
diff --git a/dcpp/SimpleXML.h b/dcpp/SimpleXML.h
new file mode 100644
index 0000000..a037372
--- /dev/null
+++ b/dcpp/SimpleXML.h
@@ -0,0 +1,289 @@
+/*
+ * Copyright (C) 2001-2005 Jacek Sieka, arnetheduck on gmail point 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 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.
+ */
+
+#if !defined(AFX_SIMPLEXML_H__3FDC96DD_A4D6_4357_9557_9D7585529A98__INCLUDED_)
+#define AFX_SIMPLEXML_H__3FDC96DD_A4D6_4357_9557_9D7585529A98__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+#include "Exception.h"
+
+#include "File.h"
+
+STANDARD_EXCEPTION(SimpleXMLException);
+
+/**
+ * This class reads an XML and calls a callback for each
+ * element encountered.
+ * It is in no way a full XML parser.
+ */
+class SimpleXMLReader {
+public:
+ struct CallBack {
+ virtual ~CallBack() { }
+ virtual void startTag(const string& name, StringPairList& attribs, bool simple) = 0;
+ virtual void endTag(const string& name, const string& data) = 0;
+
+ protected:
+ static const string& getAttrib(StringPairList& attribs, const string& name, size_t hint) {
+ hint = min(hint, attribs.size());
+
+ StringPairIter i = find_if(attribs.begin() + hint, attribs.end(), CompareFirst<string, string>(name));
+ if(i == attribs.end()) {
+ i = find_if(attribs.begin(), attribs.begin() + hint, CompareFirst<string, string>(name));
+ return ((i == (attribs.begin() + hint)) ? Util::emptyString : i->second);
+ } else {
+ return i->second;
+ }
+ }
+ private:
+ CallBack& operator=(const CallBack&);
+ };
+
+ SimpleXMLReader(CallBack* callback) : cb(callback), utf8(true) { }
+ virtual ~SimpleXMLReader() { }
+
+ string::size_type fromXML(const string& tmp, const string& n = Util::emptyString, string::size_type start = 0, int depth = 0) throw(SimpleXMLException);
+private:
+ StringPairList attribs;
+ string data;
+
+ CallBack* cb;
+ bool utf8;
+
+ string::size_type loadAttribs(const string& name, const string& tmp, string::size_type start) throw(SimpleXMLException);
+ static const int maxNesting = 200;
+};
+
+/**
+ * A simple XML class that loads an XML-ish structure into an internal tree
+ * and allows easy access to each element through a "current location".
+ */
+class SimpleXML
+{
+public:
+ SimpleXML() : root("BOGUSROOT", Util::emptyString, NULL), current(&root), found(false) {
+ resetCurrentChild();
+ };
+ ~SimpleXML() { }
+
+ void addTag(const string& aName, const string& aData = Util::emptyString) throw(SimpleXMLException);
+ void addTag(const string& aName, int aData) throw(SimpleXMLException) {
+ addTag(aName, Util::toString(aData));
+ }
+ void addTag(const string& aName, int64_t aData) throw(SimpleXMLException) {
+ addTag(aName, Util::toString(aData));
+ }
+
+ template<typename T>
+ void addAttrib(const string& aName, const T& aData) throw(SimpleXMLException) {
+ addAttrib(aName, Util::toString(aData));
+ }
+
+ void addAttrib(const string& aName, const string& aData) throw(SimpleXMLException);
+ void addAttrib(const string& aName, bool aData) throw(SimpleXMLException) {
+ addAttrib(aName, string(aData ? "1" : "0"));
+ }
+
+ template <typename T>
+ void addChildAttrib(const string& aName, const T& aData) throw(SimpleXMLException) {
+ addChildAttrib(aName, Util::toString(aData));
+ }
+ void addChildAttrib(const string& aName, const string& aData) throw(SimpleXMLException);
+ void addChildAttrib(const string& aName, bool aData) throw(SimpleXMLException) {
+ addChildAttrib(aName, string(aData ? "1" : "0"));
+ }
+
+ const string& getData() const {
+ dcassert(current != NULL);
+ return current->data;
+ }
+
+ void stepIn() throw(SimpleXMLException) {
+ checkChildSelected();
+ current = *currentChild;
+ currentChild = current->children.begin();
+ found = false;
+ }
+
+ void stepOut() throw(SimpleXMLException) {
+ if(current == &root)
+ throw SimpleXMLException("Already at lowest level");
+
+ dcassert(current->parent != NULL);
+
+ currentChild = find(current->parent->children.begin(), current->parent->children.end(), current);
+
+ current = current->parent;
+ found = true;
+ }
+
+ void resetCurrentChild() throw() {
+ found = false;
+ dcassert(current != NULL);
+ currentChild = current->children.begin();
+ }
+
+ bool findChild(const string& aName) throw();
+
+ const string& getChildData() const throw(SimpleXMLException) {
+ checkChildSelected();
+ return (*currentChild)->data;
+ }
+
+ const string& getChildAttrib(const string& aName, const string& aDefault = Util::emptyString) throw(SimpleXMLException) {
+ checkChildSelected();
+ return (*currentChild)->getAttrib(aName, aDefault);
+ }
+
+ int getIntChildAttrib(const string& aName) throw(SimpleXMLException) {
+ checkChildSelected();
+ return Util::toInt(getChildAttrib(aName));
+ }
+ int64_t getLongLongChildAttrib(const string& aName) throw(SimpleXMLException) {
+ checkChildSelected();
+ return Util::toInt64(getChildAttrib(aName));
+ }
+ bool getBoolChildAttrib(const string& aName) throw(SimpleXMLException) {
+ checkChildSelected();
+ const string& tmp = getChildAttrib(aName);
+
+ return (tmp.size() > 0) && tmp[0] == '1';
+ }
+
+ void fromXML(const string& aXML) throw(SimpleXMLException);
+ string toXML() { string tmp; StringOutputStream os(tmp); toXML(&os); return tmp; };
+ void toXML(OutputStream* f) throw(FileException) { if(!root.children.empty()) root.children[0]->toXML(0, f); };
+
+ static const string& escape(const string& str, string& tmp, bool aAttrib, bool aLoading = false, bool utf8 = true) {
+ if(needsEscape(str, aAttrib, aLoading, utf8)) {
+ tmp = str;
+ return escape(tmp, aAttrib, aLoading, utf8);
+ }
+ return str;
+ }
+ static string& escape(string& aString, bool aAttrib, bool aLoading = false, bool utf8 = true);
+ /**
+ * This is a heurestic for whether escape needs to be called or not. The results are
+ * only guaranteed for false, i e sometimes true might be returned even though escape
+ * was not needed...
+ */
+ static bool needsEscape(const string& aString, bool aAttrib, bool aLoading = false, bool utf8 = true) {
+ return !utf8 || (((aLoading) ? aString.find('&') : aString.find_first_of(aAttrib ? "<&>'\"" : "<&>")) != string::npos);
+ }
+ static const string utf8Header;
+private:
+ class Tag {
+ public:
+ typedef Tag* Ptr;
+ typedef vector<Ptr> List;
+ typedef List::iterator Iter;
+
+ /**
+ * A simple list of children. To find a tag, one must search the entire list.
+ */
+ List children;
+ /**
+ * Attributes of this tag. According to the XML standard the names
+ * must be unique (case-sensitive). (Assuming that we have few attributes here,
+ * we use a vector instead of a (hash)map to save a few bytes of memory and unnecessary
+ * calls to the memory allocator...)
+ */
+ StringPairList attribs;
+
+ /** Tag name */
+ string name;
+
+ /** Tag data, may be empty. */
+ string data;
+
+ /** Parent tag, for easy traversal */
+ Ptr parent;
+
+ Tag(const string& aName, const StringPairList& a, Ptr aParent) : attribs(a), name(aName), data(), parent(aParent) {
+ };
+
+ Tag(const string& aName, const string& d, Ptr aParent) : name(aName), data(d), parent(aParent) {
+ };
+
+ const string& getAttrib(const string& aName, const string& aDefault = Util::emptyString) {
+ StringPairIter i = find_if(attribs.begin(), attribs.end(), CompareFirst<string,string>(aName));
+ return (i == attribs.end()) ? aDefault : i->second;
+ }
+ void toXML(int indent, OutputStream* f);
+
+ void appendAttribString(string& tmp);
+ /** Delete all children! */
+ ~Tag() {
+ for(Iter i = children.begin(); i != children.end(); ++i) {
+ delete *i;
+ }
+ }
+
+ private:
+ Tag(const Tag&);
+ Tag& operator=(Tag&);
+ };
+
+ class TagReader : public SimpleXMLReader::CallBack {
+ public:
+ TagReader(Tag* root) : cur(root) { };
+ virtual bool getData(string&) { return false; }
+ virtual void startTag(const string& name, StringPairList& attribs, bool simple) {
+ cur->children.push_back(new Tag(name, attribs, cur));
+ if(!simple)
+ cur = cur->children.back();
+ }
+ virtual void endTag(const string&, const string& d) {
+ cur->data = d;
+ if(cur->parent == NULL)
+ throw SimpleXMLException("Invalid end tag");
+ cur = cur->parent;
+ }
+
+ Tag* cur;
+ };
+
+ SimpleXML(const SimpleXML&);
+ SimpleXML& operator=(const SimpleXML&);
+
+ /** Bogus root tag, should have only one child! */
+ Tag root;
+
+ /** Current position */
+ Tag::Ptr current;
+
+ Tag::Iter currentChild;
+
+ void checkChildSelected() const throw() {
+ dcassert(current != NULL);
+ dcassert(currentChild != current->children.end());
+ }
+
+ bool found;
+};
+
+#endif // !defined(AFX_SIMPLEXML_H__3FDC96DD_A4D6_4357_9557_9D7585529A98__INCLUDED_)
+
+/**
+ * @file
+ * $Id: SimpleXML.h,v 1.2 2005/08/21 14:03:43 olof Exp $
+ */
+
diff --git a/dcpp/Singleton.h b/dcpp/Singleton.h
new file mode 100644
index 0000000..df9c350
--- /dev/null
+++ b/dcpp/Singleton.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2001-2005 Jacek Sieka, arnetheduck on gmail point 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 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.
+ */
+
+#ifndef SINGLETON_H
+#define SINGLETON_H
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+template<typename T>
+class Singleton {
+public:
+ Singleton() { }
+ virtual ~Singleton() { }
+
+ static T* getInstance() {
+ dcassert(instance);
+ return instance;
+ }
+
+ static void newInstance() {
+ if(instance)
+ delete instance;
+
+ instance = new T();
+ }
+
+ static void deleteInstance() {
+ if(instance)
+ delete instance;
+ instance = NULL;
+ }
+protected:
+ static T* instance;
+private:
+ Singleton(const Singleton&);
+ Singleton& operator=(const Singleton&);
+
+};
+
+template<class T> T* Singleton<T>::instance = NULL;
+
+#endif // SINGLETON_H
+
+/**
+ * @file
+ * $Id: Singleton.h,v 1.2 2005/08/21 14:03:43 olof Exp $
+ */
diff --git a/dcpp/Socket.cpp b/dcpp/Socket.cpp
new file mode 100644
index 0000000..1d06a8b
--- /dev/null
+++ b/dcpp/Socket.cpp
@@ -0,0 +1,590 @@
+/*
+ * Copyright (C) 2001-2005 Jacek Sieka, arnetheduck on gmail point 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 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.
+ */
+
+#include "stdinc.h"
+#include "DCPlusPlus.h"
+
+#include "Socket.h"
+
+#include "SettingsManager.h"
+#include "ResourceManager.h"
+
+#include "ServerSocket.h"
+
+string Socket::udpServer;
+short Socket::udpPort;
+
+#define checkconnected() if(!isConnected()) throw SocketException(STRING(NOT_CONNECTED))
+
+#ifdef _DEBUG
+
+SocketException::SocketException(int aError) throw() {
+ error = "SocketException: " + errorToString(aError);
+ dcdebug("Thrown: %s\n", error.c_str());
+}
+
+#else // _DEBUG
+
+SocketException::SocketException(int aError) throw() {
+ error = errorToString(aError);
+}
+
+#endif
+
+Socket::Stats Socket::stats = { 0, 0 };
+
+string SocketException::errorToString(int aError) throw() {
+ switch(aError) {
+ case EWOULDBLOCK:
+ return STRING(OPERATION_WOULD_BLOCK_EXECUTION);
+ case EACCES:
+ return STRING(PERMISSION_DENIED);
+ case EADDRINUSE:
+ return STRING(ADDRESS_ALREADY_IN_USE);
+ case EADDRNOTAVAIL:
+ return STRING(ADDRESS_NOT_AVAILABLE);
+ case EALREADY:
+ return STRING(NON_BLOCKING_OPERATION);
+ case ECONNREFUSED:
+ return STRING(CONNECTION_REFUSED);
+ case ETIMEDOUT:
+ return STRING(CONNECTION_TIMEOUT);
+ case EHOSTUNREACH:
+ return STRING(HOST_UNREACHABLE);
+ case ESHUTDOWN:
+ return STRING(SOCKET_SHUT_DOWN);
+ case ECONNABORTED:
+ return STRING(CONNECTION_CLOSED);
+ case ECONNRESET:
+ return STRING(CONNECTION_RESET);
+ case ENOTSOCK:
+ return STRING(NOT_SOCKET);
+ case ENOTCONN:
+ return STRING(NOT_CONNECTED);
+ case ENETUNREACH:
+ return STRING(NETWORK_UNREACHABLE);
+ default:
+ {
+ char tmp[64];
+ sprintf(tmp, CSTRING(UNKNOWN_ERROR), aError);
+ return tmp;
+ }
+ }
+}
+
+void Socket::create(int aType /* = TYPE_TCP */, bool server /* = false */) throw(SocketException) {
+ if(sock != INVALID_SOCKET)
+ Socket::disconnect();
+
+ switch(aType) {
+ case TYPE_TCP:
+ checksocket(sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP));
+ break;
+ case TYPE_UDP:
+ checksocket(sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP));
+ break;
+ default:
+ dcasserta(0);
+ }
+ // Multiple interface fix
+ if(!server && SETTING(BIND_ADDRESS) != "0.0.0.0") {
+ sockaddr_in sock_addr;
+ sock_addr.sin_family = AF_INET;
+ sock_addr.sin_port = htons(0); // Let stack choose our port
+ sock_addr.sin_addr.s_addr = inet_addr(SETTING(BIND_ADDRESS).c_str());
+ ::bind(sock, (sockaddr *)&sock_addr, sizeof(sock_addr));
+ // If it fails, we'll get normal INADDR binding instead...
+ }
+ type = aType;
+}
+
+/**
+ * Binds an UDP socket to a certain local port.
+ */
+void Socket::bind(short aPort) throw (SocketException){
+ dcassert(type == TYPE_UDP);
+
+ sockaddr_in sock_addr;
+
+ sock_addr.sin_family = AF_INET;
+ sock_addr.sin_port = htons(aPort);
+ // Multiple interface fix
+ sock_addr.sin_addr.s_addr = inet_addr(SETTING(BIND_ADDRESS).c_str());
+ if(::bind(sock, (sockaddr *)&sock_addr, sizeof(sock_addr)) == SOCKET_ERROR) {
+ sock_addr.sin_addr.s_addr = htonl(INADDR_ANY);
+ checksockerr(::bind(sock, (sockaddr *)&sock_addr, sizeof(sock_addr)));
+ }
+ connected = true;
+}
+
+
+void Socket::accept(const ServerSocket& aSocket) throw(SocketException){
+ if(sock != INVALID_SOCKET) {
+ Socket::disconnect();
+ }
+ type = TYPE_TCP;
+ dcassert(!isConnected());
+
+ sockaddr_in sock_addr;
+ socklen_t sz = sizeof(sock_addr);
+
+ checksockerr(sock=::accept(aSocket.getSocket(), (sockaddr*)&sock_addr, &sz));
+#ifdef _WIN32
+ // Make sure we disable any inherited windows message things for this socket.
+ ::WSAAsyncSelect(sock, NULL, 0, 0);
+#endif
+ setBlocking(true);
+ connected = true;
+
+ setIp(inet_ntoa(sock_addr.sin_addr));
+}
+
+/**
+ * Connects a socket to an address/ip, closing any other connections made with
+ * this instance.
+ * @param aAddr Server address, in dns or xxx.xxx.xxx.xxx format.
+ * @param aPort Server port.
+ * @throw SocketException If any connection error occurs.
+ */
+void Socket::connect(const string& aAddr, short aPort) throw(SocketException) {
+ sockaddr_in serv_addr;
+ hostent* host;
+
+ if(sock == INVALID_SOCKET) {
+ create();
+ }
+
+ memset(&serv_addr, 0, sizeof(serv_addr));
+ serv_addr.sin_port = htons(aPort);
+ serv_addr.sin_family = AF_INET;
+
+ serv_addr.sin_addr.s_addr = inet_addr(aAddr.c_str());
+
+ if (serv_addr.sin_addr.s_addr == INADDR_NONE) { /* server address is a name or invalid */
+ host = gethostbyname(aAddr.c_str());
+ if (host == NULL) {
+ throw SocketException(STRING(UNKNOWN_ADDRESS));
+ }
+ serv_addr.sin_addr.s_addr = *((u_int32_t*)host->h_addr);
+ }
+
+ setIp(inet_ntoa(serv_addr.sin_addr));
+
+ if(::connect(sock,(sockaddr*)&serv_addr,sizeof(serv_addr)) == SOCKET_ERROR) {
+ // EWOULDBLOCK is ok, the attempt is still being made, and FD_CONNECT will be signaled...
+ if(errno != EWOULDBLOCK) {
+ checksockerr(SOCKET_ERROR);
+ }
+ }
+ connected = true;
+}
+
+/**
+ * Reads zero to aBufLen characters from this socket,
+ * @param aBuffer A buffer to store the data in.
+ * @param aBufLen Size of the buffer.
+ * @return Number of bytes read, 0 if disconnected and -1 if the call would block.
+ * @throw SocketException On any failure.
+ */
+int Socket::read(void* aBuffer, int aBufLen) throw(SocketException) {
+ checkconnected();
+ int len = 0;
+ if(type == TYPE_TCP) {
+ checkrecv(len=::recv(sock, (char*)aBuffer, aBufLen, 0));
+ } else if(type == TYPE_UDP) {
+ checkrecv(len=::recvfrom(sock, (char*)aBuffer, aBufLen, 0, NULL, NULL));
+ }
+ stats.totalDown += len;
+ return len;
+}
+
+/**
+ * Reads zero to aBufLen characters from this socket,
+ * @param aBuffer A buffer to store the data in.
+ * @param aBufLen Size of the buffer.
+ * @param aIP Remote IP address
+ * @return Number of bytes read, 0 if disconnected and -1 if the call would block.
+ * @throw SocketException On any failure.
+ */
+int Socket::read(void* aBuffer, int aBufLen, string &aIP) throw(SocketException) {
+ checkconnected();
+ int len = 0;
+
+ sockaddr_in remote_addr = { 0 };
+ socklen_t addr_length = sizeof(remote_addr);
+
+ checkrecv(len=::recvfrom(sock, (char*)aBuffer, aBufLen, 0, (sockaddr*)&remote_addr, &addr_length)); //
+ aIP = string(inet_ntoa(remote_addr.sin_addr));
+
+ stats.totalDown += len;
+ return len;
+}
+
+/**
+ * Reads data until aBufLen bytes have been read or an error occurs.
+ * On error, an unspecified amount of bytes might have already been read...
+ */
+int Socket::readFull(void* aBuffer, int aBufLen) throw(SocketException) {
+ int i = 0;
+ int j;
+ while(i < aBufLen) {
+ if((j = read(((char*)aBuffer) + i, aBufLen - i)) <= 0) {
+ return j;
+ }
+ i += j;
+ }
+ return i;
+}
+
+/**
+ * Sends data, will block until all data has been sent or an exception occurs
+ * @param aBuffer Buffer with data
+ * @param aLen Data length
+ * @throw SocketExcpetion Send failed.
+ */
+void Socket::write(const char* aBuffer, size_t aLen) throw(SocketException) {
+ checkconnected();
+// dcdebug("Writing %db: %.100s\n", aLen, aBuffer);
+
+ if(aLen == 0){
+ return;
+ }
+
+ size_t pos = 0;
+ size_t sendSize = min(aLen, (size_t)64 * 1024);
+
+ bool blockAgain = false;
+
+ while(pos < aLen) {
+ int i = ::send(sock, aBuffer+pos, (int)min(aLen-pos, sendSize), 0);
+ if(i == SOCKET_ERROR) {
+ if(errno == EWOULDBLOCK) {
+ if(blockAgain) {
+ // Uhm, two blocks in a row...try making the send window smaller...
+ if(sendSize >= 256) {
+ sendSize /= 2;
+ dcdebug("Reducing send window size to %lu\n", sendSize);
+ } else {
+ Thread::sleep(10);
+ }
+ blockAgain = false;
+ } else {
+ blockAgain = true;
+ }
+ wait(2000, WAIT_WRITE);
+
+ } else if(errno == ENOBUFS) {
+ if(sendSize > 32) {
+ sendSize /= 2;
+ dcdebug("Reducing send window size to %lu\n", sendSize);
+ } else {
+ throw SocketException(STRING(OUT_OF_BUFFER_SPACE));
+ }
+ } else {
+ checksockerr(SOCKET_ERROR);
+ }
+ } else {
+ dcassert(i != 0);
+ pos+=i;
+
+ stats.totalUp += i;
+ blockAgain = false;
+ }
+ }
+}
+
+/**
+* Sends data, will block until all data has been sent or an exception occurs
+* @param aBuffer Buffer with data
+* @param aLen Data length
+* @throw SocketExcpetion Send failed.
+*/
+void Socket::writeTo(const string& aIp, short aPort, const char* aBuffer, size_t aLen) throw(SocketException) {
+ if(sock == INVALID_SOCKET) {
+ create(TYPE_UDP);
+ }
+ dcassert(type == TYPE_UDP);
+ // dcdebug("Writing %db: %.100s\n", aLen, aBuffer);
+ dcassert(aLen > 0);
+ dcassert(aLen < 1450);
+ dcassert(sock != INVALID_SOCKET);
+
+ sockaddr_in serv_addr;
+ hostent* host;
+
+ if(aIp.empty() || aPort == 0) {
+ throw SocketException(STRING(ADDRESS_NOT_AVAILABLE));
+ }
+
+ memset(&serv_addr, 0, sizeof(serv_addr));
+
+ if(SETTING(CONNECTION_TYPE) == SettingsManager::CONNECTION_SOCKS5 && !noproxy) {
+
+ if(udpServer.empty() || udpPort == 0) {
+ throw SocketException(STRING(SOCKS_SETUP_ERROR));
+ }
+
+ serv_addr.sin_port = htons(udpPort);
+ serv_addr.sin_family = AF_INET;
+
+ serv_addr.sin_addr.s_addr = inet_addr(udpServer.c_str());
+
+ string s = BOOLSETTING(SOCKS_RESOLVE) ? resolve(ip) : ip;
+
+ // Alrite, let's get on with it...
+ AutoArray<u_int8_t> connStr(10 + s.length() + aLen);
+ connStr[0] = 0; // Reserved
+ connStr[1] = 0; // Reserved
+ connStr[2] = 0; // Fragment number, 0 always in our case...
+
+ int connLen;
+ if(BOOLSETTING(SOCKS_RESOLVE)) {
+
+ u_int8_t slen =(u_int8_t)(s.length() & 0xff);
+ connStr[3] = 3; // Address type: domain name
+ connStr[4] = slen;
+ strncpy((char*)(u_int8_t*)connStr + 5, s.c_str(), slen);
+ *((u_int16_t*)(&connStr[5 + slen])) = htons(aPort);
+ connLen = 7 + slen;
+ } else {
+ connStr[3] = 1; // Address type: IPv4;
+ *((long*)(&connStr[4])) = inet_addr(s.c_str());
+ *((u_int16_t*)(&connStr[8])) = htons(aPort);
+ connLen = 10;
+ }
+
+ memcpy(((u_int8_t*)connStr) + connLen, aBuffer, aLen);
+
+ int i = ::sendto(sock, (char*)(u_int8_t*)connStr, connLen + (int)aLen, 0, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
+ checksockerr(i);
+
+ stats.totalUp += i;
+ } else {
+ serv_addr.sin_port = htons(aPort);
+ serv_addr.sin_family = AF_INET;
+
+ serv_addr.sin_addr.s_addr = inet_addr(aIp.c_str());
+
+ if (serv_addr.sin_addr.s_addr == INADDR_NONE) { /* server address is a name or invalid */
+ host = gethostbyname(aIp.c_str());
+ if (host == NULL) {
+ throw SocketException(STRING(UNKNOWN_ADDRESS));
+ }
+ serv_addr.sin_addr.s_addr = *((u_int32_t*)host->h_addr);
+ }
+
+ int i = ::sendto(sock, aBuffer, (int)aLen, 0, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
+ checksockerr(i);
+
+ stats.totalUp += i;
+ }
+}
+
+/**
+ * Blocks until timeout is reached one of the specified conditions have been fulfilled
+ * @param millis Max milliseconds to block.
+ * @param waitFor WAIT_*** flags that set what we're waiting for, set to the combination of flags that
+ * triggered the wait stop on return (==WAIT_NONE on timeout)
+ * @return WAIT_*** ored together of the current state.
+ * @throw SocketException Select or the connection attempt failed.
+ */
+int Socket::wait(u_int32_t millis, int waitFor) throw(SocketException) {
+ timeval tv;
+ fd_set rfd, wfd, efd;
+ fd_set *rfdp = NULL, *wfdp = NULL;
+ tv.tv_sec = millis/1000;
+ tv.tv_usec = (millis%1000)*1000;
+
+ if(waitFor & WAIT_CONNECT) {
+ dcassert(!(waitFor & WAIT_READ) && !(waitFor & WAIT_WRITE));
+
+ FD_ZERO(&wfd);
+ FD_ZERO(&efd);
+
+ FD_SET(sock, &wfd);
+ FD_SET(sock, &efd);
+ checksockerr(select((int)(sock+1), NULL, &wfd, &efd, &tv));
+
+ if(FD_ISSET(sock, &wfd) || FD_ISSET(sock, &efd)) {
+ int y = 0;
+ socklen_t z = sizeof(y);
+ checksockerr(getsockopt(sock, SOL_SOCKET, SO_ERROR, (char*)&y, &z));
+
+ if(y != 0)
+ throw SocketException(y);
+ // No errors! We're connected (?)...
+ return WAIT_CONNECT;
+ }
+ return 0;
+ }
+
+ if(waitFor & WAIT_READ) {
+ dcassert(!(waitFor & WAIT_CONNECT));
+ rfdp = &rfd;
+ FD_ZERO(rfdp);
+ FD_SET(sock, rfdp);
+ }
+ if(waitFor & WAIT_WRITE) {
+ dcassert(!(waitFor & WAIT_CONNECT));
+ wfdp = &wfd;
+ FD_ZERO(wfdp);
+ FD_SET(sock, wfdp);
+ }
+ waitFor = WAIT_NONE;
+ checksockerr(select((int)(sock+1), rfdp, wfdp, NULL, &tv));
+
+ if(rfdp && FD_ISSET(sock, rfdp)) {
+ waitFor |= WAIT_READ;
+ }
+ if(wfdp && FD_ISSET(sock, wfdp)) {
+ waitFor |= WAIT_WRITE;
+ }
+
+ return waitFor;
+}
+
+string Socket::resolve(const string& aDns) {
+ sockaddr_in sock_addr;
+
+ memset(&sock_addr, 0, sizeof(sock_addr));
+ sock_addr.sin_port = 0;
+ sock_addr.sin_family = AF_INET;
+ sock_addr.sin_addr.s_addr = inet_addr(aDns.c_str());
+
+ if (sock_addr.sin_addr.s_addr == INADDR_NONE) { /* server address is a name or invalid */
+ hostent* host;
+ host = gethostbyname(aDns.c_str());
+ if (host == NULL) {
+ return Util::emptyString;
+ }
+ sock_addr.sin_addr.s_addr = *((u_int32_t*)host->h_addr);
+ return inet_ntoa(sock_addr.sin_addr);
+ } else {
+ return aDns;
+ }
+}
+
+void Socket::socksUpdated() {
+ udpServer.clear();
+ udpPort = 0;
+
+ if(SETTING(CONNECTION_TYPE) == SettingsManager::CONNECTION_SOCKS5) {
+ try {
+ Socket s(SETTING(SOCKS_SERVER), (short)SETTING(SOCKS_PORT));
+
+ if(SETTING(SOCKS_USER).empty() && SETTING(SOCKS_PASSWORD).empty()) {
+ // No username and pw, easier...=)
+ char connStr[3];
+ connStr[0] = 5; // SOCKSv5
+ connStr[1] = 1; // 1 method
+ connStr[2] = 0; // Method 0: No auth...
+
+ s.write(connStr, 3);
+
+ if(s.readFull(connStr, 2) <= 0)
+ return;
+
+ if(connStr[1] != 0) {
+ return;
+ }
+ } else {
+ // We try the username and password auth type (no, we don't support gssapi)
+ u_int8_t ulen = (u_int8_t)(SETTING(SOCKS_USER).length() & 0xff);
+ u_int8_t plen = (u_int8_t)(SETTING(SOCKS_PASSWORD).length() & 0xff);
+ AutoArray<u_int8_t> connStr(3 + ulen + plen);
+
+ connStr[0] = 5; // SOCKSv5
+ connStr[1] = 1; // 1 method
+ connStr[2] = 2; // Method 2: Name/Password...
+ s.write((char*)(u_int8_t*)connStr, 3);
+ if(s.readFull((char*)(u_int8_t*)connStr, 2) <= 0)
+ return;
+
+ if(connStr[1] != 2) {
+ return;
+ }
+ // Now we send the username / pw...
+ connStr[0] = 1;
+ connStr[1] = ulen;
+ strncpy((char*)(u_int8_t*)connStr + 2, SETTING(SOCKS_USER).c_str(), ulen);
+ connStr[2 + ulen] = plen;
+ strncpy((char*)(u_int8_t*)connStr + 3 + ulen, SETTING(SOCKS_PASSWORD).c_str(), plen);
+ s.write((char*)(u_int8_t*)connStr, 3 + plen + ulen);
+
+ if(s.readFull((char*)(u_int8_t*)connStr, 2) <= 0) {
+ return;
+ }
+
+ if(connStr[1] != 0) {
+ return;
+ }
+
+ }
+ // Alrite, let's get on with it...
+ char connStr[10];
+ connStr[0] = 5; // SOCKSv5
+ connStr[1] = 3; // UDP Associate
+ connStr[2] = 0; // Reserved
+ connStr[3] = 1; // Address type: IPv4;
+ *((long*)(&connStr[4])) = 0; // No specific outgoing UDP address
+ *((u_int16_t*)(&connStr[8])) = 0; // No specific port...
+
+ s.write(connStr, 10);
+
+ // We assume we'll get a ipv4 address back...therefore, 10 bytes...if not, things
+ // will break, but hey...noone's perfect (and I'm tired...)...
+ if(s.readFull(connStr, 10) <= 0) {
+ return;
+ }
+
+ if(connStr[0] != 5 || connStr[1] != 0) {
+ return;
+ }
+
+ udpPort = (short)ntohs(*((u_int16_t*)(&connStr[8])));
+
+ sockaddr_in serv_addr;
+
+ memset(&serv_addr, 0, sizeof(serv_addr));
+ serv_addr.sin_port = htons(udpPort);
+ serv_addr.sin_family = AF_INET;
+
+ serv_addr.sin_addr.s_addr = *((long*)(&connStr[4]));
+ udpServer = inet_ntoa(serv_addr.sin_addr);
+ } catch(const SocketException&) {
+ dcdebug("Socket: Failed to register with socks server\n");
+ // ...
+ }
+ }
+}
+
+void Socket::disconnect() throw() {
+ if(sock != INVALID_SOCKET) {
+ printf("Shuting down fd:%d\n", sock);
+ ::shutdown(sock, 1); // Make sure we send FIN (SD_SEND shutdown type...)
+ closesocket(sock);
+ }
+ connected = false;
+
+ sock = INVALID_SOCKET;
+}
+
+/**
+ * @file
+ * $Id: Socket.cpp,v 1.3 2006/01/23 20:44:43 olof Exp $
+ */
diff --git a/dcpp/Socket.h b/dcpp/Socket.h
new file mode 100644
index 0000000..3095434
--- /dev/null
+++ b/dcpp/Socket.h
@@ -0,0 +1,230 @@
+/*
+ * Copyright (C) 2001-2005 Jacek Sieka, arnetheduck on gmail point 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 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.
+ */
+
+#ifndef SOCKET_H
+#define SOCKET_H
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+#include "Util.h"
+#include "Exception.h"
+
+#ifdef _WIN32
+// Berkely constants converted to the windows equivs...
+# define EWOULDBLOCK WSAEWOULDBLOCK
+# define EINPROGRESS WSAEINPROGRESS
+# define EALREADY WSAEALREADY
+# define ENOTSOCK WSAENOTSOCK
+# define EDESTADDRREQ WSAEDESTADDRREQ
+# define EMSGSIZE WSAEMSGSIZE
+# define EPROTOTYPE WSAEPROTOTYPE
+# define ENOPROTOOPT WSAENOPROTOOPT
+# define EPROTONOSUPPORT WSAEPROTONOSUPPORT
+# define ESOCKTNOSUPPORT WSAESOCKTNOSUPPORT
+# define EOPNOTSUPP WSAEOPNOTSUPP
+# define EPFNOSUPPORT WSAEPFNOSUPPORT
+# define EAFNOSUPPORT WSAEAFNOSUPPORT
+# define EADDRINUSE WSAEADDRINUSE
+# define EADDRNOTAVAIL WSAEADDRNOTAVAIL
+# define ENETDOWN WSAENETDOWN
+# define ENETUNREACH WSAENETUNREACH
+# define ENETRESET WSAENETRESET
+# define ECONNABORTED WSAECONNABORTED
+# define ECONNRESET WSAECONNRESET
+# define ENOBUFS WSAENOBUFS
+# define EISCONN WSAEISCONN
+# define ENOTCONN WSAENOTCONN
+# define ESHUTDOWN WSAESHUTDOWN
+# define ETOOMANYREFS WSAETOOMANYREFS
+# define ETIMEDOUT WSAETIMEDOUT
+# define ECONNREFUSED WSAECONNREFUSED
+# define ELOOP WSAELOOP
+# ifdef ENAMETOOLONG
+# undef ENAMETOOLONG
+# endif
+# define ENAMETOOLONG WSAENAMETOOLONG
+# define EHOSTDOWN WSAEHOSTDOWN
+# define EHOSTUNREACH WSAEHOSTUNREACH
+# ifdef ENOTEMPTY
+# undef ENOTEMPTY
+# endif
+# define ENOTEMPTY WSAENOTEMPTY
+# define EPROCLIM WSAEPROCLIM
+# define EUSERS WSAEUSERS
+# define EDQUOT WSAEDQUOT
+# define ESTALE WSAESTALE
+# define EREMOTE WSAEREMOTE
+# ifdef EACCES
+# undef EACCES
+# endif
+# define EACCES WSAEACCES
+# ifdef errno
+# undef errno
+# endif
+# define errno ::WSAGetLastError()
+# define checksocket(x) if((x) == INVALID_SOCKET) { int a = WSAGetLastError(); Socket::disconnect(); throw SocketException(a); }
+# define checkrecv(x) if((x) == SOCKET_ERROR) { int a = WSAGetLastError(); if(a == EWOULDBLOCK) return -1; else { Socket::disconnect(); throw SocketException(a); } }
+# define checksockerr(x) if((x) == SOCKET_ERROR) { int a = WSAGetLastError(); Socket::disconnect(); throw SocketException(a); }
+
+typedef int socklen_t;
+typedef SOCKET socket_t;
+
+#else
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <netdb.h>
+#include <fcntl.h>
+
+typedef int socket_t;
+#define SOCKET_ERROR -1
+#define INVALID_SOCKET -1
+# define closesocket(x) close(x)
+# define ioctlsocket(a, b, c) ioctl(a, b, c)
+# define checksocket(x) if((x) < 0) { Socket::disconnect(); throw SocketException(errno); }
+# define checkrecv(x) if((x) == SOCKET_ERROR) { Socket::disconnect(); throw SocketException(errno); }
+# define checksockerr(x) if((x) == SOCKET_ERROR) { Socket::disconnect(); throw SocketException(errno); }
+#endif
+
+class SocketException : public Exception {
+public:
+#ifdef _DEBUG
+ SocketException(const string& aError) throw() : Exception("SocketException: " + aError) { }
+#else //_DEBUG
+ SocketException(const string& aError) throw() : Exception(aError) { }
+#endif // _DEBUG
+
+ SocketException(int aError) throw();
+ virtual ~SocketException() throw() { }
+private:
+ static string errorToString(int aError) throw();
+};
+
+class ServerSocket;
+
+class Socket
+{
+public:
+ enum {
+ WAIT_NONE = 0x00,
+ WAIT_CONNECT = 0x01,
+ WAIT_READ = 0x02,
+ WAIT_WRITE = 0x04
+ };
+
+ enum {
+ TYPE_TCP,
+ TYPE_UDP
+ };
+
+ Socket() throw(SocketException) : noproxy(false), sock(INVALID_SOCKET), connected(false) { }
+ Socket(const string& aIp, const string& aPort) throw(SocketException) : noproxy(false), sock(INVALID_SOCKET), connected(false) { connect(aIp, aPort); }
+ Socket(const string& aIp, short aPort) throw(SocketException) : noproxy(false), sock(INVALID_SOCKET), connected(false) { connect(aIp, aPort); }
+ virtual ~Socket() throw() { Socket::disconnect(); };
+
+ virtual void create(int aType = TYPE_TCP, bool server = false) throw(SocketException);
+ virtual void bind(short aPort) throw(SocketException);
+ virtual void connect(const string& aIp, short aPort) throw(SocketException);
+ void connect(const string& aIp, const string& aPort) throw(SocketException) { connect(aIp, (short)Util::toInt(aPort)); };
+ virtual void accept(const ServerSocket& aSocket) throw(SocketException);
+ virtual void write(const char* aBuffer, size_t aLen) throw(SocketException);
+ void write(const string& aData) throw(SocketException) { write(aData.data(), aData.length()); };
+ virtual void writeTo(const string& aIp, short aPort, const char* aBuffer, size_t aLen) throw(SocketException);
+ void writeTo(const string& aIp, short aPort, const string& aData) throw(SocketException) { writeTo(aIp, aPort, aData.data(), aData.length()); };
+ virtual void disconnect() throw();
+
+ int read(void* aBuffer, int aBufLen) throw(SocketException);
+ int read(void* aBuffer, int aBufLen, string &aIP) throw(SocketException);
+ int readFull(void* aBuffer, int aBufLen) throw(SocketException);
+
+ int wait(u_int32_t millis, int waitFor) throw(SocketException);
+ bool isConnected() { return connected; };
+
+ static string resolve(const string& aDns);
+ static int64_t getTotalDown() { return stats.totalDown; };
+ static int64_t getTotalUp() { return stats.totalUp; };
+
+ int getAvailable() {
+ u_int32_t i = 0;
+ ioctlsocket(sock, FIONREAD, &i);
+ return i;
+ }
+
+#ifdef _WIN32
+ void setBlocking(bool block) throw() {
+ u_long b = block ? 0 : 1;
+ ioctlsocket(sock, FIONBIO, &b);
+ }
+#else
+ void setBlocking(bool block) throw() {
+ int flags = fcntl(sock, F_GETFL, 0);
+ if(block) {
+ fcntl(sock, F_SETFL, flags | O_NONBLOCK);
+ } else {
+ fcntl(sock, F_SETFL, flags & (~O_NONBLOCK));
+ }
+ }
+#endif
+
+ string getLocalIp() throw() {
+ if(sock == INVALID_SOCKET)
+ return Util::emptyString;
+
+ sockaddr_in sock_addr;
+ socklen_t len = sizeof(sock_addr);
+ if(getsockname(sock, (sockaddr*)&sock_addr, &len) == 0) {
+ return inet_ntoa(sock_addr.sin_addr);
+ }
+ return Util::emptyString;
+ }
+
+ /** When socks settings are updated, this has to be called... */
+ static void socksUpdated();
+
+ GETSET(string, ip, Ip);
+ GETSET(bool, noproxy, Noproxy);
+protected:
+ socket_t sock;
+ int type;
+ bool connected;
+
+ static string udpServer;
+ static short udpPort;
+
+private:
+ Socket(const Socket&);
+ Socket& operator=(const Socket&);
+
+ class Stats {
+ public:
+ int64_t totalDown;
+ int64_t totalUp;
+ };
+ static Stats stats;
+};
+
+#endif // _SOCKET_H
+
+/**
+ * @file
+ * $Id: Socket.h,v 1.2 2005/08/21 14:03:43 olof Exp $
+ */
diff --git a/dcpp/Speaker.h b/dcpp/Speaker.h
new file mode 100644
index 0000000..4031ce7
--- /dev/null
+++ b/dcpp/Speaker.h
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2001-2005 Jacek Sieka, arnetheduck on gmail point 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 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.
+ */
+
+#ifndef SPEAKER_H
+#define SPEAKER_H
+
+#include "CriticalSection.h"
+
+template<typename Listener>
+class Speaker {
+ typedef vector<Listener*> ListenerList;
+ typedef typename ListenerList::iterator ListenerIter;
+
+public:
+ Speaker() throw() { };
+ virtual ~Speaker() throw() { };
+
+ template<typename T0>
+ void fire(T0 type) throw() {
+ Lock l(listenerCS);
+ tmp = listeners;
+ for(ListenerIter i=tmp.begin(); i != tmp.end(); ++i ) {
+ (*i)->on(type);
+ }
+ }
+
+ template<typename T0, class T1>
+ void fire(T0 type, const T1& p1) throw() {
+ Lock l(listenerCS);
+ tmp = listeners;
+ for(ListenerIter i=tmp.begin(); i != tmp.end(); ++i ) {
+ (*i)->on(type, p1);
+ }
+ }
+
+ template<typename T0, class T1, class T2>
+ void fire(T0 type, const T1& p1, const T2& p2) throw() {
+ Lock l(listenerCS);
+ tmp = listeners;
+ for(ListenerIter i=tmp.begin(); i != tmp.end(); ++i ) {
+ (*i)->on(type, p1, p2);
+ }
+ }
+
+ template<typename T0, class T1, class T2, class T3>
+ void fire(T0 type, const T1& p1, const T2& p2, const T3& p3) throw() {
+ Lock l(listenerCS);
+ tmp = listeners;
+ for(ListenerIter i=tmp.begin(); i != tmp.end(); ++i ) {
+ (*i)->on(type, p1, p2, p3);
+ }
+ }
+
+ template<typename T0, class T1, class T2, class T3, class T4>
+ void fire(T0 type, const T1& p1, const T2& p2, const T3& p3, const T4& p4) throw() {
+ Lock l(listenerCS);
+ tmp = listeners;
+ for(ListenerIter i=tmp.begin(); i != tmp.end(); ++i ) {
+ (*i)->on(type, p1, p2, p3, p4);
+ }
+ }
+
+ template<typename T0, class T1, class T2, class T3, class T4, class T5>
+ void fire(T0 type, const T1& p1, const T2& p2, const T3& p3, const T4& p4, const T5& p5) throw() {
+ Lock l(listenerCS);
+ tmp = listeners;
+ for(ListenerIter i=tmp.begin(); i != tmp.end(); ++i ) {
+ (*i)->on(type, p1, p2, p3, p4, p5);
+ }
+ }
+
+ template<typename T0, class T1, class T2, class T3, class T4, class T5, class T6>
+ void fire(T0 type, const T1& p1, const T2& p2, const T3& p3, const T4& p4, const T5& p5, const T6& p6) throw() {
+ Lock l(listenerCS);
+ tmp = listeners;
+ for(ListenerIter i=tmp.begin(); i != tmp.end(); ++i ) {
+ (*i)->on(type, p1, p2, p3, p4, p5, p6);
+ }
+ }
+
+ void addListener(Listener* aListener) {
+ Lock l(listenerCS);
+ if(find(listeners.begin(), listeners.end(), aListener) == listeners.end())
+ listeners.push_back(aListener);
+ }
+
+ void removeListener(Listener* aListener) {
+ Lock l(listenerCS);
+ ListenerIter it = find(listeners.begin(), listeners.end(), aListener);
+ if(it != listeners.end())
+ listeners.erase(it);
+ }
+
+ void removeListeners() {
+ Lock l(listenerCS);
+ listeners.clear();
+ }
+
+protected:
+ ListenerList listeners;
+ ListenerList tmp;
+ CriticalSection listenerCS;
+};
+
+#endif // SPEAKER_H
+/**
+ * @file
+ * $Id: Speaker.h,v 1.2 2005/08/21 14:03:43 olof Exp $
+ */
diff --git a/dcpp/Streams.h b/dcpp/Streams.h
new file mode 100644
index 0000000..894b2d0
--- /dev/null
+++ b/dcpp/Streams.h
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2001-2005 Jacek Sieka, arnetheduck on gmail point 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 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.
+ */
+
+#ifndef STREAMS_H
+#define STREAMS_H
+
+#include "Exception.h"
+STANDARD_EXCEPTION(FileException);
+
+/**
+ * A naive output stream. We don't use the stl ones to avoid compiling STLPort,
+ * besides this is a lot more lightweight (and less flexible)...
+ */
+class OutputStream {
+public:
+ OutputStream() { }
+ virtual ~OutputStream() throw() { }
+
+ /**
+ * @return The actual number of bytes written. len bytes will always be
+ * consumed, but fewer or more bytes may actually be written,
+ * for example if the stream is being compressed.
+ */
+ virtual size_t write(const void* buf, size_t len) throw(Exception) = 0;
+ /**
+ * This must be called before destroying the object to make sure all data
+ * is properly written (we don't want destructors that throw exceptions
+ * and the last flush might actually throw). Note that some implementations
+ * might not need it...
+ */
+ virtual size_t flush() throw(Exception) = 0;
+
+ size_t write(const string& str) throw(Exception) { return write(str.c_str(), str.size()); };
+private:
+ OutputStream(const OutputStream&);
+ OutputStream& operator=(const OutputStream&);
+};
+
+class InputStream {
+public:
+ InputStream() { }
+ virtual ~InputStream() throw() { }
+ /**
+ * Call this function until it returns 0 to get all bytes.
+ * @return The number of bytes read. len reflects the number of bytes
+ * actually read from the stream source in this call.
+ */
+ virtual size_t read(void* buf, size_t& len) throw(Exception) = 0;
+private:
+ InputStream(const InputStream&);
+ InputStream& operator=(const InputStream&);
+};
+
+class MemoryInputStream : public InputStream {
+public:
+ MemoryInputStream(const u_int8_t* src, size_t len) : pos(0), size(len), buf(new u_int8_t[len]) {
+ memcpy(buf, src, len);
+ }
+ MemoryInputStream(const string& src) : pos(0), size(src.size()), buf(new u_int8_t[src.size()]) {
+ memcpy(buf, src.data(), src.size());
+ }
+
+ ~MemoryInputStream() throw() {
+ delete[] buf;
+ }
+
+ virtual size_t read(void* tgt, size_t& len) throw(Exception) {
+ len = min(len, size - pos);
+ memcpy(tgt, buf+pos, len);
+ pos += len;
+ return len;
+ }
+
+ size_t getSize() { return size; }
+
+private:
+ size_t pos;
+ size_t size;
+ u_int8_t* buf;
+};
+
+class IOStream : public InputStream, public OutputStream {
+};
+
+template<bool managed>
+class LimitedInputStream : public InputStream {
+public:
+ LimitedInputStream(InputStream* is, int64_t aMaxBytes) : s(is), maxBytes(aMaxBytes) {
+ }
+ virtual ~LimitedInputStream() throw() { if(managed) delete s; }
+
+ size_t read(void* buf, size_t& len) throw(FileException) {
+ dcassert(maxBytes >= 0);
+ len = (size_t)min(maxBytes, (int64_t)len);
+ if(len == 0)
+ return 0;
+ size_t x = s->read(buf, len);
+ maxBytes -= x;
+ return x;
+ }
+
+private:
+ InputStream* s;
+ int64_t maxBytes;
+};
+
+template<bool managed>
+class BufferedOutputStream : public OutputStream {
+public:
+ using OutputStream::write;
+
+ BufferedOutputStream(OutputStream* aStream, size_t aBufSize = SETTING(BUFFER_SIZE) * 1024) : s(aStream), pos(0), bufSize(aBufSize), buf(aBufSize) { }
+ virtual ~BufferedOutputStream() throw() {
+ try {
+ // We must do this in order not to lose bytes when a download
+ // is disconnected prematurely
+ flush();
+ } catch(const Exception&) {
+ }
+ if(managed) delete s;
+ }
+
+ virtual size_t flush() throw(Exception) {
+ if(pos > 0)
+ s->write(buf, pos);
+ pos = 0;
+ s->flush();
+ return 0;
+ }
+
+ virtual size_t write(const void* wbuf, size_t len) throw(Exception) {
+ u_int8_t* b = (u_int8_t*)wbuf;
+ size_t l2 = len;
+ while(len > 0) {
+ if(pos == 0 && len >= bufSize) {
+ s->write(b, len);
+ break;
+ } else {
+ size_t n = min(bufSize - pos, len);
+ memcpy(buf + pos, b, n);
+ b += n;
+ pos += n;
+ len -= n;
+ if(pos == bufSize) {
+ s->write(buf, bufSize);
+ pos = 0;
+ }
+ }
+ }
+ return l2;
+ }
+private:
+ OutputStream* s;
+ size_t pos;
+ size_t bufSize;
+ AutoArray<u_int8_t> buf;
+};
+
+class StringOutputStream : public OutputStream {
+public:
+ StringOutputStream(string& out) : str(out) { }
+ virtual ~StringOutputStream() throw() { }
+ using OutputStream::write;
+
+ virtual size_t flush() throw(Exception) { return 0; }
+ virtual size_t write(const void* buf, size_t len) throw(Exception) {
+ str.append((char*)buf, len);
+ return len;
+ }
+private:
+ string& str;
+};
+
+
+#endif // STREAMS_H
+
+/**
+* @file
+* $Id: Streams.h,v 1.2 2005/08/21 14:03:43 olof Exp $
+*/
diff --git a/dcpp/StringDefs.cpp b/dcpp/StringDefs.cpp
new file mode 100644
index 0000000..83cafe0
--- /dev/null
+++ b/dcpp/StringDefs.cpp
@@ -0,0 +1,1121 @@
+#include "stdinc.h"
+#include "DCPlusPlus.h"
+#include "ResourceManager.h"
+string ResourceManager::strings[] = {
+"Active",
+"Enabled / Search String",
+"&Add",
+"Add To Favorites",
+"Added",
+"Address already in use",
+"Address not available",
+"Automatic Directory Listing Search",
+"Destination Directory",
+"Discard",
+"Download Matches",
+"Enabled",
+"Full Path",
+"ADLSearch Properties",
+"Search String",
+"Max FileSize",
+"Min FileSize",
+"Search Type",
+"Size Type",
+"All download slots taken",
+"All %d users offline",
+"All 3 users offline",
+"All 4 users offline",
+"All",
+"Any",
+"At least",
+"At most",
+"Audio",
+"Auto connect / Name",
+"Auto grant slot / Nick",
+"Average/s: ",
+"AWAY",
+"Away mode off",
+"Away mode on: ",
+"B",
+"Ban user(s)",
+"0/1",
+"Both users offline",
+"Browse...",
+"&Browse...",
+"Browse file list",
+"Choose folder",
+"Close",
+"Close connection",
+"Closing connection...",
+"Compressed",
+"Error during compression",
+"&Configure",
+"&Connect",
+"Connected",
+"Connecting...",
+"Connecting (forced)...",
+"Connecting to ",
+"Connection",
+"Connection closed",
+"Connection refused by target machine",
+"Connection reset by server",
+"Connection timeout",
+"Configured Public Hub Lists",
+"Copy address to clipboard",
+"Copy magnet link to clipboard",
+"Copy nick to clipboard",
+"Could not open target file: ",
+"Count",
+"Country",
+"CRC Checked",
+"Error during decompression",
+"Description",
+"Destination",
+"Directory",
+"Directory already shared",
+"Directory or directory name already exists",
+"Disk full(?)",
+"Disconnect user(s)",
+"Disconnected",
+"Disconnected user leaving the hub: ",
+"Document",
+"Done",
+"Don't remove /password before your password",
+"The temporary download directory cannot be shared",
+"Download",
+"Download failed: ",
+"Download finished, idle...",
+"Download Queue",
+"Download starting...",
+"Download to...",
+"Download whole directory",
+"Download whole directory to...",
+"Downloaded",
+"Downloaded %s (%.01f%%) in %s",
+" downloaded from ",
+"Downloading...",
+"Downloading public hub list...",
+"Downloading list...",
+"Downloads",
+"Duplicate file will not be shared: ",
+"Dupe matched against: ",
+"Duplicate source",
+"Edit",
+"&Edit",
+"E-Mail",
+"Please enter a nickname in the settings dialog!",
+"Please enter a password",
+"Please enter a reason",
+"Enter search string",
+"Please enter a destination server",
+"Errors",
+"Exact size",
+"Executable",
+"Join/part of favorite users showing off",
+"Join/part of favorite users showing on",
+"Favorite name",
+"Under what name you see the directory",
+"Favorite hub added",
+"Identification (leave blank for defaults)",
+"Favorite Hub Properties",
+"Favorite Hubs",
+"Favorite user added",
+"Favorite Users",
+"File",
+"Files",
+"File list refresh finished",
+"File list refresh initiated",
+"File not available",
+"File type",
+"A file with a different size already exists in the queue",
+"A file with diffent tth root already exists in the queue",
+"Filename",
+"files left",
+"files/h",
+"F&ilter",
+"Filtered",
+"Find",
+"Finished Downloads",
+"Finished Uploads",
+"File with '$' cannot be downloaded and will not be shared: ",
+"Force attempt",
+"GiB",
+"Get file list",
+"Go to directory",
+"Grant extra slot",
+"Hash database",
+"Creating file index...",
+"Run in background",
+"Statistics",
+"Please wait while DC++ indexes your files (they won't be shared until they've been indexed)...",
+"Hash database rebuilt",
+"Finished hashing: ",
+"High",
+"Highest",
+"Hit Ratio: ",
+"Hits: ",
+"Host unreachable",
+"Hub",
+"Hubs",
+"Address",
+"Hub list downloaded...",
+"Name",
+"Hub password",
+"Users",
+"Ignore TTH searches",
+"Ignored message: ",
+"Invalid number of slots",
+"Invalid target file (missing directory, check default download directory setting)",
+"Downloaded tree does not match TTH root",
+"IP: ",
+"IP",
+"Items",
+"Join/part showing off",
+"Join/part showing on",
+"Joins: ",
+"KiB",
+"KiB/s",
+"Kick user(s)",
+"A file of equal or larger size already exists at the target location",
+"Last change: ",
+"Hub (last seen on if offline)",
+"Time last seen",
+"left",
+"Loading DC++, please wait...",
+"Lookup TTH at Bitzi.com",
+"Low",
+"Lowest",
+"Filename:",
+"File Hash:",
+"Do nothing",
+"Add this file to your download queue",
+"Do the same action next time without asking",
+"Start a search for this file",
+"A MAGNET link was given to DC++, but it didn't contain a valid file hash for use on the Direct Connect network. No action will be taken.",
+"DC++ has detected a MAGNET link with a file hash that can be searched for on the Direct Connect network. What would you like to do?",
+"MAGNET Link detected",
+"Download files from the Direct Connect network",
+"DC++",
+"URL:MAGNET URI",
+"Match queue",
+"Matched %d file(s)",
+"Max Hubs",
+"Max Users",
+"MiB",
+"MiB/s",
+"About DC++...",
+"ADL Search",
+"Arrange icons",
+"Cascade",
+"Change Log",
+"Close disconnected",
+"Help &Contents\tF1",
+"DC++ discussion forum",
+"Donate ���/$$$ (paypal)",
+"&Download Queue\tCtrl+D",
+"&Exit",
+"Frequently asked questions",
+"&Favorite Hubs\tCtrl+F",
+"Favorite &Users\tCtrl+U",
+"&File",
+"Follow last redirec&t\tCtrl+T",
+"Indexing progress",
+"&Help",
+"Downloads",
+"GeoIP database update",
+"Help forum",
+"Translations",
+"DC++ Homepage",
+"Horizontal Tile",
+"Minimize &All",
+"Restore All",
+"Network Statistics",
+"&Notepad\tCtrl+N",
+"Open downloads directory",
+"Open file list...\tCtrl+L",
+"Open own list",
+"&Public Hubs\tCtrl+P",
+"&Quick Connect ...\tCtrl+Q",
+"Readme / Newbie help",
+"&Reconnect\tCtrl+R",
+"Refresh file list\tCtrl+E",
+"Report a bug",
+"Request a feature",
+"&Search\tCtrl+S",
+"Search Spy",
+"Settings...",
+"Show",
+"&Status bar\tCtrl+2",
+"&Toolbar\tCtrl+1",
+"T&ransfers\tCtrl+3",
+"Vertical Tile",
+"&View",
+"&Window",
+"Min Share",
+"Min Slots",
+"Move/Rename",
+"Move &Down",
+"Move &Up",
+"Network Statistics",
+"Network unreachable (are you connected to the internet?)",
+"&New...",
+"Next",
+"Nick",
+"Your nick was already taken, please change to something else!",
+" (Nick unknown)",
+"No",
+"No directory specified",
+"You're trying to download from yourself!",
+"No errors",
+"No matches",
+"No slots available",
+"No users",
+"No users to download from",
+"Non-blocking operation still in progress",
+"Normal",
+"Not connected",
+"Not a socket",
+"Notepad",
+"Offline",
+"Online",
+"Only users with free slots",
+"Only results with TTH root",
+"Only where I'm op",
+"Open",
+"Open download page?",
+"Open folder",
+"Operating system does not match minimum requirements for feature",
+"Operation would block execution",
+"Out of buffer space",
+"Parts: ",
+"Passive user",
+"Password",
+"Path",
+"Paused",
+"Permission denied",
+"PiB",
+"Picture",
+"Port: ",
+"Port %d is busy, please choose another one in the settings dialog, or disable any other application that might be using it and restart DC++",
+"Preparing file list...",
+"Press the follow redirect button to connect to ",
+"Priority",
+"Private message from ",
+"&Properties",
+"Public Hubs",
+"Purge",
+"Quick Connect",
+"Rating",
+"Ratio",
+"Re-add source",
+"Really exit?",
+"Really remove?",
+"Redirect",
+"Redirect request received to a hub that's already connected",
+"Redirect user(s)",
+"&Refresh",
+"Refresh user list",
+"Reliability",
+"&Remove",
+"Remove all",
+"Remove all subdirectories before adding this one",
+"Remove user from queue",
+"Remove source",
+"Rollback inconsistency, existing file does not match the one being downloaded",
+"Running...",
+"Search",
+"Search for",
+"Search for alternates",
+"Search for file",
+"Search options",
+"Search spam detected from ",
+"Search Spy",
+"Search String",
+"Searching for ",
+"Ready to search...",
+"Searching too soon, next search in %i seconds",
+"Request to seek beyond the end of data",
+"Send private message",
+"Separator",
+"Server",
+"Set priority",
+"Settings",
+"Add finished files to share instantly (if shared)",
+"&Add folder",
+"Break on first ADLSearch match",
+"Advanced",
+"Advanced\\Experts only",
+"Advanced resume using TTH",
+"Advanced settings",
+"Use antifragmentation method for downloads",
+"Appearance",
+"Colors and sounds",
+"Auto-away on minimize (and back on restore)",
+"Automatically follow redirects",
+"Automatically disconnect users who leave the hub",
+"Automatically search for alternative download locations",
+"Automatically match queue for auto search hits",
+"Automatically refresh share list every hour",
+"Auto-open at startup",
+"Bind address",
+"&Change",
+"Clear search box after each search",
+"Colors",
+"Command",
+"Enable safe and compressed transfers",
+"Configure Public Hub Lists",
+"Confirm application exit",
+"Confirm favorite hub removal",
+"Confirm item removal in download queue",
+"Connection Settings (see the help file if unsure)",
+"Connection Type",
+"Default away message",
+"Directories",
+"Don't download files already in share",
+"Default download directory",
+"Limits",
+"Downloads",
+"Maximum simultaneous downloads (0 = infinite)",
+"No new downloads if speed exceeds (KiB/s, 0 = disable)",
+"Donate €€€:s! (ok, dirty dollars are fine as well =) (see help menu)",
+"Only show joins / parts for favorite users",
+"Downloads\\Favorites",
+"Favorite download directories",
+"Filename",
+"Filter kick and NMDC debug messages",
+"Set Finished Manager(s) tab bold when an entry is added",
+"Format",
+"Personal information ",
+"Get User Country",
+"Accept custom user commands from hub",
+"Ignore private messages from offline users",
+"IP",
+"Don't delete file lists when exiting",
+"Language file",
+"Keep duplicate files in your file list",
+"Log downloads",
+"Log filelist transfers",
+"Log main chat",
+"Log private chat",
+"Log status messages",
+"Log system messages",
+"Log uploads",
+"Logging",
+"Logs",
+"Ask what to do when a magnet link is detected.",
+"Max hash speed",
+"Max tab rows",
+"Minimize to tray",
+"Name",
+"Connection settings",
+"Don't send the away message to bots",
+"Note; Files appear in the share only after they've been hashed!",
+"Search for files with TTH root only as standard",
+"Open new window when using /join",
+"Always open help file with this dialog",
+"Options",
+"Passive",
+"Personal Information",
+"Make an annoying sound every time a private message is received",
+"Make an annoying sound when a private message window is opened",
+"PM history",
+"Open new file list windows in the background",
+"Open new private message windows in the background",
+"Open private messages from offline users in their own window",
+"Open private messages in their own window",
+"Public Hubs list",
+"HTTP Proxy (for hublist only)",
+"Public Hubs list URL",
+"Set Download Queue tab bold when contents change",
+"Downloads\\Queue",
+"Rename",
+"Note; most of these options require that you restart DC++",
+"Rollback",
+"Search history",
+"Select &text style",
+"Select &window color",
+"Send unknown /commands to the hub",
+"Enable automatic SFV checking",
+"Share hidden files",
+"Total size:",
+"Shared directories",
+"Show joins / parts in chat by default",
+"Show progress bars for transfers (uses some CPU)",
+"Skip zero-byte files",
+"Use small send buffer (enable if uploads slow downloads a lot)",
+"SOCKS5",
+"Socks IP",
+"Port",
+"Use SOCKS5 server to resolve hostnames",
+"Username",
+"Sounds",
+"Note; because of changing download speeds, this is not 100% accurate...",
+"View status messages in main chat",
+"Tab completion of nicks in chat",
+"Set hub/PM/Search tab bold when contents change",
+"TCP Port",
+"Mini slot size",
+"Highest prio size",
+"High prio size",
+"Normal prio size",
+"Low prio size",
+"Lowest prio enabled",
+"Show timestamps in chat by default",
+"Set timestamps",
+"Toggle window when selecting an active tab",
+"UDP Port",
+"Unfinished downloads directory",
+"Sharing",
+"Automatically open an extra slot if speed is below (0 = disable)",
+"Upload slots",
+"Register with Windows to handle dchub:// and adc:// URL links",
+"Register with Windows to handle magnet: URI links",
+"Use CTRL for line history",
+"Use OEM monospaced font for viewing text files",
+"Use system icons when browsing files (slows browsing down a bit)",
+"Use UPnP Control",
+"Advanced\\User Commands",
+"Windows",
+"Window options",
+"Write buffer size",
+"CRC32 inconsistency (SFV-Check)",
+"Shared",
+"Shared Files",
+"Size",
+"Max Size",
+"Min Size",
+"New virtual name matches old name, skipping...",
+"Slot granted",
+"Slots",
+"Slots set",
+"Socket has been shut down",
+"Socks server authentication failed (bad username / password?)",
+"The socks server doesn't support user / password authentication",
+"The socks server failed establish a connection",
+"The socks server requires authentication",
+"Failed to set up the socks server for UDP relay (check socks address and port)",
+"Source Type",
+"Specify a search string",
+"Specify a server to connect to",
+"Speed",
+"Status",
+"Stored password sent...",
+"Tag",
+"Target filename too long",
+"TiB",
+"Time",
+"Time left",
+"Timestamps disabled",
+"Timestamps enabled",
+"More data was sent than was expected",
+"Total: ",
+"A file with the same hash already exists in your share",
+"TTH inconsistency",
+"TTH Root",
+"Type",
+"Unable to create thread",
+"Unknown",
+"Unknown address",
+"Unknown command: ",
+"Unknown error: 0x%x",
+"Unsupported filelist format",
+"Upload finished, idle...",
+"Upload starting...",
+"Uploaded %s (%.01f%%) in %s",
+" uploaded to ",
+"Uploads",
+"Failed to create port mappings. Please set up your NAT yourself.",
+"Failed to remove port mappings",
+"Failed to get external IP via UPnP. Please set it yourself.",
+"User",
+"Chat",
+"Command",
+"Context",
+"Filelist Menu",
+"Hub IP / DNS (empty = all, 'op' = where operator)",
+"Hub Menu",
+"Send once per nick",
+"Parameters",
+"PM",
+"Text sent to hub",
+"Raw",
+"Search Menu",
+"To",
+"Command Type",
+"User Menu",
+"Create / Modify Command",
+"User Description",
+"User offline",
+"User went offline",
+"Users",
+"Video",
+"View as text",
+"Virtual name",
+"Virtual directory name already exists",
+"Name under which the others see the directory",
+"Waiting...",
+"Waiting to retry...",
+"Waiting (User online)",
+"Waiting (%d of %d users online)",
+"Yes",
+"You are being redirected to ",
+};
+string ResourceManager::names[] = {
+"Active",
+"ActiveSearchString",
+"Add",
+"AddToFavorites",
+"Added",
+"AddressAlreadyInUse",
+"AddressNotAvailable",
+"AdlSearch",
+"AdlsDestination",
+"AdlsDiscard",
+"AdlsDownload",
+"AdlsEnabled",
+"AdlsFullPath",
+"AdlsProperties",
+"AdlsSearchString",
+"AdlsSizeMax",
+"AdlsSizeMin",
+"AdlsType",
+"AdlsUnits",
+"AllDownloadSlotsTaken",
+"AllUsersOffline",
+"All3UsersOffline",
+"All4UsersOffline",
+"All",
+"Any",
+"AtLeast",
+"AtMost",
+"Audio",
+"AutoConnect",
+"AutoGrant",
+"Average",
+"Away",
+"AwayModeOff",
+"AwayModeOn",
+"B",
+"BanUser",
+"Bool",
+"BothUsersOffline",
+"Browse",
+"BrowseAccel",
+"BrowseFileList",
+"ChooseFolder",
+"Close",
+"CloseConnection",
+"ClosingConnection",
+"Compressed",
+"CompressionError",
+"Configure",
+"Connect",
+"Connected",
+"Connecting",
+"ConnectingForced",
+"ConnectingTo",
+"Connection",
+"ConnectionClosed",
+"ConnectionRefused",
+"ConnectionReset",
+"ConnectionTimeout",
+"ConfiguredHubLists",
+"CopyHub",
+"CopyMagnet",
+"CopyNick",
+"CouldNotOpenTargetFile",
+"Count",
+"Country",
+"CrcChecked",
+"DecompressionError",
+"Description",
+"Destination",
+"Directory",
+"DirectoryAlreadyShared",
+"DirectoryAddError",
+"DiscFull",
+"DisconnectUser",
+"Disconnected",
+"DisconnectedUser",
+"Document",
+"Done",
+"DontRemoveSlashPassword",
+"DontShareTempDirectory",
+"Download",
+"DownloadFailed",
+"DownloadFinishedIdle",
+"DownloadQueue",
+"DownloadStarting",
+"DownloadTo",
+"DownloadWholeDir",
+"DownloadWholeDirTo",
+"Downloaded",
+"DownloadedBytes",
+"DownloadedFrom",
+"Downloading",
+"DownloadingHubList",
+"DownloadingList",
+"Downloads",
+"DuplicateFileNotShared",
+"DuplicateMatch",
+"DuplicateSource",
+"Edit",
+"EditAccel",
+"Email",
+"EnterNick",
+"EnterPassword",
+"EnterReason",
+"EnterSearchString",
+"EnterServer",
+"Errors",
+"ExactSize",
+"Executable",
+"FavJoinShowingOff",
+"FavJoinShowingOn",
+"FavoriteDirName",
+"FavoriteDirNameLong",
+"FavoriteHubAdded",
+"FavoriteHubIdentity",
+"FavoriteHubProperties",
+"FavoriteHubs",
+"FavoriteUserAdded",
+"FavoriteUsers",
+"File",
+"Files",
+"FileListRefreshFinished",
+"FileListRefreshInitiated",
+"FileNotAvailable",
+"FileType",
+"FileWithDifferentSize",
+"FileWithDifferentTth",
+"Filename",
+"FilesLeft",
+"FilesPerHour",
+"Filter",
+"Filtered",
+"Find",
+"FinishedDownloads",
+"FinishedUploads",
+"ForbiddenDollarFile",
+"ForceAttempt",
+"Gib",
+"GetFileList",
+"GoToDirectory",
+"GrantExtraSlot",
+"HashDatabase",
+"HashProgress",
+"HashProgressBackground",
+"HashProgressStats",
+"HashProgressText",
+"HashRebuilt",
+"HashingFinished",
+"High",
+"Highest",
+"HitRatio",
+"Hits",
+"HostUnreachable",
+"Hub",
+"Hubs",
+"HubAddress",
+"HubListDownloaded",
+"HubName",
+"HubPassword",
+"HubUsers",
+"IgnoreTthSearches",
+"IgnoredMessage",
+"InvalidNumberOfSlots",
+"InvalidTargetFile",
+"InvalidTree",
+"Ip",
+"IpBare",
+"Items",
+"JoinShowingOff",
+"JoinShowingOn",
+"Joins",
+"Kib",
+"Kibps",
+"KickUser",
+"LargerTargetFileExists",
+"LastChange",
+"LastHub",
+"LastSeen",
+"Left",
+"Loading",
+"LookupAtBitzi",
+"Low",
+"Lowest",
+"MagnetDlgFile",
+"MagnetDlgHash",
+"MagnetDlgNothing",
+"MagnetDlgQueue",
+"MagnetDlgRemember",
+"MagnetDlgSearch",
+"MagnetDlgTextBad",
+"MagnetDlgTextGood",
+"MagnetDlgTitle",
+"MagnetHandlerDesc",
+"MagnetHandlerRoot",
+"MagnetShellDesc",
+"MatchQueue",
+"MatchedFiles",
+"MaxHubs",
+"MaxUsers",
+"Mib",
+"Mibps",
+"MenuAbout",
+"MenuAdlSearch",
+"MenuArrange",
+"MenuCascade",
+"MenuChangelog",
+"MenuCloseDisconnected",
+"MenuContents",
+"MenuDiscuss",
+"MenuDonate",
+"MenuDownloadQueue",
+"MenuExit",
+"MenuFaq",
+"MenuFavoriteHubs",
+"MenuFavoriteUsers",
+"MenuFile",
+"MenuFollowRedirect",
+"MenuHashProgress",
+"MenuHelp",
+"MenuHelpDownloads",
+"MenuHelpGeoipfile",
+"MenuHelpForum",
+"MenuHelpTranslations",
+"MenuHomepage",
+"MenuHorizontalTile",
+"MenuMinimizeAll",
+"MenuRestoreAll",
+"MenuNetworkStatistics",
+"MenuNotepad",
+"MenuOpenDownloadsDir",
+"MenuOpenFileList",
+"MenuOpenOwnList",
+"MenuPublicHubs",
+"MenuQuickConnect",
+"MenuReadme",
+"MenuReconnect",
+"MenuRefreshFileList",
+"MenuReportBug",
+"MenuRequestFeature",
+"MenuSearch",
+"MenuSearchSpy",
+"MenuSettings",
+"MenuShow",
+"MenuStatusBar",
+"MenuToolbar",
+"MenuTransferView",
+"MenuVerticalTile",
+"MenuView",
+"MenuWindow",
+"MinShare",
+"MinSlots",
+"Move",
+"MoveDown",
+"MoveUp",
+"NetworkStatistics",
+"NetworkUnreachable",
+"New",
+"Next",
+"Nick",
+"NickTaken",
+"NickUnknown",
+"NoStr",
+"NoDirectorySpecified",
+"NoDownloadsFromSelf",
+"NoErrors",
+"NoMatches",
+"NoSlotsAvailable",
+"NoUsers",
+"NoUsersToDownloadFrom",
+"NonBlockingOperation",
+"Normal",
+"NotConnected",
+"NotSocket",
+"Notepad",
+"Offline",
+"Online",
+"OnlyFreeSlots",
+"OnlyTth",
+"OnlyWhereOp",
+"Open",
+"OpenDownloadPage",
+"OpenFolder",
+"OperatingSystemNotCompatible",
+"OperationWouldBlockExecution",
+"OutOfBufferSpace",
+"Parts",
+"PassiveUser",
+"Password",
+"Path",
+"Paused",
+"PermissionDenied",
+"Pib",
+"Picture",
+"Port",
+"PortIsBusy",
+"PreparingFileList",
+"PressFollow",
+"Priority",
+"PrivateMessageFrom",
+"Properties",
+"PublicHubs",
+"Purge",
+"QuickConnect",
+"Rating",
+"Ratio",
+"ReaddSource",
+"ReallyExit",
+"ReallyRemove",
+"Redirect",
+"RedirectAlreadyConnected",
+"RedirectUser",
+"Refresh",
+"RefreshUserList",
+"Reliability",
+"Remove",
+"RemoveAll",
+"RemoveAllSubdirectories",
+"RemoveFromAll",
+"RemoveSource",
+"RollbackInconsistency",
+"Running",
+"Search",
+"SearchFor",
+"SearchForAlternates",
+"SearchForFile",
+"SearchOptions",
+"SearchSpamFrom",
+"SearchSpy",
+"SearchString",
+"SearchingFor",
+"SearchingReady",
+"SearchingWait",
+"SeekBeyondEnd",
+"SendPrivateMessage",
+"Separator",
+"Server",
+"SetPriority",
+"Settings",
+"SettingsAddFinishedInstantly",
+"SettingsAddFolder",
+"SettingsAdlsBreakOnFirst",
+"SettingsAdvanced",
+"SettingsAdvanced3",
+"SettingsAdvancedResume",
+"SettingsAdvancedSettings",
+"SettingsAntiFrag",
+"SettingsAppearance",
+"SettingsAppearance2",
+"SettingsAutoAway",
+"SettingsAutoFollow",
+"SettingsAutoKick",
+"SettingsAutoSearch",
+"SettingsAutoSearchAutoMatch",
+"SettingsAutoUpdateList",
+"SettingsAutoOpen",
+"SettingsBindAddress",
+"SettingsChange",
+"SettingsClearSearch",
+"SettingsColors",
+"SettingsCommand",
+"SettingsCompressTransfers",
+"SettingsConfigureHubLists",
+"SettingsConfirmExit",
+"SettingsConfirmHubRemoval",
+"SettingsConfirmItemRemoval",
+"SettingsConnectionSettings",
+"SettingsConnectionType",
+"SettingsDefaultAwayMsg",
+"SettingsDirectories",
+"SettingsDontDlAlreadyShared",
+"SettingsDownloadDirectory",
+"SettingsDownloadLimits",
+"SettingsDownloads",
+"SettingsDownloadsMax",
+"SettingsDownloadsSpeedPause",
+"SettingsExampleText",
+"SettingsFavShowJoins",
+"SettingsFavoriteDirsPage",
+"SettingsFavoriteDirs",
+"SettingsFileName",
+"SettingsFilterMessages",
+"SettingsFinishedDirty",
+"SettingsFormat",
+"SettingsGeneral",
+"SettingsGetUserCountry",
+"SettingsHubUserCommands",
+"SettingsIgnoreOffline",
+"SettingsIp",
+"SettingsKeepLists",
+"SettingsLanguageFile",
+"SettingsListDupes",
+"SettingsLogDownloads",
+"SettingsLogFilelistTransfers",
+"SettingsLogMainChat",
+"SettingsLogPrivateChat",
+"SettingsLogStatusMessages",
+"SettingsLogSystemMessages",
+"SettingsLogUploads",
+"SettingsLogging",
+"SettingsLogs",
+"SettingsMagnetAsk",
+"SettingsMaxHashSpeed",
+"SettingsMaxTabRows",
+"SettingsMinimizeTray",
+"SettingsName",
+"SettingsNetwork",
+"SettingsNoAwaymsgToBots",
+"SettingsOnlyHashed",
+"SettingsOnlyTth",
+"SettingsOpenNewWindow",
+"SettingsOpenUserCmdHelp",
+"SettingsOptions",
+"SettingsPassive",
+"SettingsPersonalInformation",
+"SettingsPmBeep",
+"SettingsPmBeepOpen",
+"SettingsPmHistory",
+"SettingsPopunderFilelist",
+"SettingsPopunderPm",
+"SettingsPopupOffline",
+"SettingsPopupPms",
+"SettingsPublicHubList",
+"SettingsPublicHubListHttpProxy",
+"SettingsPublicHubListUrl",
+"SettingsQueueDirty",
+"SettingsQueue",
+"SettingsRenameFolder",
+"SettingsRequiresRestart",
+"SettingsRollback",
+"SettingsSearchHistory",
+"SettingsSelectTextFace",
+"SettingsSelectWindowColor",
+"SettingsSendUnknownCommands",
+"SettingsSfvCheck",
+"SettingsShareHidden",
+"SettingsShareSize",
+"SettingsSharedDirectories",
+"SettingsShowJoins",
+"SettingsShowProgressBars",
+"SettingsSkipZeroByte",
+"SettingsSmallSendBuffer",
+"SettingsSocks5",
+"SettingsSocks5Ip",
+"SettingsSocks5Port",
+"SettingsSocks5Resolve",
+"SettingsSocks5Username",
+"SettingsSounds",
+"SettingsSpeedsNotAccurate",
+"SettingsStatusInChat",
+"SettingsTabCompletion",
+"SettingsTabDirty",
+"SettingsTcpPort",
+"SettingsTextMinislot",
+"SettingsPrioHighest",
+"SettingsPrioHigh",
+"SettingsPrioNormal",
+"SettingsPrioLow",
+"SettingsPrioLowest",
+"SettingsTimeStamps",
+"SettingsTimeStampsFormat",
+"SettingsToggleActiveWindow",
+"SettingsUdpPort",
+"SettingsUnfinishedDownloadDirectory",
+"SettingsUploads",
+"SettingsUploadsMinSpeed",
+"SettingsUploadsSlots",
+"SettingsUrlHandler",
+"SettingsUrlMagnet",
+"SettingsUseCtrlForLineHistory",
+"SettingsUseOemMonofont",
+"SettingsUseSystemIcons",
+"SettingsUseUpnp",
+"SettingsUserCommands",
+"SettingsWindows",
+"SettingsWindowsOptions",
+"SettingsWriteBuffer",
+"SfvInconsistency",
+"Shared",
+"SharedFiles",
+"Size",
+"SizeMax",
+"SizeMin",
+"SkipRename",
+"SlotGranted",
+"Slots",
+"SlotsSet",
+"SocketShutDown",
+"SocksAuthFailed",
+"SocksAuthUnsupported",
+"SocksFailed",
+"SocksNeedsAuth",
+"SocksSetupError",
+"SourceType",
+"SpecifySearchString",
+"SpecifyServer",
+"Speed",
+"Status",
+"StoredPasswordSent",
+"Tag",
+"TargetFilenameTooLong",
+"Tib",
+"Time",
+"TimeLeft",
+"TimestampsDisabled",
+"TimestampsEnabled",
+"TooMuchData",
+"Total",
+"TthAlreadyShared",
+"TthInconsistency",
+"TthRoot",
+"Type",
+"UnableToCreateThread",
+"Unknown",
+"UnknownAddress",
+"UnknownCommand",
+"UnknownError",
+"UnsupportedFilelistFormat",
+"UploadFinishedIdle",
+"UploadStarting",
+"UploadedBytes",
+"UploadedTo",
+"Uploads",
+"UpnpFailedToCreateMappings",
+"UpnpFailedToRemoveMappings",
+"UpnpFailedToGetExternalIp",
+"User",
+"UserCmdChat",
+"UserCmdCommand",
+"UserCmdContext",
+"UserCmdFilelistMenu",
+"UserCmdHub",
+"UserCmdHubMenu",
+"UserCmdOnce",
+"UserCmdParameters",
+"UserCmdPm",
+"UserCmdPreview",
+"UserCmdRaw",
+"UserCmdSearchMenu",
+"UserCmdTo",
+"UserCmdType",
+"UserCmdUserMenu",
+"UserCmdWindow",
+"UserDescription",
+"UserOffline",
+"UserWentOffline",
+"Users",
+"Video",
+"ViewAsText",
+"VirtualName",
+"VirtualNameExists",
+"VirtualNameLong",
+"Waiting",
+"WaitingToRetry",
+"WaitingUserOnline",
+"WaitingUsersOnline",
+"YesStr",
+"YouAreBeingRedirected",
+};
diff --git a/dcpp/StringDefs.h b/dcpp/StringDefs.h
new file mode 100644
index 0000000..fb9b0dc
--- /dev/null
+++ b/dcpp/StringDefs.h
@@ -0,0 +1,566 @@
+// @Prolog: #include "stdinc.h"
+// @Prolog: #include "DCPlusPlus.h"
+// @Prolog: #include "ResourceManager.h"
+// @Strings: string ResourceManager::strings[]
+// @Names: string ResourceManager::names[]
+
+enum Strings { // @DontAdd
+ ACTIVE, // "Active"
+ ACTIVE_SEARCH_STRING, // "Enabled / Search String"
+ ADD, // "&Add"
+ ADD_TO_FAVORITES, // "Add To Favorites"
+ ADDED, // "Added"
+ ADDRESS_ALREADY_IN_USE, // "Address already in use"
+ ADDRESS_NOT_AVAILABLE, // "Address not available"
+ ADL_SEARCH, // "Automatic Directory Listing Search"
+ ADLS_DESTINATION, // "Destination Directory"
+ ADLS_DISCARD, // "Discard"
+ ADLS_DOWNLOAD, // "Download Matches"
+ ADLS_ENABLED, // "Enabled"
+ ADLS_FULL_PATH, // "Full Path"
+ ADLS_PROPERTIES, // "ADLSearch Properties"
+ ADLS_SEARCH_STRING, // "Search String"
+ ADLS_SIZE_MAX, // "Max FileSize"
+ ADLS_SIZE_MIN, // "Min FileSize"
+ ADLS_TYPE, // "Search Type"
+ ADLS_UNITS, // "Size Type"
+ ALL_DOWNLOAD_SLOTS_TAKEN, // "All download slots taken"
+ ALL_USERS_OFFLINE, // "All %d users offline"
+ ALL_3_USERS_OFFLINE, // "All 3 users offline"
+ ALL_4_USERS_OFFLINE, // "All 4 users offline"
+ ALL, // "All"
+ ANY, // "Any"
+ AT_LEAST, // "At least"
+ AT_MOST, // "At most"
+ AUDIO, // "Audio"
+ AUTO_CONNECT, // "Auto connect / Name"
+ AUTO_GRANT, // "Auto grant slot / Nick"
+ AVERAGE, // "Average/s: "
+ AWAY, // "AWAY"
+ AWAY_MODE_OFF, // "Away mode off"
+ AWAY_MODE_ON, // "Away mode on: "
+ B, // "B"
+ BAN_USER, // "Ban user(s)"
+ BOOL, // "0/1"
+ BOTH_USERS_OFFLINE, // "Both users offline"
+ BROWSE, // "Browse..."
+ BROWSE_ACCEL, // "&Browse..."
+ BROWSE_FILE_LIST, // "Browse file list"
+ CHOOSE_FOLDER, // "Choose folder"
+ CLOSE, // "Close"
+ CLOSE_CONNECTION, // "Close connection"
+ CLOSING_CONNECTION, // "Closing connection..."
+ COMPRESSED, // "Compressed"
+ COMPRESSION_ERROR, // "Error during compression"
+ CONFIGURE, // "&Configure"
+ CONNECT, // "&Connect"
+ CONNECTED, // "Connected"
+ CONNECTING, // "Connecting..."
+ CONNECTING_FORCED, // "Connecting (forced)..."
+ CONNECTING_TO, // "Connecting to "
+ CONNECTION, // "Connection"
+ CONNECTION_CLOSED, // "Connection closed"
+ CONNECTION_REFUSED, // "Connection refused by target machine"
+ CONNECTION_RESET, // "Connection reset by server"
+ CONNECTION_TIMEOUT, // "Connection timeout"
+ CONFIGURED_HUB_LISTS, // "Configured Public Hub Lists"
+ COPY_HUB, // "Copy address to clipboard"
+ COPY_MAGNET, // "Copy magnet link to clipboard"
+ COPY_NICK, // "Copy nick to clipboard"
+ COULD_NOT_OPEN_TARGET_FILE, // "Could not open target file: "
+ COUNT, // "Count"
+ COUNTRY, // "Country"
+ CRC_CHECKED, // "CRC Checked"
+ DECOMPRESSION_ERROR, // "Error during decompression"
+ DESCRIPTION, // "Description"
+ DESTINATION, // "Destination"
+ DIRECTORY, // "Directory"
+ DIRECTORY_ALREADY_SHARED, // "Directory already shared"
+ DIRECTORY_ADD_ERROR, // "Directory or directory name already exists"
+ DISC_FULL, // "Disk full(?)"
+ DISCONNECT_USER, // "Disconnect user(s)"
+ DISCONNECTED, // "Disconnected"
+ DISCONNECTED_USER, // "Disconnected user leaving the hub: "
+ DOCUMENT, // "Document"
+ DONE, // "Done"
+ DONT_REMOVE_SLASH_PASSWORD, // "Don't remove /password before your password"
+ DONT_SHARE_TEMP_DIRECTORY, // "The temporary download directory cannot be shared"
+ DOWNLOAD, // "Download"
+ DOWNLOAD_FAILED, // "Download failed: "
+ DOWNLOAD_FINISHED_IDLE, // "Download finished, idle..."
+ DOWNLOAD_QUEUE, // "Download Queue"
+ DOWNLOAD_STARTING, // "Download starting..."
+ DOWNLOAD_TO, // "Download to..."
+ DOWNLOAD_WHOLE_DIR, // "Download whole directory"
+ DOWNLOAD_WHOLE_DIR_TO, // "Download whole directory to..."
+ DOWNLOADED, // "Downloaded"
+ DOWNLOADED_BYTES, // "Downloaded %s (%.01f%%) in %s"
+ DOWNLOADED_FROM, // " downloaded from "
+ DOWNLOADING, // "Downloading..."
+ DOWNLOADING_HUB_LIST, // "Downloading public hub list..."
+ DOWNLOADING_LIST, // "Downloading list..."
+ DOWNLOADS, // "Downloads"
+ DUPLICATE_FILE_NOT_SHARED, // "Duplicate file will not be shared: "
+ DUPLICATE_MATCH, // "Dupe matched against: "
+ DUPLICATE_SOURCE, // "Duplicate source"
+ EDIT, // "Edit"
+ EDIT_ACCEL, // "&Edit"
+ EMAIL, // "E-Mail"
+ ENTER_NICK, // "Please enter a nickname in the settings dialog!"
+ ENTER_PASSWORD, // "Please enter a password"
+ ENTER_REASON, // "Please enter a reason"
+ ENTER_SEARCH_STRING, // "Enter search string"
+ ENTER_SERVER, // "Please enter a destination server"
+ ERRORS, // "Errors"
+ EXACT_SIZE, // "Exact size"
+ EXECUTABLE, // "Executable"
+ FAV_JOIN_SHOWING_OFF, // "Join/part of favorite users showing off"
+ FAV_JOIN_SHOWING_ON, // "Join/part of favorite users showing on"
+ FAVORITE_DIR_NAME, // "Favorite name"
+ FAVORITE_DIR_NAME_LONG, // "Under what name you see the directory"
+ FAVORITE_HUB_ADDED, // "Favorite hub added"
+ FAVORITE_HUB_IDENTITY, // "Identification (leave blank for defaults)"
+ FAVORITE_HUB_PROPERTIES, // "Favorite Hub Properties"
+ FAVORITE_HUBS, // "Favorite Hubs"
+ FAVORITE_USER_ADDED, // "Favorite user added"
+ FAVORITE_USERS, // "Favorite Users"
+ FILE, // "File"
+ FILES, // "Files"
+ FILE_LIST_REFRESH_FINISHED, // "File list refresh finished"
+ FILE_LIST_REFRESH_INITIATED, // "File list refresh initiated"
+ FILE_NOT_AVAILABLE, // "File not available"
+ FILE_TYPE, // "File type"
+ FILE_WITH_DIFFERENT_SIZE, // "A file with a different size already exists in the queue"
+ FILE_WITH_DIFFERENT_TTH, // "A file with diffent tth root already exists in the queue"
+ FILENAME, // "Filename"
+ FILES_LEFT, // "files left"
+ FILES_PER_HOUR, // "files/h"
+ FILTER, // "F&ilter"
+ FILTERED, // "Filtered"
+ FIND, // "Find"
+ FINISHED_DOWNLOADS, // "Finished Downloads"
+ FINISHED_UPLOADS, // "Finished Uploads"
+ FORBIDDEN_DOLLAR_FILE, // "File with '$' cannot be downloaded and will not be shared: "
+ FORCE_ATTEMPT, // "Force attempt"
+ GiB, // "GiB"
+ GET_FILE_LIST, // "Get file list"
+ GO_TO_DIRECTORY, // "Go to directory"
+ GRANT_EXTRA_SLOT, // "Grant extra slot"
+ HASH_DATABASE, // "Hash database"
+ HASH_PROGRESS, // "Creating file index..."
+ HASH_PROGRESS_BACKGROUND, // "Run in background"
+ HASH_PROGRESS_STATS, // "Statistics"
+ HASH_PROGRESS_TEXT, // "Please wait while DC++ indexes your files (they won't be shared until they've been indexed)..."
+ HASH_REBUILT, // "Hash database rebuilt"
+ HASHING_FINISHED, // "Finished hashing: "
+ HIGH, // "High"
+ HIGHEST, // "Highest"
+ HIT_RATIO, // "Hit Ratio: "
+ HITS, // "Hits: "
+ HOST_UNREACHABLE, // "Host unreachable"
+ HUB, // "Hub"
+ HUBS, // "Hubs"
+ HUB_ADDRESS, // "Address"
+ HUB_LIST_DOWNLOADED, // "Hub list downloaded..."
+ HUB_NAME, // "Name"
+ HUB_PASSWORD, // "Hub password"
+ HUB_USERS, // "Users"
+ IGNORE_TTH_SEARCHES, // "Ignore TTH searches"
+ IGNORED_MESSAGE, // "Ignored message: "
+ INVALID_NUMBER_OF_SLOTS, // "Invalid number of slots"
+ INVALID_TARGET_FILE, // "Invalid target file (missing directory, check default download directory setting)"
+ INVALID_TREE, // "Downloaded tree does not match TTH root"
+ IP, // "IP: "
+ IP_BARE, // "IP"
+ ITEMS, // "Items"
+ JOIN_SHOWING_OFF, // "Join/part showing off"
+ JOIN_SHOWING_ON, // "Join/part showing on"
+ JOINS, // "Joins: "
+ KiB, // "KiB"
+ KiBPS, // "KiB/s"
+ KICK_USER, // "Kick user(s)"
+ LARGER_TARGET_FILE_EXISTS, // "A file of equal or larger size already exists at the target location"
+ LAST_CHANGE, // "Last change: "
+ LAST_HUB, // "Hub (last seen on if offline)"
+ LAST_SEEN, // "Time last seen"
+ LEFT, // "left"
+ LOADING, // "Loading DC++, please wait..."
+ LOOKUP_AT_BITZI, // "Lookup TTH at Bitzi.com"
+ LOW, // "Low"
+ LOWEST, // "Lowest"
+ MAGNET_DLG_FILE, // "Filename:"
+ MAGNET_DLG_HASH, // "File Hash:"
+ MAGNET_DLG_NOTHING, // "Do nothing"
+ MAGNET_DLG_QUEUE, // "Add this file to your download queue"
+ MAGNET_DLG_REMEMBER, // "Do the same action next time without asking"
+ MAGNET_DLG_SEARCH, // "Start a search for this file"
+ MAGNET_DLG_TEXT_BAD, // "A MAGNET link was given to DC++, but it didn't contain a valid file hash for use on the Direct Connect network. No action will be taken."
+ MAGNET_DLG_TEXT_GOOD, // "DC++ has detected a MAGNET link with a file hash that can be searched for on the Direct Connect network. What would you like to do?"
+ MAGNET_DLG_TITLE, // "MAGNET Link detected"
+ MAGNET_HANDLER_DESC, // "Download files from the Direct Connect network"
+ MAGNET_HANDLER_ROOT, // "DC++"
+ MAGNET_SHELL_DESC, // "URL:MAGNET URI"
+ MATCH_QUEUE, // "Match queue"
+ MATCHED_FILES, // "Matched %d file(s)"
+ MAX_HUBS, // "Max Hubs"
+ MAX_USERS, // "Max Users"
+ MiB, // "MiB"
+ MiBPS, // "MiB/s"
+ MENU_ABOUT, // "About DC++..."
+ MENU_ADL_SEARCH, // "ADL Search"
+ MENU_ARRANGE, // "Arrange icons"
+ MENU_CASCADE, // "Cascade"
+ MENU_CHANGELOG, // "Change Log"
+ MENU_CLOSE_DISCONNECTED, // "Close disconnected"
+ MENU_CONTENTS, // "Help &Contents\tF1"
+ MENU_DISCUSS, // "DC++ discussion forum"
+ MENU_DONATE, // "Donate ���/$$$ (paypal)"
+ MENU_DOWNLOAD_QUEUE, // "&Download Queue\tCtrl+D"
+ MENU_EXIT, // "&Exit"
+ MENU_FAQ, // "Frequently asked questions"
+ MENU_FAVORITE_HUBS, // "&Favorite Hubs\tCtrl+F"
+ MENU_FAVORITE_USERS, // "Favorite &Users\tCtrl+U"
+ MENU_FILE, // "&File"
+ MENU_FOLLOW_REDIRECT, // "Follow last redirec&t\tCtrl+T"
+ MENU_HASH_PROGRESS, // "Indexing progress"
+ MENU_HELP, // "&Help"
+ MENU_HELP_DOWNLOADS, // "Downloads"
+ MENU_HELP_GEOIPFILE, // "GeoIP database update"
+ MENU_HELP_FORUM, // "Help forum"
+ MENU_HELP_TRANSLATIONS, // "Translations"
+ MENU_HOMEPAGE, // "DC++ Homepage"
+ MENU_HORIZONTAL_TILE, // "Horizontal Tile"
+ MENU_MINIMIZE_ALL, // "Minimize &All"
+ MENU_RESTORE_ALL, // "Restore All"
+ MENU_NETWORK_STATISTICS, // "Network Statistics"
+ MENU_NOTEPAD, // "&Notepad\tCtrl+N"
+ MENU_OPEN_DOWNLOADS_DIR, // "Open downloads directory"
+ MENU_OPEN_FILE_LIST, // "Open file list...\tCtrl+L"
+ MENU_OPEN_OWN_LIST, // "Open own list"
+ MENU_PUBLIC_HUBS, // "&Public Hubs\tCtrl+P"
+ MENU_QUICK_CONNECT, // "&Quick Connect ...\tCtrl+Q"
+ MENU_README, // "Readme / Newbie help"
+ MENU_RECONNECT, // "&Reconnect\tCtrl+R"
+ MENU_REFRESH_FILE_LIST, // "Refresh file list\tCtrl+E"
+ MENU_REPORT_BUG, // "Report a bug"
+ MENU_REQUEST_FEATURE, // "Request a feature"
+ MENU_SEARCH, // "&Search\tCtrl+S"
+ MENU_SEARCH_SPY, // "Search Spy"
+ MENU_SETTINGS, // "Settings..."
+ MENU_SHOW, // "Show"
+ MENU_STATUS_BAR, // "&Status bar\tCtrl+2"
+ MENU_TOOLBAR, // "&Toolbar\tCtrl+1"
+ MENU_TRANSFER_VIEW, // "T&ransfers\tCtrl+3"
+ MENU_VERTICAL_TILE, // "Vertical Tile"
+ MENU_VIEW, // "&View"
+ MENU_WINDOW, // "&Window"
+ MIN_SHARE, // "Min Share"
+ MIN_SLOTS, // "Min Slots"
+ MOVE, // "Move/Rename"
+ MOVE_DOWN, // "Move &Down"
+ MOVE_UP, // "Move &Up"
+ NETWORK_STATISTICS, // "Network Statistics"
+ NETWORK_UNREACHABLE, // "Network unreachable (are you connected to the internet?)"
+ NEW, // "&New..."
+ NEXT, // "Next"
+ NICK, // "Nick"
+ NICK_TAKEN, // "Your nick was already taken, please change to something else!"
+ NICK_UNKNOWN, // " (Nick unknown)"
+ NO_STR, // "No"
+ NO_DIRECTORY_SPECIFIED, // "No directory specified"
+ NO_DOWNLOADS_FROM_SELF, // "You're trying to download from yourself!"
+ NO_ERRORS, // "No errors"
+ NO_MATCHES, // "No matches"
+ NO_SLOTS_AVAILABLE, // "No slots available"
+ NO_USERS, // "No users"
+ NO_USERS_TO_DOWNLOAD_FROM, // "No users to download from"
+ NON_BLOCKING_OPERATION, // "Non-blocking operation still in progress"
+ NORMAL, // "Normal"
+ NOT_CONNECTED, // "Not connected"
+ NOT_SOCKET, // "Not a socket"
+ NOTEPAD, // "Notepad"
+ OFFLINE, // "Offline"
+ ONLINE, // "Online"
+ ONLY_FREE_SLOTS, // "Only users with free slots"
+ ONLY_TTH, // "Only results with TTH root"
+ ONLY_WHERE_OP, // "Only where I'm op"
+ OPEN, // "Open"
+ OPEN_DOWNLOAD_PAGE, // "Open download page?"
+ OPEN_FOLDER, // "Open folder"
+ OPERATING_SYSTEM_NOT_COMPATIBLE, // "Operating system does not match minimum requirements for feature"
+ OPERATION_WOULD_BLOCK_EXECUTION, // "Operation would block execution"
+ OUT_OF_BUFFER_SPACE, // "Out of buffer space"
+ PARTS, // "Parts: "
+ PASSIVE_USER, // "Passive user"
+ PASSWORD, // "Password"
+ PATH, // "Path"
+ PAUSED, // "Paused"
+ PERMISSION_DENIED, // "Permission denied"
+ PIB, // "PiB"
+ PICTURE, // "Picture"
+ PORT, // "Port: "
+ PORT_IS_BUSY, // "Port %d is busy, please choose another one in the settings dialog, or disable any other application that might be using it and restart DC++"
+ PREPARING_FILE_LIST, // "Preparing file list..."
+ PRESS_FOLLOW, // "Press the follow redirect button to connect to "
+ PRIORITY, // "Priority"
+ PRIVATE_MESSAGE_FROM, // "Private message from "
+ PROPERTIES, // "&Properties"
+ PUBLIC_HUBS, // "Public Hubs"
+ PURGE, // "Purge"
+ QUICK_CONNECT, // "Quick Connect"
+ RATING, // "Rating"
+ RATIO, // "Ratio"
+ READD_SOURCE, // "Re-add source"
+ REALLY_EXIT, // "Really exit?"
+ REALLY_REMOVE, // "Really remove?"
+ REDIRECT, // "Redirect"
+ REDIRECT_ALREADY_CONNECTED, // "Redirect request received to a hub that's already connected"
+ REDIRECT_USER, // "Redirect user(s)"
+ REFRESH, // "&Refresh"
+ REFRESH_USER_LIST, // "Refresh user list"
+ RELIABILITY, // "Reliability"
+ REMOVE, // "&Remove"
+ REMOVE_ALL, // "Remove all"
+ REMOVE_ALL_SUBDIRECTORIES, // "Remove all subdirectories before adding this one"
+ REMOVE_FROM_ALL, // "Remove user from queue"
+ REMOVE_SOURCE, // "Remove source"
+ ROLLBACK_INCONSISTENCY, // "Rollback inconsistency, existing file does not match the one being downloaded"
+ RUNNING, // "Running..."
+ SEARCH, // "Search"
+ SEARCH_FOR, // "Search for"
+ SEARCH_FOR_ALTERNATES, // "Search for alternates"
+ SEARCH_FOR_FILE, // "Search for file"
+ SEARCH_OPTIONS, // "Search options"
+ SEARCH_SPAM_FROM, // "Search spam detected from "
+ SEARCH_SPY, // "Search Spy"
+ SEARCH_STRING, // "Search String"
+ SEARCHING_FOR, // "Searching for "
+ SEARCHING_READY, // "Ready to search..."
+ SEARCHING_WAIT, // "Searching too soon, next search in %i seconds"
+ SEEK_BEYOND_END, // "Request to seek beyond the end of data"
+ SEND_PRIVATE_MESSAGE, // "Send private message"
+ SEPARATOR, // "Separator"
+ SERVER, // "Server"
+ SET_PRIORITY, // "Set priority"
+ SETTINGS, // "Settings"
+ SETTINGS_ADD_FINISHED_INSTANTLY, // "Add finished files to share instantly (if shared)"
+ SETTINGS_ADD_FOLDER, // "&Add folder"
+ SETTINGS_ADLS_BREAK_ON_FIRST, // "Break on first ADLSearch match"
+ SETTINGS_ADVANCED, // "Advanced"
+ SETTINGS_ADVANCED3, // "Advanced\\Experts only"
+ SETTINGS_ADVANCED_RESUME, // "Advanced resume using TTH"
+ SETTINGS_ADVANCED_SETTINGS, // "Advanced settings"
+ SETTINGS_ANTI_FRAG, // "Use antifragmentation method for downloads"
+ SETTINGS_APPEARANCE, // "Appearance"
+ SETTINGS_APPEARANCE2, // "Colors and sounds"
+ SETTINGS_AUTO_AWAY, // "Auto-away on minimize (and back on restore)"
+ SETTINGS_AUTO_FOLLOW, // "Automatically follow redirects"
+ SETTINGS_AUTO_KICK, // "Automatically disconnect users who leave the hub"
+ SETTINGS_AUTO_SEARCH, // "Automatically search for alternative download locations"
+ SETTINGS_AUTO_SEARCH_AUTO_MATCH, // "Automatically match queue for auto search hits"
+ SETTINGS_AUTO_UPDATE_LIST, // "Automatically refresh share list every hour"
+ SETTINGS_AUTO_OPEN, // "Auto-open at startup"
+ SETTINGS_BIND_ADDRESS, // "Bind address"
+ SETTINGS_CHANGE, // "&Change"
+ SETTINGS_CLEAR_SEARCH, // "Clear search box after each search"
+ SETTINGS_COLORS, // "Colors"
+ SETTINGS_COMMAND, // "Command"
+ SETTINGS_COMPRESS_TRANSFERS, // "Enable safe and compressed transfers"
+ SETTINGS_CONFIGURE_HUB_LISTS, // "Configure Public Hub Lists"
+ SETTINGS_CONFIRM_EXIT, // "Confirm application exit"
+ SETTINGS_CONFIRM_HUB_REMOVAL, // "Confirm favorite hub removal"
+ SETTINGS_CONFIRM_ITEM_REMOVAL, // "Confirm item removal in download queue"
+ SETTINGS_CONNECTION_SETTINGS, // "Connection Settings (see the help file if unsure)"
+ SETTINGS_CONNECTION_TYPE, // "Connection Type"
+ SETTINGS_DEFAULT_AWAY_MSG, // "Default away message"
+ SETTINGS_DIRECTORIES, // "Directories"
+ SETTINGS_DONT_DL_ALREADY_SHARED, // "Don't download files already in share"
+ SETTINGS_DOWNLOAD_DIRECTORY, // "Default download directory"
+ SETTINGS_DOWNLOAD_LIMITS, // "Limits"
+ SETTINGS_DOWNLOADS, // "Downloads"
+ SETTINGS_DOWNLOADS_MAX, // "Maximum simultaneous downloads (0 = infinite)"
+ SETTINGS_DOWNLOADS_SPEED_PAUSE, // "No new downloads if speed exceeds (KiB/s, 0 = disable)"
+ SETTINGS_EXAMPLE_TEXT, // "Donate €€€:s! (ok, dirty dollars are fine as well =) (see help menu)"
+ SETTINGS_FAV_SHOW_JOINS, // "Only show joins / parts for favorite users"
+ SETTINGS_FAVORITE_DIRS_PAGE, // "Downloads\\Favorites"
+ SETTINGS_FAVORITE_DIRS, // "Favorite download directories"
+ SETTINGS_FILE_NAME, // "Filename"
+ SETTINGS_FILTER_MESSAGES, // "Filter kick and NMDC debug messages"
+ SETTINGS_FINISHED_DIRTY, // "Set Finished Manager(s) tab bold when an entry is added"
+ SETTINGS_FORMAT, // "Format"
+ SETTINGS_GENERAL, // "Personal information "
+ SETTINGS_GET_USER_COUNTRY, // "Get User Country"
+ SETTINGS_HUB_USER_COMMANDS, // "Accept custom user commands from hub"
+ SETTINGS_IGNORE_OFFLINE, // "Ignore private messages from offline users"
+ SETTINGS_IP, // "IP"
+ SETTINGS_KEEP_LISTS, // "Don't delete file lists when exiting"
+ SETTINGS_LANGUAGE_FILE, // "Language file"
+ SETTINGS_LIST_DUPES, // "Keep duplicate files in your file list"
+ SETTINGS_LOG_DOWNLOADS, // "Log downloads"
+ SETTINGS_LOG_FILELIST_TRANSFERS, // "Log filelist transfers"
+ SETTINGS_LOG_MAIN_CHAT, // "Log main chat"
+ SETTINGS_LOG_PRIVATE_CHAT, // "Log private chat"
+ SETTINGS_LOG_STATUS_MESSAGES, // "Log status messages"
+ SETTINGS_LOG_SYSTEM_MESSAGES, // "Log system messages"
+ SETTINGS_LOG_UPLOADS, // "Log uploads"
+ SETTINGS_LOGGING, // "Logging"
+ SETTINGS_LOGS, // "Logs"
+ SETTINGS_MAGNET_ASK, // "Ask what to do when a magnet link is detected."
+ SETTINGS_MAX_HASH_SPEED, // "Max hash speed"
+ SETTINGS_MAX_TAB_ROWS, // "Max tab rows"
+ SETTINGS_MINIMIZE_TRAY, // "Minimize to tray"
+ SETTINGS_NAME, // "Name"
+ SETTINGS_NETWORK, // "Connection settings"
+ SETTINGS_NO_AWAYMSG_TO_BOTS, // "Don't send the away message to bots"
+ SETTINGS_ONLY_HASHED, // "Note; Files appear in the share only after they've been hashed!"
+ SETTINGS_ONLY_TTH, // "Search for files with TTH root only as standard"
+ SETTINGS_OPEN_NEW_WINDOW, // "Open new window when using /join"
+ SETTINGS_OPEN_USER_CMD_HELP, // "Always open help file with this dialog"
+ SETTINGS_OPTIONS, // "Options"
+ SETTINGS_PASSIVE, // "Passive"
+ SETTINGS_PERSONAL_INFORMATION, // "Personal Information"
+ SETTINGS_PM_BEEP, // "Make an annoying sound every time a private message is received"
+ SETTINGS_PM_BEEP_OPEN, // "Make an annoying sound when a private message window is opened"
+ SETTINGS_PM_HISTORY, // "PM history"
+ SETTINGS_POPUNDER_FILELIST, // "Open new file list windows in the background"
+ SETTINGS_POPUNDER_PM, // "Open new private message windows in the background"
+ SETTINGS_POPUP_OFFLINE, // "Open private messages from offline users in their own window"
+ SETTINGS_POPUP_PMS, // "Open private messages in their own window"
+ SETTINGS_PUBLIC_HUB_LIST, // "Public Hubs list"
+ SETTINGS_PUBLIC_HUB_LIST_HTTP_PROXY, // "HTTP Proxy (for hublist only)"
+ SETTINGS_PUBLIC_HUB_LIST_URL, // "Public Hubs list URL"
+ SETTINGS_QUEUE_DIRTY, // "Set Download Queue tab bold when contents change"
+ SETTINGS_QUEUE, // "Downloads\\Queue"
+ SETTINGS_RENAME_FOLDER, // "Rename"
+ SETTINGS_REQUIRES_RESTART, // "Note; most of these options require that you restart DC++"
+ SETTINGS_ROLLBACK, // "Rollback"
+ SETTINGS_SEARCH_HISTORY, // "Search history"
+ SETTINGS_SELECT_TEXT_FACE, // "Select &text style"
+ SETTINGS_SELECT_WINDOW_COLOR, // "Select &window color"
+ SETTINGS_SEND_UNKNOWN_COMMANDS, // "Send unknown /commands to the hub"
+ SETTINGS_SFV_CHECK, // "Enable automatic SFV checking"
+ SETTINGS_SHARE_HIDDEN, // "Share hidden files"
+ SETTINGS_SHARE_SIZE, // "Total size:"
+ SETTINGS_SHARED_DIRECTORIES, // "Shared directories"
+ SETTINGS_SHOW_JOINS, // "Show joins / parts in chat by default"
+ SETTINGS_SHOW_PROGRESS_BARS, // "Show progress bars for transfers (uses some CPU)"
+ SETTINGS_SKIP_ZERO_BYTE, // "Skip zero-byte files"
+ SETTINGS_SMALL_SEND_BUFFER, // "Use small send buffer (enable if uploads slow downloads a lot)"
+ SETTINGS_SOCKS5, // "SOCKS5"
+ SETTINGS_SOCKS5_IP, // "Socks IP"
+ SETTINGS_SOCKS5_PORT, // "Port"
+ SETTINGS_SOCKS5_RESOLVE, // "Use SOCKS5 server to resolve hostnames"
+ SETTINGS_SOCKS5_USERNAME, // "Username"
+ SETTINGS_SOUNDS, // "Sounds"
+ SETTINGS_SPEEDS_NOT_ACCURATE, // "Note; because of changing download speeds, this is not 100% accurate..."
+ SETTINGS_STATUS_IN_CHAT, // "View status messages in main chat"
+ SETTINGS_TAB_COMPLETION, // "Tab completion of nicks in chat"
+ SETTINGS_TAB_DIRTY, // "Set hub/PM/Search tab bold when contents change"
+ SETTINGS_TCP_PORT, // "TCP Port"
+ SETTINGS_TEXT_MINISLOT, // "Mini slot size"
+ SETTINGS_PRIO_HIGHEST, // "Highest prio size",
+ SETTINGS_PRIO_HIGH, // "High prio size",
+ SETTINGS_PRIO_NORMAL, // "Normal prio size",
+ SETTINGS_PRIO_LOW, // "Low prio size",
+ SETTINGS_PRIO_LOWEST, // "Lowest prio enabled",
+ SETTINGS_TIME_STAMPS, // "Show timestamps in chat by default"
+ SETTINGS_TIME_STAMPS_FORMAT, // "Set timestamps"
+ SETTINGS_TOGGLE_ACTIVE_WINDOW, // "Toggle window when selecting an active tab"
+ SETTINGS_UDP_PORT, // "UDP Port"
+ SETTINGS_UNFINISHED_DOWNLOAD_DIRECTORY, // "Unfinished downloads directory"
+ SETTINGS_UPLOADS, // "Sharing"
+ SETTINGS_UPLOADS_MIN_SPEED, // "Automatically open an extra slot if speed is below (0 = disable)"
+ SETTINGS_UPLOADS_SLOTS, // "Upload slots"
+ SETTINGS_URL_HANDLER, // "Register with Windows to handle dchub:// and adc:// URL links"
+ SETTINGS_URL_MAGNET, // "Register with Windows to handle magnet: URI links"
+ SETTINGS_USE_CTRL_FOR_LINE_HISTORY, // "Use CTRL for line history"
+ SETTINGS_USE_OEM_MONOFONT, // "Use OEM monospaced font for viewing text files"
+ SETTINGS_USE_SYSTEM_ICONS, // "Use system icons when browsing files (slows browsing down a bit)"
+ SETTINGS_USE_UPNP, // "Use UPnP Control"
+ SETTINGS_USER_COMMANDS, // "Advanced\\User Commands"
+ SETTINGS_WINDOWS, // "Windows"
+ SETTINGS_WINDOWS_OPTIONS, // "Window options"
+ SETTINGS_WRITE_BUFFER, // "Write buffer size"
+ SFV_INCONSISTENCY, // "CRC32 inconsistency (SFV-Check)"
+ SHARED, // "Shared"
+ SHARED_FILES, // "Shared Files"
+ SIZE, // "Size"
+ SIZE_MAX, // "Max Size"
+ SIZE_MIN, // "Min Size"
+ SKIP_RENAME, // "New virtual name matches old name, skipping..."
+ SLOT_GRANTED, // "Slot granted"
+ SLOTS, // "Slots"
+ SLOTS_SET, // "Slots set"
+ SOCKET_SHUT_DOWN, // "Socket has been shut down"
+ SOCKS_AUTH_FAILED, // "Socks server authentication failed (bad username / password?)"
+ SOCKS_AUTH_UNSUPPORTED, // "The socks server doesn't support user / password authentication"
+ SOCKS_FAILED, // "The socks server failed establish a connection"
+ SOCKS_NEEDS_AUTH, // "The socks server requires authentication"
+ SOCKS_SETUP_ERROR, // "Failed to set up the socks server for UDP relay (check socks address and port)"
+ SOURCE_TYPE, // "Source Type"
+ SPECIFY_SEARCH_STRING, // "Specify a search string"
+ SPECIFY_SERVER, // "Specify a server to connect to"
+ SPEED, // "Speed"
+ STATUS, // "Status"
+ STORED_PASSWORD_SENT, // "Stored password sent..."
+ TAG, // "Tag"
+ TARGET_FILENAME_TOO_LONG, // "Target filename too long"
+ TiB, // "TiB"
+ TIME, // "Time"
+ TIME_LEFT, // "Time left"
+ TIMESTAMPS_DISABLED, // "Timestamps disabled"
+ TIMESTAMPS_ENABLED, // "Timestamps enabled"
+ TOO_MUCH_DATA, // "More data was sent than was expected"
+ TOTAL, // "Total: "
+ TTH_ALREADY_SHARED, // "A file with the same hash already exists in your share"
+ TTH_INCONSISTENCY, // "TTH inconsistency"
+ TTH_ROOT, // "TTH Root"
+ TYPE, // "Type"
+ UNABLE_TO_CREATE_THREAD, // "Unable to create thread"
+ UNKNOWN, // "Unknown"
+ UNKNOWN_ADDRESS, // "Unknown address"
+ UNKNOWN_COMMAND, // "Unknown command: "
+ UNKNOWN_ERROR, // "Unknown error: 0x%x"
+ UNSUPPORTED_FILELIST_FORMAT, // "Unsupported filelist format"
+ UPLOAD_FINISHED_IDLE, // "Upload finished, idle..."
+ UPLOAD_STARTING, // "Upload starting..."
+ UPLOADED_BYTES, // "Uploaded %s (%.01f%%) in %s"
+ UPLOADED_TO, // " uploaded to "
+ UPLOADS, // "Uploads"
+ UPNP_FAILED_TO_CREATE_MAPPINGS, // "Failed to create port mappings. Please set up your NAT yourself."
+ UPNP_FAILED_TO_REMOVE_MAPPINGS, // "Failed to remove port mappings"
+ UPNP_FAILED_TO_GET_EXTERNAL_IP, // "Failed to get external IP via UPnP. Please set it yourself."
+ USER, // "User"
+ USER_CMD_CHAT, // "Chat"
+ USER_CMD_COMMAND, // "Command"
+ USER_CMD_CONTEXT, // "Context"
+ USER_CMD_FILELIST_MENU, // "Filelist Menu"
+ USER_CMD_HUB, // "Hub IP / DNS (empty = all, 'op' = where operator)"
+ USER_CMD_HUB_MENU, // "Hub Menu"
+ USER_CMD_ONCE, // "Send once per nick"
+ USER_CMD_PARAMETERS, // "Parameters"
+ USER_CMD_PM, // "PM"
+ USER_CMD_PREVIEW, // "Text sent to hub"
+ USER_CMD_RAW, // "Raw"
+ USER_CMD_SEARCH_MENU, // "Search Menu"
+ USER_CMD_TO, // "To"
+ USER_CMD_TYPE, // "Command Type"
+ USER_CMD_USER_MENU, // "User Menu"
+ USER_CMD_WINDOW, // "Create / Modify Command"
+ USER_DESCRIPTION, // "User Description"
+ USER_OFFLINE, // "User offline"
+ USER_WENT_OFFLINE, // "User went offline"
+ USERS, // "Users"
+ VIDEO, // "Video"
+ VIEW_AS_TEXT, // "View as text"
+ VIRTUAL_NAME, // "Virtual name"
+ VIRTUAL_NAME_EXISTS, // "Virtual directory name already exists"
+ VIRTUAL_NAME_LONG, // "Name under which the others see the directory"
+ WAITING, // "Waiting..."
+ WAITING_TO_RETRY, // "Waiting to retry..."
+ WAITING_USER_ONLINE, // "Waiting (User online)"
+ WAITING_USERS_ONLINE, // "Waiting (%d of %d users online)"
+ YES_STR, // "Yes"
+ YOU_ARE_BEING_REDIRECTED, // "You are being redirected to "
+ LAST // @DontAdd
+};
diff --git a/dcpp/StringSearch.h b/dcpp/StringSearch.h
new file mode 100644
index 0000000..ffa51ab
--- /dev/null
+++ b/dcpp/StringSearch.h
@@ -0,0 +1,117 @@
+/*
+* Copyright (C) 2001-2005 Jacek Sieka, arnetheduck on gmail point 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 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.
+*/
+
+#ifndef STRINGSEARCH_H
+#define STRINGSEARCH_H
+
+#include "Text.h"
+
+/**
+ * A class that implements a fast substring search algo suited for matching
+ * one pattern against many strings (currently Quick Search, a variant of
+ * Boyer-Moore. Code based on "A very fast substring search algorithm" by
+ * D. Sunday).
+ * @todo Perhaps find an algo suitable for matching multiple substrings.
+ */
+class StringSearch {
+public:
+ typedef vector<StringSearch> List;
+ typedef List::iterator Iter;
+
+ explicit StringSearch(const string& aPattern) throw() : pattern(Text::toLower(aPattern)) {
+ initDelta1();
+ };
+ StringSearch(const StringSearch& rhs) throw() : pattern(rhs.pattern) {
+ memcpy(delta1, rhs.delta1, sizeof(delta1));
+ };
+ const StringSearch& operator=(const StringSearch& rhs) {
+ memcpy(delta1, rhs.delta1, sizeof(delta1));
+ pattern = rhs.pattern;
+ return *this;
+ }
+ const StringSearch& operator=(const string& rhs) {
+ pattern = Text::toLower(rhs);
+ initDelta1();
+ return *this;
+ }
+
+ bool operator==(const StringSearch& rhs) { return pattern == rhs.pattern; };
+
+ const string& getPattern() const { return pattern; };
+
+ /** Match a text against the pattern */
+ bool match(const string& aText) const throw() {
+
+ // Lower-case representation of UTF-8 string, since we no longer have that 1 char = 1 byte...
+ string lower;
+ Text::toLower(aText, lower);
+
+ // u_int8_t to avoid problems with signed char pointer arithmetic
+ u_int8_t *tx = (u_int8_t*)lower.c_str();
+ u_int8_t *px = (u_int8_t*)pattern.c_str();
+
+ string::size_type plen = pattern.length();
+
+ if(aText.length() < plen) {
+ return false;
+ }
+
+ u_int8_t *end = tx + aText.length() - plen + 1;
+ while(tx < end) {
+ size_t i = 0;
+ for(; px[i] && (px[i] == tx[i]); ++i)
+ ; // Empty!
+
+ if(px[i] == 0)
+ return true;
+
+ tx += delta1[tx[plen]];
+ }
+
+ return false;
+ }
+
+private:
+ enum { ASIZE = 256 };
+ /**
+ * Delta1 shift, u_int16_t because we expect all patterns to be shorter than 2^16
+ * chars.
+ */
+ u_int16_t delta1[ASIZE];
+ string pattern;
+
+ void initDelta1() {
+ u_int16_t x = (u_int16_t)(pattern.length() + 1);
+ u_int16_t i;
+ for(i = 0; i < ASIZE; ++i) {
+ delta1[i] = x;
+ }
+ // x = pattern.length();
+ x--;
+ u_int8_t* p = (u_int8_t*)pattern.data();
+ for(i = 0; i < x; ++i) {
+ delta1[p[i]] = (u_int16_t)(x - i);
+ }
+ }
+};
+
+#endif // STRINGSEARCH_H
+/**
+ * @file
+ * $Id: StringSearch.h,v 1.2 2005/08/21 14:03:43 olof Exp $
+ */
diff --git a/dcpp/StringTokenizer.cpp b/dcpp/StringTokenizer.cpp
new file mode 100644
index 0000000..99b63c3
--- /dev/null
+++ b/dcpp/StringTokenizer.cpp
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2001-2005 Jacek Sieka, arnetheduck on gmail point 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 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.
+ */
+
+#include "stdinc.h"
+#include "DCPlusPlus.h"
+
+#include "StringTokenizer.h"
+
+/**
+ * @file
+ * $Id: StringTokenizer.cpp,v 1.2 2005/08/21 14:03:43 olof Exp $
+ */
+
diff --git a/dcpp/StringTokenizer.h b/dcpp/StringTokenizer.h
new file mode 100644
index 0000000..9387ca4
--- /dev/null
+++ b/dcpp/StringTokenizer.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2001-2005 Jacek Sieka, arnetheduck on gmail point 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 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.
+ */
+
+#if !defined(AFX_STRINGTOKENIZER_H__E9B493AC_97A7_4A18_AF7C_06BFE1926A52__INCLUDED_)
+#define AFX_STRINGTOKENIZER_H__E9B493AC_97A7_4A18_AF7C_06BFE1926A52__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+template<class T>
+class StringTokenizer
+{
+private:
+ vector<T> tokens;
+public:
+ StringTokenizer(const T& aString, typename T::value_type aToken) {
+ string::size_type i = 0;
+ string::size_type j = 0;
+ while( (i=aString.find(aToken, j)) != string::npos ) {
+ tokens.push_back(aString.substr(j, i-j));
+ j = i + 1;
+ }
+ if(j < aString.size())
+ tokens.push_back(aString.substr(j, aString.size()-j));
+ }
+
+ StringTokenizer(const T& aString, typename T::value_type* aToken) {
+ string::size_type i = 0;
+ string::size_type j = 0;
+ size_t l = strlen(aToken);
+ while( (i=aString.find(aToken, j)) != string::npos ) {
+ tokens.push_back(aString.substr(j, i-j));
+ j = i + l;
+ }
+ if(j < aString.size())
+ tokens.push_back(aString.substr(j, aString.size()-j));
+ }
+
+ vector<T>& getTokens() { return tokens; };
+
+ ~StringTokenizer() { };
+
+};
+
+#endif // !defined(AFX_STRINGTOKENIZER_H__E9B493AC_97A7_4A18_AF7C_06BFE1926A52__INCLUDED_)
+
+/**
+ * @file
+ * $Id: StringTokenizer.h,v 1.2 2005/08/21 14:03:43 olof Exp $
+ */
diff --git a/dcpp/Text.cpp b/dcpp/Text.cpp
new file mode 100644
index 0000000..6c829ac
--- /dev/null
+++ b/dcpp/Text.cpp
@@ -0,0 +1,285 @@
+/*
+ * Copyright (C) 2001-2005 Jacek Sieka, arnetheduck on gmail point 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 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.
+ */
+
+#include "stdinc.h"
+#include "DCPlusPlus.h"
+
+#include "Text.h"
+
+char Text::asciiLower[128];
+wchar_t Text::lower[65536];
+
+// When using GNU C library; setlocale should be called before Text::initialize
+
+void Text::initialize() {
+ for(size_t i = 0; i < 65536; ++i) {
+#ifdef _WIN32
+ lower[i] = (wchar_t)CharLowerW((LPWSTR)i);
+#else
+ lower[i] = (char)towlower(i);
+#endif
+ }
+
+ for(size_t i = 0; i < 128; ++i) {
+ asciiLower[i] = (char)lower[i];
+ }
+
+}
+
+int Text::utf8ToWc(const char* str, wchar_t& c) {
+ u_int8_t c0 = (u_int8_t)str[0];
+ if(c0 & 0x80) { // 1xxx xxxx
+ if(c0 & 0x40) { // 11xx xxxx
+ if(c0 & 0x20) { // 111x xxxx
+ if(c0 & 0x10) { // 1111 xxxx
+ int n = -4;
+ if(c0 & 0x08) { // 1111 1xxx
+ n = -5;
+ if(c0 & 0x04) { // 1111 11xx
+ if(c0 & 0x02) { // 1111 111x
+ return -1;
+ }
+ n = -6;
+ }
+ }
+ int i = -1;
+ while(i > n && (str[abs(i)] & 0x80) == 0x80)
+ --i;
+ return i;
+ } else { // 1110xxxx
+ u_int8_t c1 = (u_int8_t)str[1];
+ if((c1 & (0x80 | 0x40)) != 0x80)
+ return -1;
+
+ u_int8_t c2 = (u_int8_t)str[2];
+ if((c2 & (0x80 | 0x40)) != 0x80)
+ return -2;
+
+ // Ugly utf-16 surrogate catch
+ if((c0 & 0x0f) == 0x0d && (c1 & 0x3c) >= (0x08 << 2))
+ return -3;
+
+ // Overlong encoding
+ if(c0 == (0x80 | 0x40 | 0x20) && (c1 & (0x80 | 0x40 | 0x20)) == 0x80)
+ return -3;
+
+ c = (((wchar_t)c0 & 0x0f) << 12) |
+ (((wchar_t)c1 & 0x3f) << 6) |
+ ((wchar_t)c2 & 0x3f);
+
+ return 3;
+ }
+ } else { // 110xxxxx
+ u_int8_t c1 = (u_int8_t)str[1];
+ if((c1 & (0x80 | 0x40)) != 0x80)
+ return -1;
+
+ // Overlong encoding
+ if((c0 & ~1) == (0x80 | 0x40))
+ return -2;
+
+ c = (((wchar_t)c0 & 0x1f) << 6) |
+ ((wchar_t)c1 & 0x3f);
+ return 2;
+ }
+ } else { // 10xxxxxx
+ return -1;
+ }
+ } else { // 0xxxxxxx
+ c = (unsigned char)str[0];
+ return 1;
+ }
+ dcassert(0);
+}
+
+void Text::wcToUtf8(wchar_t c, string& str) {
+ if(c >= 0x0800) {
+ str += (char)(0x80 | 0x40 | 0x20 | (c >> 12));
+ str += (char)(0x80 | ((c >> 6) & 0x3f));
+ str += (char)(0x80 | (c & 0x3f));
+ } else if(c >= 0x0080) {
+ str += (char)(0x80 | 0x40 | (c >> 6));
+ str += (char)(0x80 | (c & 0x3f));
+ } else {
+ str += (char)c;
+ }
+}
+
+string& Text::acpToUtf8(const string& str, string& tmp) throw() {
+ wstring wtmp;
+ return wideToUtf8(acpToWide(str, wtmp), tmp);
+}
+
+wstring& Text::acpToWide(const string& str, wstring& tmp) throw() {
+#ifdef _WIN32
+ int n = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, str.c_str(), (int)str.length(), NULL, 0);
+ if(n == 0) {
+ return tmp;
+ }
+
+ tmp.resize(n);
+ n = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, str.c_str(), (int)str.length(), &tmp[0], n);
+ if(n == 0) {
+ tmp.clear();
+ return tmp;
+ }
+ return tmp;
+#else
+ //convert from current locale multibyte (equivalent to CP_ACP?) to wide char
+ /* const char* src = str.c_str();
+ int n = mbsrtowcs(NULL, &src, 0, NULL);
+ if (n < 1) {
+ return tmp;
+ }
+ tmp.resize(n);
+ n = mbsrtowcs(&tmp[0], &src, n, NULL);
+ if (n < 1) {
+ tmp.clear();
+ return tmp;
+ } */
+
+ // Rikards CODE
+ const char* src = str.c_str();
+ int length = 0;
+ while(*src) {length++;src++;}
+ src = str.c_str();
+ wchar_t *wdata = (wchar_t*)calloc(length+1,sizeof(wchar_t));
+ wchar_t *p = wdata;
+ while(*src) {
+ *p = wchar_t( (unsigned char)(*src));
+ src++;
+ p++;
+ }
+ *p = 0;
+
+ tmp.assign(wdata);
+ free(wdata);
+ // end Rikards CODE
+
+ return tmp;
+#endif
+}
+
+string& Text::wideToUtf8(const wstring& str, string& tgt) throw() {
+ string::size_type n = str.length();
+ for(string::size_type i = 0; i < n; ++i) {
+ wcToUtf8(str[i], tgt);
+ }
+ return tgt;
+}
+
+string& Text::wideToAcp(const wstring& str, string& tmp) throw() {
+#ifdef _WIN32
+ int n = WideCharToMultiByte(CP_ACP, 0, str.c_str(), (int)str.length(), NULL, 0, NULL, NULL);
+ if(n == 0) {
+ return tmp;
+ }
+
+ tmp.resize(n);
+ n = WideCharToMultiByte(CP_ACP, 0, str.c_str(), (int)str.length(), &tmp[0], n, NULL, NULL);
+ if(n == 0) {
+ tmp.clear();
+ return tmp;
+ }
+ return tmp;
+#else
+ const wchar_t* src = str.c_str();
+ //int n = wcsrtombs(NULL, &src, 0, NULL);
+ //if(n < 1) {
+ // return tmp;
+ //}
+ //tmp.resize(n);
+ //n = wcsrtombs(&tmp[0], &src, n, NULL);
+ //if(n < 1) {
+ // tmp.clear();
+ // return tmp;
+ //}
+ // RIKARD CODE
+ tmp.resize( str.size() );
+ for(int i=0;i<str.size();i++) {
+ tmp[i]=(src[i]>0xff) ? '?' :(unsigned char)src[i];
+ }
+ //END RIKARD CODE
+ return tmp;
+#endif
+}
+
+bool Text::validateUtf8(const string& str) throw() {
+ string::size_type i = 0;
+ while(i < str.length()) {
+ wchar_t dummy = 0;
+ int j = utf8ToWc(&str[i], dummy);
+ if(j < 0)
+ return false;
+ i += j;
+ }
+ return true;
+}
+
+string& Text::utf8ToAcp(const string& str, string& tmp) throw() {
+ wstring wtmp;
+ return wideToAcp(utf8ToWide(str, wtmp), tmp);
+}
+
+wstring& Text::utf8ToWide(const string& str, wstring& tgt) throw() {
+ tgt.reserve(str.length());
+ string::size_type n = str.length();
+ for(string::size_type i = 0; i < n; ) {
+ wchar_t c = 0;
+ int x = utf8ToWc(str.c_str() + i, c);
+ if(x < 0) {
+ tgt += '_';
+ i += abs(x);
+ } else {
+ i += x;
+ tgt += c;
+ }
+ }
+ return tgt;
+}
+
+wstring& Text::toLower(const wstring& str, wstring& tmp) throw() {
+ tmp.reserve(str.length());
+ wstring::const_iterator end = str.end();
+ for(wstring::const_iterator i = str.begin(); i != end; ++i) {
+ tmp += toLower(*i);
+ }
+ return tmp;
+}
+
+string& Text::toLower(const string& str, string& tmp) throw() {
+ tmp.reserve(str.length());
+ const char* end = &str[0] + str.length();
+ for(const char* p = &str[0]; p < end;) {
+ wchar_t c = 0;
+ int n = utf8ToWc(p, c);
+ if(n < 0) {
+ tmp += '_';
+ p += abs(n);
+ } else {
+ p += n;
+ wcToUtf8(toLower(c), tmp);
+ }
+ }
+ return tmp;
+}
+
+/**
+ * @file
+ * $Id: Text.cpp,v 1.4 2005/09/01 18:09:27 olof Exp $
+ */
diff --git a/dcpp/Text.h b/dcpp/Text.h
new file mode 100644
index 0000000..cc212e9
--- /dev/null
+++ b/dcpp/Text.h
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2001-2005 Jacek Sieka, arnetheduck on gmail point 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 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.
+ */
+
+#ifndef TEXT_H
+#define TEXT_H
+
+/**
+ * Text handling routines for DC++. DC++ internally uses UTF-8 for
+ * (almost) all string:s, hence all foreign text must be converted
+ * appropriately...
+ * acp - ANSI code page used by the system
+ * wide - wide unicode string
+ * utf8 - UTF-8 representation of the string
+ * t - current text GUI text format
+ * string - UTF-8 string (most of the time)
+ * wstring - Wide string
+ * tstring - GUI type string (acp string or wide string depending on build type)
+ */
+class Text {
+public:
+ static void initialize();
+
+ static string& acpToUtf8(const string& str, string& tmp) throw();
+ static string acpToUtf8(const string& str) throw() {
+ string tmp;
+ return acpToUtf8(str, tmp);
+ }
+
+ static wstring& acpToWide(const string& str, wstring& tmp) throw();
+ static wstring acpToWide(const string& str) throw() {
+ wstring tmp;
+ return acpToWide(str, tmp);
+ }
+
+ static string& utf8ToAcp(const string& str, string& tmp) throw();
+ static string utf8ToAcp(const string& str) throw() {
+ string tmp;
+ return utf8ToAcp(str, tmp);
+ }
+
+ static wstring& utf8ToWide(const string& str, wstring& tmp) throw();
+ static wstring utf8ToWide(const string& str) throw() {
+ wstring tmp;
+ return utf8ToWide(str, tmp);
+ }
+
+ static string& wideToAcp(const wstring& str, string& tmp) throw();
+ static string wideToAcp(const wstring& str) throw() {
+ string tmp;
+ return wideToAcp(str, tmp);
+ }
+ static string& wideToUtf8(const wstring& str, string& tmp) throw();
+ static string wideToUtf8(const wstring& str) throw() {
+ string tmp;
+ return wideToUtf8(str, tmp);
+ }
+
+ static int utf8ToWc(const char* str, wchar_t& c);
+ static void wcToUtf8(wchar_t c, string& str);
+
+#ifdef UNICODE
+ static tstring toT(const string& str) throw() { return utf8ToWide(str); }
+ static tstring& toT(const string& str, tstring& tmp) throw() { return utf8ToWide(str, tmp); }
+
+ static string fromT(const tstring& str) throw() { return wideToUtf8(str); }
+ static string fromT(const tstring& str, string& tmp) throw() { return wideToUtf8(str, tmp); }
+#else
+ static tstring toT(const string& str) throw() { return utf8ToAcp(str); }
+ static tstring& toT(const string& str, tstring& tmp) throw() { return utf8ToAcp(str, tmp); }
+
+ static string fromT(const tstring& str) throw() { return acpToUtf8(str); }
+ static string& fromT(const tstring& str, string& tmp) throw() { return acpToUtf8(str, tmp); }
+#endif
+
+ static bool isAscii(const string& str) {
+ return isAscii(str.c_str());
+ }
+ static bool isAscii(const char* str) {
+ for(const u_int8_t* p = (const u_int8_t*)str; *p; ++p) {
+ if(*p & 0x80)
+ return false;
+ }
+ return true;
+ }
+
+ static bool validateUtf8(const string& str) throw();
+
+ static char asciiToLower(char c) { dcassert((((u_int8_t)c) & 0x80) == 0); return asciiLower[(u_int8_t)c]; }
+
+ static wchar_t toLower(wchar_t c) { return lower[(u_int16_t)c]; }
+ static wstring toLower(const wstring& str) throw() {
+ wstring tmp;
+ return toLower(str, tmp);
+ }
+ static wstring& toLower(const wstring& str, wstring& tmp) throw();
+ static string toLower(const string& str) throw() {
+ string tmp;
+ return toLower(str, tmp);
+ }
+ static string& toLower(const string& str, string& tmp) throw();
+private:
+ static char asciiLower[128];
+ static wchar_t lower[65536];
+
+};
+
+#endif
+
+/**
+ * @file
+ * $Id: Text.h,v 1.2 2005/08/21 14:03:43 olof Exp $
+ */
diff --git a/dcpp/Thread.cpp b/dcpp/Thread.cpp
new file mode 100644
index 0000000..ef8d8a8
--- /dev/null
+++ b/dcpp/Thread.cpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2001-2005 Jacek Sieka, arnetheduck on gmail point 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 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.
+ */
+
+#include "stdinc.h"
+#include "DCPlusPlus.h"
+
+#include "Thread.h"
+
+#include "ResourceManager.h"
+
+#ifdef _WIN32
+void Thread::start() throw(ThreadException) {
+ join();
+ if( (threadHandle = CreateThread(NULL, 0, &starter, this, 0, &threadId)) == NULL) {
+ throw ThreadException(STRING(UNABLE_TO_CREATE_THREAD));
+ }
+}
+
+#else
+void Thread::start() throw(ThreadException) {
+ join();
+ if(pthread_create(&threadHandle, NULL, &starter, this) != 0) {
+ throw ThreadException(STRING(UNABLE_TO_CREATE_THREAD));
+ }
+}
+#endif
+/**
+ * @file
+ * $Id: Thread.cpp,v 1.2 2005/08/21 14:03:43 olof Exp $
+ */
+
diff --git a/dcpp/Thread.h b/dcpp/Thread.h
new file mode 100644
index 0000000..f8823b7
--- /dev/null
+++ b/dcpp/Thread.h
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2001-2005 Jacek Sieka, arnetheduck on gmail point 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 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.
+ */
+
+#ifndef THREAD_H
+#define THREAD_H
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+#ifndef _WIN32
+#include <pthread.h>
+#include <sched.h>
+#include <sys/resource.h>
+#endif
+
+#ifdef __i386__
+#include "atomic.h"
+#elif __ppc__
+#include "atomic-ppc.h"
+#elif TARGET_X86_64
+#include "atomic-x86_64.h"
+#elif HAVE_ASM_ATOMIC_H
+#include <asm/atomic.h>
+#endif
+
+#include "Exception.h"
+STANDARD_EXCEPTION(ThreadException);
+
+class Thread
+{
+public:
+#ifdef _WIN32
+ enum Priority {
+ IDLE = THREAD_PRIORITY_IDLE,
+ LOW = THREAD_PRIORITY_BELOW_NORMAL,
+ NORMAL = THREAD_PRIORITY_NORMAL,
+ HIGH = THREAD_PRIORITY_ABOVE_NORMAL
+ };
+
+ Thread() throw() : threadHandle(NULL), threadId(0){ };
+ virtual ~Thread() {
+ if(threadHandle)
+ CloseHandle(threadHandle);
+ };
+
+ void start() throw(ThreadException);
+ void join() throw(ThreadException) {
+ if(threadHandle == NULL) {
+ return;
+ }
+
+ WaitForSingleObject(threadHandle, INFINITE);
+ CloseHandle(threadHandle);
+ threadHandle = NULL;
+ }
+
+ void setThreadPriority(Priority p) throw() { ::SetThreadPriority(threadHandle, p); };
+
+ static void sleep(u_int32_t millis) { ::Sleep(millis); };
+ static void yield() { ::Sleep(0); };
+ static long safeInc(volatile long& v) { return InterlockedIncrement(&v); };
+ static long safeDec(volatile long& v) { return InterlockedDecrement(&v); };
+ static long safeExchange(volatile long& target, long value) { return InterlockedExchange(&target, value); };
+
+#else
+
+ enum Priority {
+ IDLE = 1,
+ LOW = 1,
+ NORMAL = 0,
+ HIGH = -1
+ };
+ Thread() throw() : threadHandle(0) { };
+ virtual ~Thread() {
+ if(threadHandle != 0) {
+ pthread_detach(threadHandle);
+ }
+ };
+ void start() throw(ThreadException);
+ void join() throw() {
+ if (threadHandle) {
+ pthread_join(threadHandle, 0);
+ threadHandle = 0;
+ }
+ };
+
+ void setThreadPriority(Priority p) { setpriority(PRIO_PROCESS, 0, p); };
+ static void sleep(u_int32_t millis) { ::usleep(millis*1000); };
+ static void yield() { ::sched_yield(); };
+ static long safeInc(volatile long& v) {
+#ifdef HAVE_ASM_ATOMIC_H
+ atomic_t t = ATOMIC_INIT(v);
+ atomic_inc(&t);
+ return (v=t.counter);
+#else
+#warning FIXME
+ return ++v;
+#endif
+ };
+ static long safeDec(volatile long& v) {
+#ifdef HAVE_ASM_ATOMIC_H
+ atomic_t t = ATOMIC_INIT(v);
+ atomic_dec(&t);
+ return (v=t.counter);
+#else
+#warning FIXME
+ return --v;
+#endif
+ };
+#endif
+
+protected:
+ virtual int run() = 0;
+
+private:
+ Thread(const Thread&);
+ Thread& operator=(const Thread&);
+
+#ifdef _WIN32
+ HANDLE threadHandle;
+ DWORD threadId;
+ static DWORD WINAPI starter(void* p) {
+ Thread* t = (Thread*)p;
+ t->run();
+ return 0;
+ }
+#else
+ pthread_t threadHandle;
+ static void* starter(void* p) {
+ Thread* t = (Thread*)p;
+ t->run();
+ return NULL;
+ }
+#endif
+};
+
+#endif // !defined(AFX_THREAD_H__3006956B_7C69_4DAD_9596_A49E1BD007D5__INCLUDED_)
+
+/**
+ * @file
+ * $Id: Thread.h,v 1.3 2005/11/28 20:18:14 olof Exp $
+ */
+
diff --git a/dcpp/TigerHash.cpp b/dcpp/TigerHash.cpp
new file mode 100644
index 0000000..5c2407a
--- /dev/null
+++ b/dcpp/TigerHash.cpp
@@ -0,0 +1,717 @@
+/*
+ * Copyright (C) 2001-2005 Jacek Sieka, arnetheduck on gmail point 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 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.
+ */
+
+#include "stdinc.h"
+#include "DCPlusPlus.h"
+
+#include "TigerHash.h"
+
+#define PASSES 3
+
+#define t1 (table)
+#define t2 (table+256)
+#define t3 (table+256*2)
+#define t4 (table+256*3)
+
+#define save_abc \
+ aa = a; \
+ bb = b; \
+ cc = c;
+
+#define round(a,b,c,x,mul) \
+ c ^= x; \
+ a -= t1[(u_int8_t)(c)] ^ \
+ t2[(u_int8_t)(((u_int32_t)(c))>>(2*8))] ^ \
+ t3[(u_int8_t)((c)>>(4*8))] ^ \
+ t4[(u_int8_t)(((u_int32_t)((c)>>(4*8)))>>(2*8))] ; \
+ b += t4[(u_int8_t)(((u_int32_t)(c))>>(1*8))] ^ \
+ t3[(u_int8_t)(((u_int32_t)(c))>>(3*8))] ^ \
+ t2[(u_int8_t)(((u_int32_t)((c)>>(4*8)))>>(1*8))] ^ \
+ t1[(u_int8_t)(((u_int32_t)((c)>>(4*8)))>>(3*8))]; \
+ b *= mul;
+
+#define pass(a,b,c,mul) \
+ round(a,b,c,x0,mul) \
+ round(b,c,a,x1,mul) \
+ round(c,a,b,x2,mul) \
+ round(a,b,c,x3,mul) \
+ round(b,c,a,x4,mul) \
+ round(c,a,b,x5,mul) \
+ round(a,b,c,x6,mul) \
+ round(b,c,a,x7,mul)
+
+#define key_schedule \
+ x0 -= x7 ^ _ULL(0xA5A5A5A5A5A5A5A5); \
+ x1 ^= x0; \
+ x2 += x1; \
+ x3 -= x2 ^ ((~x1)<<19); \
+ x4 ^= x3; \
+ x5 += x4; \
+ x6 -= x5 ^ ((~x4)>>23); \
+ x7 ^= x6; \
+ x0 += x7; \
+ x1 -= x0 ^ ((~x7)<<19); \
+ x2 ^= x1; \
+ x3 += x2; \
+ x4 -= x3 ^ ((~x2)>>23); \
+ x5 ^= x4; \
+ x6 += x5; \
+ x7 -= x6 ^ _ULL(0x0123456789ABCDEF);
+
+#define feedforward \
+ a ^= aa; \
+ b -= bb; \
+ c += cc;
+
+#define compress \
+ save_abc \
+ for(pass_no=0; pass_no<PASSES; pass_no++) { \
+ if(pass_no != 0) {key_schedule} \
+ pass(a,b,c,(pass_no==0?5:pass_no==1?7:9)); \
+ tmpa=a; a=c; c=b; b=tmpa;} \
+ feedforward
+
+#define tiger_compress_macro(str, state) \
+{ \
+ register u_int64_t a, b, c, tmpa; \
+ u_int64_t aa, bb, cc; \
+ register u_int64_t x0, x1, x2, x3, x4, x5, x6, x7; \
+ int pass_no; \
+ \
+ a = state[0]; \
+ b = state[1]; \
+ c = state[2]; \
+ \
+ x0=str[0]; x1=str[1]; x2=str[2]; x3=str[3]; \
+ x4=str[4]; x5=str[5]; x6=str[6]; x7=str[7]; \
+ \
+ compress; \
+ \
+ state[0] = a; \
+ state[1] = b; \
+ state[2] = c; \
+}
+
+/* The compress function is a function. Requires smaller cache? */
+void TigerHash::tigerCompress(const u_int64_t *str, u_int64_t state[3]) {
+ tiger_compress_macro(((const u_int64_t*)str), ((u_int64_t*)state));
+}
+
+void TigerHash::update(const void* data, size_t length) {
+ size_t tmppos = (u_int32_t)(pos & BLOCK_SIZE-1);
+ const u_int8_t* str = (const u_int8_t*)data;
+ // First empty tmp buffer if possible
+ if(tmppos > 0) {
+ size_t n = min(length, BLOCK_SIZE-tmppos);
+ memcpy(tmp + tmppos, str, n);
+ str += n;
+ pos += n;
+ length -= n;
+
+ if((tmppos + n) == BLOCK_SIZE) {
+#ifdef __BIG_ENDIAN__
+ unsigned char temp[BLOCK_SIZE];
+ // Convert to little endian
+ for(int j=0; j<BLOCK_SIZE; j++)
+ temp[j^7] = ((u_int8_t*)tmp)[j];
+ tigerCompress((u_int64_t*)temp, res);
+#else
+ tigerCompress((u_int64_t*)tmp, res);
+#endif
+ tmppos = 0;
+ }
+ }
+
+ // So, now either tmp is empty or all data has been consumed...
+ dcassert(length == 0 || tmppos == 0);
+
+ // Process the bulk of data
+ while(length>=BLOCK_SIZE) {
+#ifdef __BIG_ENDIAN__
+ unsigned char temp[BLOCK_SIZE];
+ // Convert to little endian
+ for(int j=0; j<BLOCK_SIZE; j++)
+ temp[j^7] = ((u_int8_t*)str)[j];
+ tigerCompress((u_int64_t*)temp, res);
+#else
+ tigerCompress((u_int64_t*)str, res);
+#endif
+ str += BLOCK_SIZE;
+ pos += BLOCK_SIZE;
+ length -= BLOCK_SIZE;
+ }
+
+ // Copy the rest to the tmp buffer
+ memcpy(tmp, str, length);
+ pos += length;
+}
+
+u_int8_t* TigerHash::finalize() {
+ size_t tmppos = (size_t)(pos & BLOCK_SIZE-1);
+ // Tmp buffer always has at least one pos, otherwise it would have
+ // been processed in update()
+
+ tmp[tmppos++] = 0x01;
+
+ if(tmppos > (BLOCK_SIZE - sizeof(u_int64_t))) {
+ memset(tmp + tmppos, 0, BLOCK_SIZE - tmppos);
+#ifdef __BIG_ENDIAN__
+ unsigned char temp[BLOCK_SIZE];
+ // Convert to little endian
+ for(int j=0; j<BLOCK_SIZE; j++)
+ temp[j^7] = ((u_int8_t*)tmp)[j];
+ tigerCompress((u_int64_t*)temp, res);
+#else
+ tigerCompress((u_int64_t*)tmp, res);
+#endif
+ memset(tmp, 0, BLOCK_SIZE);
+ } else {
+ memset(tmp + tmppos, 0, BLOCK_SIZE - tmppos - sizeof(u_int64_t));
+#ifdef __BIG_ENDIAN__
+ unsigned char temp[BLOCK_SIZE];
+ // Convert to little endian
+ for(int j=0; j<BLOCK_SIZE; j++)
+ temp[j^7] = ((u_int8_t*)tmp)[j];
+ memcpy(tmp, temp, BLOCK_SIZE);
+#endif
+ }
+
+ ((u_int64_t*)(&(tmp[56])))[0] = pos<<3;
+ tigerCompress((u_int64_t*)tmp, res);
+ return getResult();
+}
+
+u_int64_t TigerHash::table[4*256] = {
+ _ULL(0x02AAB17CF7E90C5E) /* 0 */, _ULL(0xAC424B03E243A8EC) /* 1 */,
+ _ULL(0x72CD5BE30DD5FCD3) /* 2 */, _ULL(0x6D019B93F6F97F3A) /* 3 */,
+ _ULL(0xCD9978FFD21F9193) /* 4 */, _ULL(0x7573A1C9708029E2) /* 5 */,
+ _ULL(0xB164326B922A83C3) /* 6 */, _ULL(0x46883EEE04915870) /* 7 */,
+ _ULL(0xEAACE3057103ECE6) /* 8 */, _ULL(0xC54169B808A3535C) /* 9 */,
+ _ULL(0x4CE754918DDEC47C) /* 10 */, _ULL(0x0AA2F4DFDC0DF40C) /* 11 */,
+ _ULL(0x10B76F18A74DBEFA) /* 12 */, _ULL(0xC6CCB6235AD1AB6A) /* 13 */,
+ _ULL(0x13726121572FE2FF) /* 14 */, _ULL(0x1A488C6F199D921E) /* 15 */,
+ _ULL(0x4BC9F9F4DA0007CA) /* 16 */, _ULL(0x26F5E6F6E85241C7) /* 17 */,
+ _ULL(0x859079DBEA5947B6) /* 18 */, _ULL(0x4F1885C5C99E8C92) /* 19 */,
+ _ULL(0xD78E761EA96F864B) /* 20 */, _ULL(0x8E36428C52B5C17D) /* 21 */,
+ _ULL(0x69CF6827373063C1) /* 22 */, _ULL(0xB607C93D9BB4C56E) /* 23 */,
+ _ULL(0x7D820E760E76B5EA) /* 24 */, _ULL(0x645C9CC6F07FDC42) /* 25 */,
+ _ULL(0xBF38A078243342E0) /* 26 */, _ULL(0x5F6B343C9D2E7D04) /* 27 */,
+ _ULL(0xF2C28AEB600B0EC6) /* 28 */, _ULL(0x6C0ED85F7254BCAC) /* 29 */,
+ _ULL(0x71592281A4DB4FE5) /* 30 */, _ULL(0x1967FA69CE0FED9F) /* 31 */,
+ _ULL(0xFD5293F8B96545DB) /* 32 */, _ULL(0xC879E9D7F2A7600B) /* 33 */,
+ _ULL(0x860248920193194E) /* 34 */, _ULL(0xA4F9533B2D9CC0B3) /* 35 */,
+ _ULL(0x9053836C15957613) /* 36 */, _ULL(0xDB6DCF8AFC357BF1) /* 37 */,
+ _ULL(0x18BEEA7A7A370F57) /* 38 */, _ULL(0x037117CA50B99066) /* 39 */,
+ _ULL(0x6AB30A9774424A35) /* 40 */, _ULL(0xF4E92F02E325249B) /* 41 */,
+ _ULL(0x7739DB07061CCAE1) /* 42 */, _ULL(0xD8F3B49CECA42A05) /* 43 */,
+ _ULL(0xBD56BE3F51382F73) /* 44 */, _ULL(0x45FAED5843B0BB28) /* 45 */,
+ _ULL(0x1C813D5C11BF1F83) /* 46 */, _ULL(0x8AF0E4B6D75FA169) /* 47 */,
+ _ULL(0x33EE18A487AD9999) /* 48 */, _ULL(0x3C26E8EAB1C94410) /* 49 */,
+ _ULL(0xB510102BC0A822F9) /* 50 */, _ULL(0x141EEF310CE6123B) /* 51 */,
+ _ULL(0xFC65B90059DDB154) /* 52 */, _ULL(0xE0158640C5E0E607) /* 53 */,
+ _ULL(0x884E079826C3A3CF) /* 54 */, _ULL(0x930D0D9523C535FD) /* 55 */,
+ _ULL(0x35638D754E9A2B00) /* 56 */, _ULL(0x4085FCCF40469DD5) /* 57 */,
+ _ULL(0xC4B17AD28BE23A4C) /* 58 */, _ULL(0xCAB2F0FC6A3E6A2E) /* 59 */,
+ _ULL(0x2860971A6B943FCD) /* 60 */, _ULL(0x3DDE6EE212E30446) /* 61 */,
+ _ULL(0x6222F32AE01765AE) /* 62 */, _ULL(0x5D550BB5478308FE) /* 63 */,
+ _ULL(0xA9EFA98DA0EDA22A) /* 64 */, _ULL(0xC351A71686C40DA7) /* 65 */,
+ _ULL(0x1105586D9C867C84) /* 66 */, _ULL(0xDCFFEE85FDA22853) /* 67 */,
+ _ULL(0xCCFBD0262C5EEF76) /* 68 */, _ULL(0xBAF294CB8990D201) /* 69 */,
+ _ULL(0xE69464F52AFAD975) /* 70 */, _ULL(0x94B013AFDF133E14) /* 71 */,
+ _ULL(0x06A7D1A32823C958) /* 72 */, _ULL(0x6F95FE5130F61119) /* 73 */,
+ _ULL(0xD92AB34E462C06C0) /* 74 */, _ULL(0xED7BDE33887C71D2) /* 75 */,
+ _ULL(0x79746D6E6518393E) /* 76 */, _ULL(0x5BA419385D713329) /* 77 */,
+ _ULL(0x7C1BA6B948A97564) /* 78 */, _ULL(0x31987C197BFDAC67) /* 79 */,
+ _ULL(0xDE6C23C44B053D02) /* 80 */, _ULL(0x581C49FED002D64D) /* 81 */,
+ _ULL(0xDD474D6338261571) /* 82 */, _ULL(0xAA4546C3E473D062) /* 83 */,
+ _ULL(0x928FCE349455F860) /* 84 */, _ULL(0x48161BBACAAB94D9) /* 85 */,
+ _ULL(0x63912430770E6F68) /* 86 */, _ULL(0x6EC8A5E602C6641C) /* 87 */,
+ _ULL(0x87282515337DDD2B) /* 88 */, _ULL(0x2CDA6B42034B701B) /* 89 */,
+ _ULL(0xB03D37C181CB096D) /* 90 */, _ULL(0xE108438266C71C6F) /* 91 */,
+ _ULL(0x2B3180C7EB51B255) /* 92 */, _ULL(0xDF92B82F96C08BBC) /* 93 */,
+ _ULL(0x5C68C8C0A632F3BA) /* 94 */, _ULL(0x5504CC861C3D0556) /* 95 */,
+ _ULL(0xABBFA4E55FB26B8F) /* 96 */, _ULL(0x41848B0AB3BACEB4) /* 97 */,
+ _ULL(0xB334A273AA445D32) /* 98 */, _ULL(0xBCA696F0A85AD881) /* 99 */,
+ _ULL(0x24F6EC65B528D56C) /* 100 */, _ULL(0x0CE1512E90F4524A) /* 101 */,
+ _ULL(0x4E9DD79D5506D35A) /* 102 */, _ULL(0x258905FAC6CE9779) /* 103 */,
+ _ULL(0x2019295B3E109B33) /* 104 */, _ULL(0xF8A9478B73A054CC) /* 105 */,
+ _ULL(0x2924F2F934417EB0) /* 106 */, _ULL(0x3993357D536D1BC4) /* 107 */,
+ _ULL(0x38A81AC21DB6FF8B) /* 108 */, _ULL(0x47C4FBF17D6016BF) /* 109 */,
+ _ULL(0x1E0FAADD7667E3F5) /* 110 */, _ULL(0x7ABCFF62938BEB96) /* 111 */,
+ _ULL(0xA78DAD948FC179C9) /* 112 */, _ULL(0x8F1F98B72911E50D) /* 113 */,
+ _ULL(0x61E48EAE27121A91) /* 114 */, _ULL(0x4D62F7AD31859808) /* 115 */,
+ _ULL(0xECEBA345EF5CEAEB) /* 116 */, _ULL(0xF5CEB25EBC9684CE) /* 117 */,
+ _ULL(0xF633E20CB7F76221) /* 118 */, _ULL(0xA32CDF06AB8293E4) /* 119 */,
+ _ULL(0x985A202CA5EE2CA4) /* 120 */, _ULL(0xCF0B8447CC8A8FB1) /* 121 */,
+ _ULL(0x9F765244979859A3) /* 122 */, _ULL(0xA8D516B1A1240017) /* 123 */,
+ _ULL(0x0BD7BA3EBB5DC726) /* 124 */, _ULL(0xE54BCA55B86ADB39) /* 125 */,
+ _ULL(0x1D7A3AFD6C478063) /* 126 */, _ULL(0x519EC608E7669EDD) /* 127 */,
+ _ULL(0x0E5715A2D149AA23) /* 128 */, _ULL(0x177D4571848FF194) /* 129 */,
+ _ULL(0xEEB55F3241014C22) /* 130 */, _ULL(0x0F5E5CA13A6E2EC2) /* 131 */,
+ _ULL(0x8029927B75F5C361) /* 132 */, _ULL(0xAD139FABC3D6E436) /* 133 */,
+ _ULL(0x0D5DF1A94CCF402F) /* 134 */, _ULL(0x3E8BD948BEA5DFC8) /* 135 */,
+ _ULL(0xA5A0D357BD3FF77E) /* 136 */, _ULL(0xA2D12E251F74F645) /* 137 */,
+ _ULL(0x66FD9E525E81A082) /* 138 */, _ULL(0x2E0C90CE7F687A49) /* 139 */,
+ _ULL(0xC2E8BCBEBA973BC5) /* 140 */, _ULL(0x000001BCE509745F) /* 141 */,
+ _ULL(0x423777BBE6DAB3D6) /* 142 */, _ULL(0xD1661C7EAEF06EB5) /* 143 */,
+ _ULL(0xA1781F354DAACFD8) /* 144 */, _ULL(0x2D11284A2B16AFFC) /* 145 */,
+ _ULL(0xF1FC4F67FA891D1F) /* 146 */, _ULL(0x73ECC25DCB920ADA) /* 147 */,
+ _ULL(0xAE610C22C2A12651) /* 148 */, _ULL(0x96E0A810D356B78A) /* 149 */,
+ _ULL(0x5A9A381F2FE7870F) /* 150 */, _ULL(0xD5AD62EDE94E5530) /* 151 */,
+ _ULL(0xD225E5E8368D1427) /* 152 */, _ULL(0x65977B70C7AF4631) /* 153 */,
+ _ULL(0x99F889B2DE39D74F) /* 154 */, _ULL(0x233F30BF54E1D143) /* 155 */,
+ _ULL(0x9A9675D3D9A63C97) /* 156 */, _ULL(0x5470554FF334F9A8) /* 157 */,
+ _ULL(0x166ACB744A4F5688) /* 158 */, _ULL(0x70C74CAAB2E4AEAD) /* 159 */,
+ _ULL(0xF0D091646F294D12) /* 160 */, _ULL(0x57B82A89684031D1) /* 161 */,
+ _ULL(0xEFD95A5A61BE0B6B) /* 162 */, _ULL(0x2FBD12E969F2F29A) /* 163 */,
+ _ULL(0x9BD37013FEFF9FE8) /* 164 */, _ULL(0x3F9B0404D6085A06) /* 165 */,
+ _ULL(0x4940C1F3166CFE15) /* 166 */, _ULL(0x09542C4DCDF3DEFB) /* 167 */,
+ _ULL(0xB4C5218385CD5CE3) /* 168 */, _ULL(0xC935B7DC4462A641) /* 169 */,
+ _ULL(0x3417F8A68ED3B63F) /* 170 */, _ULL(0xB80959295B215B40) /* 171 */,
+ _ULL(0xF99CDAEF3B8C8572) /* 172 */, _ULL(0x018C0614F8FCB95D) /* 173 */,
+ _ULL(0x1B14ACCD1A3ACDF3) /* 174 */, _ULL(0x84D471F200BB732D) /* 175 */,
+ _ULL(0xC1A3110E95E8DA16) /* 176 */, _ULL(0x430A7220BF1A82B8) /* 177 */,
+ _ULL(0xB77E090D39DF210E) /* 178 */, _ULL(0x5EF4BD9F3CD05E9D) /* 179 */,
+ _ULL(0x9D4FF6DA7E57A444) /* 180 */, _ULL(0xDA1D60E183D4A5F8) /* 181 */,
+ _ULL(0xB287C38417998E47) /* 182 */, _ULL(0xFE3EDC121BB31886) /* 183 */,
+ _ULL(0xC7FE3CCC980CCBEF) /* 184 */, _ULL(0xE46FB590189BFD03) /* 185 */,
+ _ULL(0x3732FD469A4C57DC) /* 186 */, _ULL(0x7EF700A07CF1AD65) /* 187 */,
+ _ULL(0x59C64468A31D8859) /* 188 */, _ULL(0x762FB0B4D45B61F6) /* 189 */,
+ _ULL(0x155BAED099047718) /* 190 */, _ULL(0x68755E4C3D50BAA6) /* 191 */,
+ _ULL(0xE9214E7F22D8B4DF) /* 192 */, _ULL(0x2ADDBF532EAC95F4) /* 193 */,
+ _ULL(0x32AE3909B4BD0109) /* 194 */, _ULL(0x834DF537B08E3450) /* 195 */,
+ _ULL(0xFA209DA84220728D) /* 196 */, _ULL(0x9E691D9B9EFE23F7) /* 197 */,
+ _ULL(0x0446D288C4AE8D7F) /* 198 */, _ULL(0x7B4CC524E169785B) /* 199 */,
+ _ULL(0x21D87F0135CA1385) /* 200 */, _ULL(0xCEBB400F137B8AA5) /* 201 */,
+ _ULL(0x272E2B66580796BE) /* 202 */, _ULL(0x3612264125C2B0DE) /* 203 */,
+ _ULL(0x057702BDAD1EFBB2) /* 204 */, _ULL(0xD4BABB8EACF84BE9) /* 205 */,
+ _ULL(0x91583139641BC67B) /* 206 */, _ULL(0x8BDC2DE08036E024) /* 207 */,
+ _ULL(0x603C8156F49F68ED) /* 208 */, _ULL(0xF7D236F7DBEF5111) /* 209 */,
+ _ULL(0x9727C4598AD21E80) /* 210 */, _ULL(0xA08A0896670A5FD7) /* 211 */,
+ _ULL(0xCB4A8F4309EBA9CB) /* 212 */, _ULL(0x81AF564B0F7036A1) /* 213 */,
+ _ULL(0xC0B99AA778199ABD) /* 214 */, _ULL(0x959F1EC83FC8E952) /* 215 */,
+ _ULL(0x8C505077794A81B9) /* 216 */, _ULL(0x3ACAAF8F056338F0) /* 217 */,
+ _ULL(0x07B43F50627A6778) /* 218 */, _ULL(0x4A44AB49F5ECCC77) /* 219 */,
+ _ULL(0x3BC3D6E4B679EE98) /* 220 */, _ULL(0x9CC0D4D1CF14108C) /* 221 */,
+ _ULL(0x4406C00B206BC8A0) /* 222 */, _ULL(0x82A18854C8D72D89) /* 223 */,
+ _ULL(0x67E366B35C3C432C) /* 224 */, _ULL(0xB923DD61102B37F2) /* 225 */,
+ _ULL(0x56AB2779D884271D) /* 226 */, _ULL(0xBE83E1B0FF1525AF) /* 227 */,
+ _ULL(0xFB7C65D4217E49A9) /* 228 */, _ULL(0x6BDBE0E76D48E7D4) /* 229 */,
+ _ULL(0x08DF828745D9179E) /* 230 */, _ULL(0x22EA6A9ADD53BD34) /* 231 */,
+ _ULL(0xE36E141C5622200A) /* 232 */, _ULL(0x7F805D1B8CB750EE) /* 233 */,
+ _ULL(0xAFE5C7A59F58E837) /* 234 */, _ULL(0xE27F996A4FB1C23C) /* 235 */,
+ _ULL(0xD3867DFB0775F0D0) /* 236 */, _ULL(0xD0E673DE6E88891A) /* 237 */,
+ _ULL(0x123AEB9EAFB86C25) /* 238 */, _ULL(0x30F1D5D5C145B895) /* 239 */,
+ _ULL(0xBB434A2DEE7269E7) /* 240 */, _ULL(0x78CB67ECF931FA38) /* 241 */,
+ _ULL(0xF33B0372323BBF9C) /* 242 */, _ULL(0x52D66336FB279C74) /* 243 */,
+ _ULL(0x505F33AC0AFB4EAA) /* 244 */, _ULL(0xE8A5CD99A2CCE187) /* 245 */,
+ _ULL(0x534974801E2D30BB) /* 246 */, _ULL(0x8D2D5711D5876D90) /* 247 */,
+ _ULL(0x1F1A412891BC038E) /* 248 */, _ULL(0xD6E2E71D82E56648) /* 249 */,
+ _ULL(0x74036C3A497732B7) /* 250 */, _ULL(0x89B67ED96361F5AB) /* 251 */,
+ _ULL(0xFFED95D8F1EA02A2) /* 252 */, _ULL(0xE72B3BD61464D43D) /* 253 */,
+ _ULL(0xA6300F170BDC4820) /* 254 */, _ULL(0xEBC18760ED78A77A) /* 255 */,
+ _ULL(0xE6A6BE5A05A12138) /* 256 */, _ULL(0xB5A122A5B4F87C98) /* 257 */,
+ _ULL(0x563C6089140B6990) /* 258 */, _ULL(0x4C46CB2E391F5DD5) /* 259 */,
+ _ULL(0xD932ADDBC9B79434) /* 260 */, _ULL(0x08EA70E42015AFF5) /* 261 */,
+ _ULL(0xD765A6673E478CF1) /* 262 */, _ULL(0xC4FB757EAB278D99) /* 263 */,
+ _ULL(0xDF11C6862D6E0692) /* 264 */, _ULL(0xDDEB84F10D7F3B16) /* 265 */,
+ _ULL(0x6F2EF604A665EA04) /* 266 */, _ULL(0x4A8E0F0FF0E0DFB3) /* 267 */,
+ _ULL(0xA5EDEEF83DBCBA51) /* 268 */, _ULL(0xFC4F0A2A0EA4371E) /* 269 */,
+ _ULL(0xE83E1DA85CB38429) /* 270 */, _ULL(0xDC8FF882BA1B1CE2) /* 271 */,
+ _ULL(0xCD45505E8353E80D) /* 272 */, _ULL(0x18D19A00D4DB0717) /* 273 */,
+ _ULL(0x34A0CFEDA5F38101) /* 274 */, _ULL(0x0BE77E518887CAF2) /* 275 */,
+ _ULL(0x1E341438B3C45136) /* 276 */, _ULL(0xE05797F49089CCF9) /* 277 */,
+ _ULL(0xFFD23F9DF2591D14) /* 278 */, _ULL(0x543DDA228595C5CD) /* 279 */,
+ _ULL(0x661F81FD99052A33) /* 280 */, _ULL(0x8736E641DB0F7B76) /* 281 */,
+ _ULL(0x15227725418E5307) /* 282 */, _ULL(0xE25F7F46162EB2FA) /* 283 */,
+ _ULL(0x48A8B2126C13D9FE) /* 284 */, _ULL(0xAFDC541792E76EEA) /* 285 */,
+ _ULL(0x03D912BFC6D1898F) /* 286 */, _ULL(0x31B1AAFA1B83F51B) /* 287 */,
+ _ULL(0xF1AC2796E42AB7D9) /* 288 */, _ULL(0x40A3A7D7FCD2EBAC) /* 289 */,
+ _ULL(0x1056136D0AFBBCC5) /* 290 */, _ULL(0x7889E1DD9A6D0C85) /* 291 */,
+ _ULL(0xD33525782A7974AA) /* 292 */, _ULL(0xA7E25D09078AC09B) /* 293 */,
+ _ULL(0xBD4138B3EAC6EDD0) /* 294 */, _ULL(0x920ABFBE71EB9E70) /* 295 */,
+ _ULL(0xA2A5D0F54FC2625C) /* 296 */, _ULL(0xC054E36B0B1290A3) /* 297 */,
+ _ULL(0xF6DD59FF62FE932B) /* 298 */, _ULL(0x3537354511A8AC7D) /* 299 */,
+ _ULL(0xCA845E9172FADCD4) /* 300 */, _ULL(0x84F82B60329D20DC) /* 301 */,
+ _ULL(0x79C62CE1CD672F18) /* 302 */, _ULL(0x8B09A2ADD124642C) /* 303 */,
+ _ULL(0xD0C1E96A19D9E726) /* 304 */, _ULL(0x5A786A9B4BA9500C) /* 305 */,
+ _ULL(0x0E020336634C43F3) /* 306 */, _ULL(0xC17B474AEB66D822) /* 307 */,
+ _ULL(0x6A731AE3EC9BAAC2) /* 308 */, _ULL(0x8226667AE0840258) /* 309 */,
+ _ULL(0x67D4567691CAECA5) /* 310 */, _ULL(0x1D94155C4875ADB5) /* 311 */,
+ _ULL(0x6D00FD985B813FDF) /* 312 */, _ULL(0x51286EFCB774CD06) /* 313 */,
+ _ULL(0x5E8834471FA744AF) /* 314 */, _ULL(0xF72CA0AEE761AE2E) /* 315 */,
+ _ULL(0xBE40E4CDAEE8E09A) /* 316 */, _ULL(0xE9970BBB5118F665) /* 317 */,
+ _ULL(0x726E4BEB33DF1964) /* 318 */, _ULL(0x703B000729199762) /* 319 */,
+ _ULL(0x4631D816F5EF30A7) /* 320 */, _ULL(0xB880B5B51504A6BE) /* 321 */,
+ _ULL(0x641793C37ED84B6C) /* 322 */, _ULL(0x7B21ED77F6E97D96) /* 323 */,
+ _ULL(0x776306312EF96B73) /* 324 */, _ULL(0xAE528948E86FF3F4) /* 325 */,
+ _ULL(0x53DBD7F286A3F8F8) /* 326 */, _ULL(0x16CADCE74CFC1063) /* 327 */,
+ _ULL(0x005C19BDFA52C6DD) /* 328 */, _ULL(0x68868F5D64D46AD3) /* 329 */,
+ _ULL(0x3A9D512CCF1E186A) /* 330 */, _ULL(0x367E62C2385660AE) /* 331 */,
+ _ULL(0xE359E7EA77DCB1D7) /* 332 */, _ULL(0x526C0773749ABE6E) /* 333 */,
+ _ULL(0x735AE5F9D09F734B) /* 334 */, _ULL(0x493FC7CC8A558BA8) /* 335 */,
+ _ULL(0xB0B9C1533041AB45) /* 336 */, _ULL(0x321958BA470A59BD) /* 337 */,
+ _ULL(0x852DB00B5F46C393) /* 338 */, _ULL(0x91209B2BD336B0E5) /* 339 */,
+ _ULL(0x6E604F7D659EF19F) /* 340 */, _ULL(0xB99A8AE2782CCB24) /* 341 */,
+ _ULL(0xCCF52AB6C814C4C7) /* 342 */, _ULL(0x4727D9AFBE11727B) /* 343 */,
+ _ULL(0x7E950D0C0121B34D) /* 344 */, _ULL(0x756F435670AD471F) /* 345 */,
+ _ULL(0xF5ADD442615A6849) /* 346 */, _ULL(0x4E87E09980B9957A) /* 347 */,
+ _ULL(0x2ACFA1DF50AEE355) /* 348 */, _ULL(0xD898263AFD2FD556) /* 349 */,
+ _ULL(0xC8F4924DD80C8FD6) /* 350 */, _ULL(0xCF99CA3D754A173A) /* 351 */,
+ _ULL(0xFE477BACAF91BF3C) /* 352 */, _ULL(0xED5371F6D690C12D) /* 353 */,
+ _ULL(0x831A5C285E687094) /* 354 */, _ULL(0xC5D3C90A3708A0A4) /* 355 */,
+ _ULL(0x0F7F903717D06580) /* 356 */, _ULL(0x19F9BB13B8FDF27F) /* 357 */,
+ _ULL(0xB1BD6F1B4D502843) /* 358 */, _ULL(0x1C761BA38FFF4012) /* 359 */,
+ _ULL(0x0D1530C4E2E21F3B) /* 360 */, _ULL(0x8943CE69A7372C8A) /* 361 */,
+ _ULL(0xE5184E11FEB5CE66) /* 362 */, _ULL(0x618BDB80BD736621) /* 363 */,
+ _ULL(0x7D29BAD68B574D0B) /* 364 */, _ULL(0x81BB613E25E6FE5B) /* 365 */,
+ _ULL(0x071C9C10BC07913F) /* 366 */, _ULL(0xC7BEEB7909AC2D97) /* 367 */,
+ _ULL(0xC3E58D353BC5D757) /* 368 */, _ULL(0xEB017892F38F61E8) /* 369 */,
+ _ULL(0xD4EFFB9C9B1CC21A) /* 370 */, _ULL(0x99727D26F494F7AB) /* 371 */,
+ _ULL(0xA3E063A2956B3E03) /* 372 */, _ULL(0x9D4A8B9A4AA09C30) /* 373 */,
+ _ULL(0x3F6AB7D500090FB4) /* 374 */, _ULL(0x9CC0F2A057268AC0) /* 375 */,
+ _ULL(0x3DEE9D2DEDBF42D1) /* 376 */, _ULL(0x330F49C87960A972) /* 377 */,
+ _ULL(0xC6B2720287421B41) /* 378 */, _ULL(0x0AC59EC07C00369C) /* 379 */,
+ _ULL(0xEF4EAC49CB353425) /* 380 */, _ULL(0xF450244EEF0129D8) /* 381 */,
+ _ULL(0x8ACC46E5CAF4DEB6) /* 382 */, _ULL(0x2FFEAB63989263F7) /* 383 */,
+ _ULL(0x8F7CB9FE5D7A4578) /* 384 */, _ULL(0x5BD8F7644E634635) /* 385 */,
+ _ULL(0x427A7315BF2DC900) /* 386 */, _ULL(0x17D0C4AA2125261C) /* 387 */,
+ _ULL(0x3992486C93518E50) /* 388 */, _ULL(0xB4CBFEE0A2D7D4C3) /* 389 */,
+ _ULL(0x7C75D6202C5DDD8D) /* 390 */, _ULL(0xDBC295D8E35B6C61) /* 391 */,
+ _ULL(0x60B369D302032B19) /* 392 */, _ULL(0xCE42685FDCE44132) /* 393 */,
+ _ULL(0x06F3DDB9DDF65610) /* 394 */, _ULL(0x8EA4D21DB5E148F0) /* 395 */,
+ _ULL(0x20B0FCE62FCD496F) /* 396 */, _ULL(0x2C1B912358B0EE31) /* 397 */,
+ _ULL(0xB28317B818F5A308) /* 398 */, _ULL(0xA89C1E189CA6D2CF) /* 399 */,
+ _ULL(0x0C6B18576AAADBC8) /* 400 */, _ULL(0xB65DEAA91299FAE3) /* 401 */,
+ _ULL(0xFB2B794B7F1027E7) /* 402 */, _ULL(0x04E4317F443B5BEB) /* 403 */,
+ _ULL(0x4B852D325939D0A6) /* 404 */, _ULL(0xD5AE6BEEFB207FFC) /* 405 */,
+ _ULL(0x309682B281C7D374) /* 406 */, _ULL(0xBAE309A194C3B475) /* 407 */,
+ _ULL(0x8CC3F97B13B49F05) /* 408 */, _ULL(0x98A9422FF8293967) /* 409 */,
+ _ULL(0x244B16B01076FF7C) /* 410 */, _ULL(0xF8BF571C663D67EE) /* 411 */,
+ _ULL(0x1F0D6758EEE30DA1) /* 412 */, _ULL(0xC9B611D97ADEB9B7) /* 413 */,
+ _ULL(0xB7AFD5887B6C57A2) /* 414 */, _ULL(0x6290AE846B984FE1) /* 415 */,
+ _ULL(0x94DF4CDEACC1A5FD) /* 416 */, _ULL(0x058A5BD1C5483AFF) /* 417 */,
+ _ULL(0x63166CC142BA3C37) /* 418 */, _ULL(0x8DB8526EB2F76F40) /* 419 */,
+ _ULL(0xE10880036F0D6D4E) /* 420 */, _ULL(0x9E0523C9971D311D) /* 421 */,
+ _ULL(0x45EC2824CC7CD691) /* 422 */, _ULL(0x575B8359E62382C9) /* 423 */,
+ _ULL(0xFA9E400DC4889995) /* 424 */, _ULL(0xD1823ECB45721568) /* 425 */,
+ _ULL(0xDAFD983B8206082F) /* 426 */, _ULL(0xAA7D29082386A8CB) /* 427 */,
+ _ULL(0x269FCD4403B87588) /* 428 */, _ULL(0x1B91F5F728BDD1E0) /* 429 */,
+ _ULL(0xE4669F39040201F6) /* 430 */, _ULL(0x7A1D7C218CF04ADE) /* 431 */,
+ _ULL(0x65623C29D79CE5CE) /* 432 */, _ULL(0x2368449096C00BB1) /* 433 */,
+ _ULL(0xAB9BF1879DA503BA) /* 434 */, _ULL(0xBC23ECB1A458058E) /* 435 */,
+ _ULL(0x9A58DF01BB401ECC) /* 436 */, _ULL(0xA070E868A85F143D) /* 437 */,
+ _ULL(0x4FF188307DF2239E) /* 438 */, _ULL(0x14D565B41A641183) /* 439 */,
+ _ULL(0xEE13337452701602) /* 440 */, _ULL(0x950E3DCF3F285E09) /* 441 */,
+ _ULL(0x59930254B9C80953) /* 442 */, _ULL(0x3BF299408930DA6D) /* 443 */,
+ _ULL(0xA955943F53691387) /* 444 */, _ULL(0xA15EDECAA9CB8784) /* 445 */,
+ _ULL(0x29142127352BE9A0) /* 446 */, _ULL(0x76F0371FFF4E7AFB) /* 447 */,
+ _ULL(0x0239F450274F2228) /* 448 */, _ULL(0xBB073AF01D5E868B) /* 449 */,
+ _ULL(0xBFC80571C10E96C1) /* 450 */, _ULL(0xD267088568222E23) /* 451 */,
+ _ULL(0x9671A3D48E80B5B0) /* 452 */, _ULL(0x55B5D38AE193BB81) /* 453 */,
+ _ULL(0x693AE2D0A18B04B8) /* 454 */, _ULL(0x5C48B4ECADD5335F) /* 455 */,
+ _ULL(0xFD743B194916A1CA) /* 456 */, _ULL(0x2577018134BE98C4) /* 457 */,
+ _ULL(0xE77987E83C54A4AD) /* 458 */, _ULL(0x28E11014DA33E1B9) /* 459 */,
+ _ULL(0x270CC59E226AA213) /* 460 */, _ULL(0x71495F756D1A5F60) /* 461 */,
+ _ULL(0x9BE853FB60AFEF77) /* 462 */, _ULL(0xADC786A7F7443DBF) /* 463 */,
+ _ULL(0x0904456173B29A82) /* 464 */, _ULL(0x58BC7A66C232BD5E) /* 465 */,
+ _ULL(0xF306558C673AC8B2) /* 466 */, _ULL(0x41F639C6B6C9772A) /* 467 */,
+ _ULL(0x216DEFE99FDA35DA) /* 468 */, _ULL(0x11640CC71C7BE615) /* 469 */,
+ _ULL(0x93C43694565C5527) /* 470 */, _ULL(0xEA038E6246777839) /* 471 */,
+ _ULL(0xF9ABF3CE5A3E2469) /* 472 */, _ULL(0x741E768D0FD312D2) /* 473 */,
+ _ULL(0x0144B883CED652C6) /* 474 */, _ULL(0xC20B5A5BA33F8552) /* 475 */,
+ _ULL(0x1AE69633C3435A9D) /* 476 */, _ULL(0x97A28CA4088CFDEC) /* 477 */,
+ _ULL(0x8824A43C1E96F420) /* 478 */, _ULL(0x37612FA66EEEA746) /* 479 */,
+ _ULL(0x6B4CB165F9CF0E5A) /* 480 */, _ULL(0x43AA1C06A0ABFB4A) /* 481 */,
+ _ULL(0x7F4DC26FF162796B) /* 482 */, _ULL(0x6CBACC8E54ED9B0F) /* 483 */,
+ _ULL(0xA6B7FFEFD2BB253E) /* 484 */, _ULL(0x2E25BC95B0A29D4F) /* 485 */,
+ _ULL(0x86D6A58BDEF1388C) /* 486 */, _ULL(0xDED74AC576B6F054) /* 487 */,
+ _ULL(0x8030BDBC2B45805D) /* 488 */, _ULL(0x3C81AF70E94D9289) /* 489 */,
+ _ULL(0x3EFF6DDA9E3100DB) /* 490 */, _ULL(0xB38DC39FDFCC8847) /* 491 */,
+ _ULL(0x123885528D17B87E) /* 492 */, _ULL(0xF2DA0ED240B1B642) /* 493 */,
+ _ULL(0x44CEFADCD54BF9A9) /* 494 */, _ULL(0x1312200E433C7EE6) /* 495 */,
+ _ULL(0x9FFCC84F3A78C748) /* 496 */, _ULL(0xF0CD1F72248576BB) /* 497 */,
+ _ULL(0xEC6974053638CFE4) /* 498 */, _ULL(0x2BA7B67C0CEC4E4C) /* 499 */,
+ _ULL(0xAC2F4DF3E5CE32ED) /* 500 */, _ULL(0xCB33D14326EA4C11) /* 501 */,
+ _ULL(0xA4E9044CC77E58BC) /* 502 */, _ULL(0x5F513293D934FCEF) /* 503 */,
+ _ULL(0x5DC9645506E55444) /* 504 */, _ULL(0x50DE418F317DE40A) /* 505 */,
+ _ULL(0x388CB31A69DDE259) /* 506 */, _ULL(0x2DB4A83455820A86) /* 507 */,
+ _ULL(0x9010A91E84711AE9) /* 508 */, _ULL(0x4DF7F0B7B1498371) /* 509 */,
+ _ULL(0xD62A2EABC0977179) /* 510 */, _ULL(0x22FAC097AA8D5C0E) /* 511 */,
+ _ULL(0xF49FCC2FF1DAF39B) /* 512 */, _ULL(0x487FD5C66FF29281) /* 513 */,
+ _ULL(0xE8A30667FCDCA83F) /* 514 */, _ULL(0x2C9B4BE3D2FCCE63) /* 515 */,
+ _ULL(0xDA3FF74B93FBBBC2) /* 516 */, _ULL(0x2FA165D2FE70BA66) /* 517 */,
+ _ULL(0xA103E279970E93D4) /* 518 */, _ULL(0xBECDEC77B0E45E71) /* 519 */,
+ _ULL(0xCFB41E723985E497) /* 520 */, _ULL(0xB70AAA025EF75017) /* 521 */,
+ _ULL(0xD42309F03840B8E0) /* 522 */, _ULL(0x8EFC1AD035898579) /* 523 */,
+ _ULL(0x96C6920BE2B2ABC5) /* 524 */, _ULL(0x66AF4163375A9172) /* 525 */,
+ _ULL(0x2174ABDCCA7127FB) /* 526 */, _ULL(0xB33CCEA64A72FF41) /* 527 */,
+ _ULL(0xF04A4933083066A5) /* 528 */, _ULL(0x8D970ACDD7289AF5) /* 529 */,
+ _ULL(0x8F96E8E031C8C25E) /* 530 */, _ULL(0xF3FEC02276875D47) /* 531 */,
+ _ULL(0xEC7BF310056190DD) /* 532 */, _ULL(0xF5ADB0AEBB0F1491) /* 533 */,
+ _ULL(0x9B50F8850FD58892) /* 534 */, _ULL(0x4975488358B74DE8) /* 535 */,
+ _ULL(0xA3354FF691531C61) /* 536 */, _ULL(0x0702BBE481D2C6EE) /* 537 */,
+ _ULL(0x89FB24057DEDED98) /* 538 */, _ULL(0xAC3075138596E902) /* 539 */,
+ _ULL(0x1D2D3580172772ED) /* 540 */, _ULL(0xEB738FC28E6BC30D) /* 541 */,
+ _ULL(0x5854EF8F63044326) /* 542 */, _ULL(0x9E5C52325ADD3BBE) /* 543 */,
+ _ULL(0x90AA53CF325C4623) /* 544 */, _ULL(0xC1D24D51349DD067) /* 545 */,
+ _ULL(0x2051CFEEA69EA624) /* 546 */, _ULL(0x13220F0A862E7E4F) /* 547 */,
+ _ULL(0xCE39399404E04864) /* 548 */, _ULL(0xD9C42CA47086FCB7) /* 549 */,
+ _ULL(0x685AD2238A03E7CC) /* 550 */, _ULL(0x066484B2AB2FF1DB) /* 551 */,
+ _ULL(0xFE9D5D70EFBF79EC) /* 552 */, _ULL(0x5B13B9DD9C481854) /* 553 */,
+ _ULL(0x15F0D475ED1509AD) /* 554 */, _ULL(0x0BEBCD060EC79851) /* 555 */,
+ _ULL(0xD58C6791183AB7F8) /* 556 */, _ULL(0xD1187C5052F3EEE4) /* 557 */,
+ _ULL(0xC95D1192E54E82FF) /* 558 */, _ULL(0x86EEA14CB9AC6CA2) /* 559 */,
+ _ULL(0x3485BEB153677D5D) /* 560 */, _ULL(0xDD191D781F8C492A) /* 561 */,
+ _ULL(0xF60866BAA784EBF9) /* 562 */, _ULL(0x518F643BA2D08C74) /* 563 */,
+ _ULL(0x8852E956E1087C22) /* 564 */, _ULL(0xA768CB8DC410AE8D) /* 565 */,
+ _ULL(0x38047726BFEC8E1A) /* 566 */, _ULL(0xA67738B4CD3B45AA) /* 567 */,
+ _ULL(0xAD16691CEC0DDE19) /* 568 */, _ULL(0xC6D4319380462E07) /* 569 */,
+ _ULL(0xC5A5876D0BA61938) /* 570 */, _ULL(0x16B9FA1FA58FD840) /* 571 */,
+ _ULL(0x188AB1173CA74F18) /* 572 */, _ULL(0xABDA2F98C99C021F) /* 573 */,
+ _ULL(0x3E0580AB134AE816) /* 574 */, _ULL(0x5F3B05B773645ABB) /* 575 */,
+ _ULL(0x2501A2BE5575F2F6) /* 576 */, _ULL(0x1B2F74004E7E8BA9) /* 577 */,
+ _ULL(0x1CD7580371E8D953) /* 578 */, _ULL(0x7F6ED89562764E30) /* 579 */,
+ _ULL(0xB15926FF596F003D) /* 580 */, _ULL(0x9F65293DA8C5D6B9) /* 581 */,
+ _ULL(0x6ECEF04DD690F84C) /* 582 */, _ULL(0x4782275FFF33AF88) /* 583 */,
+ _ULL(0xE41433083F820801) /* 584 */, _ULL(0xFD0DFE409A1AF9B5) /* 585 */,
+ _ULL(0x4325A3342CDB396B) /* 586 */, _ULL(0x8AE77E62B301B252) /* 587 */,
+ _ULL(0xC36F9E9F6655615A) /* 588 */, _ULL(0x85455A2D92D32C09) /* 589 */,
+ _ULL(0xF2C7DEA949477485) /* 590 */, _ULL(0x63CFB4C133A39EBA) /* 591 */,
+ _ULL(0x83B040CC6EBC5462) /* 592 */, _ULL(0x3B9454C8FDB326B0) /* 593 */,
+ _ULL(0x56F56A9E87FFD78C) /* 594 */, _ULL(0x2DC2940D99F42BC6) /* 595 */,
+ _ULL(0x98F7DF096B096E2D) /* 596 */, _ULL(0x19A6E01E3AD852BF) /* 597 */,
+ _ULL(0x42A99CCBDBD4B40B) /* 598 */, _ULL(0xA59998AF45E9C559) /* 599 */,
+ _ULL(0x366295E807D93186) /* 600 */, _ULL(0x6B48181BFAA1F773) /* 601 */,
+ _ULL(0x1FEC57E2157A0A1D) /* 602 */, _ULL(0x4667446AF6201AD5) /* 603 */,
+ _ULL(0xE615EBCACFB0F075) /* 604 */, _ULL(0xB8F31F4F68290778) /* 605 */,
+ _ULL(0x22713ED6CE22D11E) /* 606 */, _ULL(0x3057C1A72EC3C93B) /* 607 */,
+ _ULL(0xCB46ACC37C3F1F2F) /* 608 */, _ULL(0xDBB893FD02AAF50E) /* 609 */,
+ _ULL(0x331FD92E600B9FCF) /* 610 */, _ULL(0xA498F96148EA3AD6) /* 611 */,
+ _ULL(0xA8D8426E8B6A83EA) /* 612 */, _ULL(0xA089B274B7735CDC) /* 613 */,
+ _ULL(0x87F6B3731E524A11) /* 614 */, _ULL(0x118808E5CBC96749) /* 615 */,
+ _ULL(0x9906E4C7B19BD394) /* 616 */, _ULL(0xAFED7F7E9B24A20C) /* 617 */,
+ _ULL(0x6509EADEEB3644A7) /* 618 */, _ULL(0x6C1EF1D3E8EF0EDE) /* 619 */,
+ _ULL(0xB9C97D43E9798FB4) /* 620 */, _ULL(0xA2F2D784740C28A3) /* 621 */,
+ _ULL(0x7B8496476197566F) /* 622 */, _ULL(0x7A5BE3E6B65F069D) /* 623 */,
+ _ULL(0xF96330ED78BE6F10) /* 624 */, _ULL(0xEEE60DE77A076A15) /* 625 */,
+ _ULL(0x2B4BEE4AA08B9BD0) /* 626 */, _ULL(0x6A56A63EC7B8894E) /* 627 */,
+ _ULL(0x02121359BA34FEF4) /* 628 */, _ULL(0x4CBF99F8283703FC) /* 629 */,
+ _ULL(0x398071350CAF30C8) /* 630 */, _ULL(0xD0A77A89F017687A) /* 631 */,
+ _ULL(0xF1C1A9EB9E423569) /* 632 */, _ULL(0x8C7976282DEE8199) /* 633 */,
+ _ULL(0x5D1737A5DD1F7ABD) /* 634 */, _ULL(0x4F53433C09A9FA80) /* 635 */,
+ _ULL(0xFA8B0C53DF7CA1D9) /* 636 */, _ULL(0x3FD9DCBC886CCB77) /* 637 */,
+ _ULL(0xC040917CA91B4720) /* 638 */, _ULL(0x7DD00142F9D1DCDF) /* 639 */,
+ _ULL(0x8476FC1D4F387B58) /* 640 */, _ULL(0x23F8E7C5F3316503) /* 641 */,
+ _ULL(0x032A2244E7E37339) /* 642 */, _ULL(0x5C87A5D750F5A74B) /* 643 */,
+ _ULL(0x082B4CC43698992E) /* 644 */, _ULL(0xDF917BECB858F63C) /* 645 */,
+ _ULL(0x3270B8FC5BF86DDA) /* 646 */, _ULL(0x10AE72BB29B5DD76) /* 647 */,
+ _ULL(0x576AC94E7700362B) /* 648 */, _ULL(0x1AD112DAC61EFB8F) /* 649 */,
+ _ULL(0x691BC30EC5FAA427) /* 650 */, _ULL(0xFF246311CC327143) /* 651 */,
+ _ULL(0x3142368E30E53206) /* 652 */, _ULL(0x71380E31E02CA396) /* 653 */,
+ _ULL(0x958D5C960AAD76F1) /* 654 */, _ULL(0xF8D6F430C16DA536) /* 655 */,
+ _ULL(0xC8FFD13F1BE7E1D2) /* 656 */, _ULL(0x7578AE66004DDBE1) /* 657 */,
+ _ULL(0x05833F01067BE646) /* 658 */, _ULL(0xBB34B5AD3BFE586D) /* 659 */,
+ _ULL(0x095F34C9A12B97F0) /* 660 */, _ULL(0x247AB64525D60CA8) /* 661 */,
+ _ULL(0xDCDBC6F3017477D1) /* 662 */, _ULL(0x4A2E14D4DECAD24D) /* 663 */,
+ _ULL(0xBDB5E6D9BE0A1EEB) /* 664 */, _ULL(0x2A7E70F7794301AB) /* 665 */,
+ _ULL(0xDEF42D8A270540FD) /* 666 */, _ULL(0x01078EC0A34C22C1) /* 667 */,
+ _ULL(0xE5DE511AF4C16387) /* 668 */, _ULL(0x7EBB3A52BD9A330A) /* 669 */,
+ _ULL(0x77697857AA7D6435) /* 670 */, _ULL(0x004E831603AE4C32) /* 671 */,
+ _ULL(0xE7A21020AD78E312) /* 672 */, _ULL(0x9D41A70C6AB420F2) /* 673 */,
+ _ULL(0x28E06C18EA1141E6) /* 674 */, _ULL(0xD2B28CBD984F6B28) /* 675 */,
+ _ULL(0x26B75F6C446E9D83) /* 676 */, _ULL(0xBA47568C4D418D7F) /* 677 */,
+ _ULL(0xD80BADBFE6183D8E) /* 678 */, _ULL(0x0E206D7F5F166044) /* 679 */,
+ _ULL(0xE258A43911CBCA3E) /* 680 */, _ULL(0x723A1746B21DC0BC) /* 681 */,
+ _ULL(0xC7CAA854F5D7CDD3) /* 682 */, _ULL(0x7CAC32883D261D9C) /* 683 */,
+ _ULL(0x7690C26423BA942C) /* 684 */, _ULL(0x17E55524478042B8) /* 685 */,
+ _ULL(0xE0BE477656A2389F) /* 686 */, _ULL(0x4D289B5E67AB2DA0) /* 687 */,
+ _ULL(0x44862B9C8FBBFD31) /* 688 */, _ULL(0xB47CC8049D141365) /* 689 */,
+ _ULL(0x822C1B362B91C793) /* 690 */, _ULL(0x4EB14655FB13DFD8) /* 691 */,
+ _ULL(0x1ECBBA0714E2A97B) /* 692 */, _ULL(0x6143459D5CDE5F14) /* 693 */,
+ _ULL(0x53A8FBF1D5F0AC89) /* 694 */, _ULL(0x97EA04D81C5E5B00) /* 695 */,
+ _ULL(0x622181A8D4FDB3F3) /* 696 */, _ULL(0xE9BCD341572A1208) /* 697 */,
+ _ULL(0x1411258643CCE58A) /* 698 */, _ULL(0x9144C5FEA4C6E0A4) /* 699 */,
+ _ULL(0x0D33D06565CF620F) /* 700 */, _ULL(0x54A48D489F219CA1) /* 701 */,
+ _ULL(0xC43E5EAC6D63C821) /* 702 */, _ULL(0xA9728B3A72770DAF) /* 703 */,
+ _ULL(0xD7934E7B20DF87EF) /* 704 */, _ULL(0xE35503B61A3E86E5) /* 705 */,
+ _ULL(0xCAE321FBC819D504) /* 706 */, _ULL(0x129A50B3AC60BFA6) /* 707 */,
+ _ULL(0xCD5E68EA7E9FB6C3) /* 708 */, _ULL(0xB01C90199483B1C7) /* 709 */,
+ _ULL(0x3DE93CD5C295376C) /* 710 */, _ULL(0xAED52EDF2AB9AD13) /* 711 */,
+ _ULL(0x2E60F512C0A07884) /* 712 */, _ULL(0xBC3D86A3E36210C9) /* 713 */,
+ _ULL(0x35269D9B163951CE) /* 714 */, _ULL(0x0C7D6E2AD0CDB5FA) /* 715 */,
+ _ULL(0x59E86297D87F5733) /* 716 */, _ULL(0x298EF221898DB0E7) /* 717 */,
+ _ULL(0x55000029D1A5AA7E) /* 718 */, _ULL(0x8BC08AE1B5061B45) /* 719 */,
+ _ULL(0xC2C31C2B6C92703A) /* 720 */, _ULL(0x94CC596BAF25EF42) /* 721 */,
+ _ULL(0x0A1D73DB22540456) /* 722 */, _ULL(0x04B6A0F9D9C4179A) /* 723 */,
+ _ULL(0xEFFDAFA2AE3D3C60) /* 724 */, _ULL(0xF7C8075BB49496C4) /* 725 */,
+ _ULL(0x9CC5C7141D1CD4E3) /* 726 */, _ULL(0x78BD1638218E5534) /* 727 */,
+ _ULL(0xB2F11568F850246A) /* 728 */, _ULL(0xEDFABCFA9502BC29) /* 729 */,
+ _ULL(0x796CE5F2DA23051B) /* 730 */, _ULL(0xAAE128B0DC93537C) /* 731 */,
+ _ULL(0x3A493DA0EE4B29AE) /* 732 */, _ULL(0xB5DF6B2C416895D7) /* 733 */,
+ _ULL(0xFCABBD25122D7F37) /* 734 */, _ULL(0x70810B58105DC4B1) /* 735 */,
+ _ULL(0xE10FDD37F7882A90) /* 736 */, _ULL(0x524DCAB5518A3F5C) /* 737 */,
+ _ULL(0x3C9E85878451255B) /* 738 */, _ULL(0x4029828119BD34E2) /* 739 */,
+ _ULL(0x74A05B6F5D3CECCB) /* 740 */, _ULL(0xB610021542E13ECA) /* 741 */,
+ _ULL(0x0FF979D12F59E2AC) /* 742 */, _ULL(0x6037DA27E4F9CC50) /* 743 */,
+ _ULL(0x5E92975A0DF1847D) /* 744 */, _ULL(0xD66DE190D3E623FE) /* 745 */,
+ _ULL(0x5032D6B87B568048) /* 746 */, _ULL(0x9A36B7CE8235216E) /* 747 */,
+ _ULL(0x80272A7A24F64B4A) /* 748 */, _ULL(0x93EFED8B8C6916F7) /* 749 */,
+ _ULL(0x37DDBFF44CCE1555) /* 750 */, _ULL(0x4B95DB5D4B99BD25) /* 751 */,
+ _ULL(0x92D3FDA169812FC0) /* 752 */, _ULL(0xFB1A4A9A90660BB6) /* 753 */,
+ _ULL(0x730C196946A4B9B2) /* 754 */, _ULL(0x81E289AA7F49DA68) /* 755 */,
+ _ULL(0x64669A0F83B1A05F) /* 756 */, _ULL(0x27B3FF7D9644F48B) /* 757 */,
+ _ULL(0xCC6B615C8DB675B3) /* 758 */, _ULL(0x674F20B9BCEBBE95) /* 759 */,
+ _ULL(0x6F31238275655982) /* 760 */, _ULL(0x5AE488713E45CF05) /* 761 */,
+ _ULL(0xBF619F9954C21157) /* 762 */, _ULL(0xEABAC46040A8EAE9) /* 763 */,
+ _ULL(0x454C6FE9F2C0C1CD) /* 764 */, _ULL(0x419CF6496412691C) /* 765 */,
+ _ULL(0xD3DC3BEF265B0F70) /* 766 */, _ULL(0x6D0E60F5C3578A9E) /* 767 */,
+ _ULL(0x5B0E608526323C55) /* 768 */, _ULL(0x1A46C1A9FA1B59F5) /* 769 */,
+ _ULL(0xA9E245A17C4C8FFA) /* 770 */, _ULL(0x65CA5159DB2955D7) /* 771 */,
+ _ULL(0x05DB0A76CE35AFC2) /* 772 */, _ULL(0x81EAC77EA9113D45) /* 773 */,
+ _ULL(0x528EF88AB6AC0A0D) /* 774 */, _ULL(0xA09EA253597BE3FF) /* 775 */,
+ _ULL(0x430DDFB3AC48CD56) /* 776 */, _ULL(0xC4B3A67AF45CE46F) /* 777 */,
+ _ULL(0x4ECECFD8FBE2D05E) /* 778 */, _ULL(0x3EF56F10B39935F0) /* 779 */,
+ _ULL(0x0B22D6829CD619C6) /* 780 */, _ULL(0x17FD460A74DF2069) /* 781 */,
+ _ULL(0x6CF8CC8E8510ED40) /* 782 */, _ULL(0xD6C824BF3A6ECAA7) /* 783 */,
+ _ULL(0x61243D581A817049) /* 784 */, _ULL(0x048BACB6BBC163A2) /* 785 */,
+ _ULL(0xD9A38AC27D44CC32) /* 786 */, _ULL(0x7FDDFF5BAAF410AB) /* 787 */,
+ _ULL(0xAD6D495AA804824B) /* 788 */, _ULL(0xE1A6A74F2D8C9F94) /* 789 */,
+ _ULL(0xD4F7851235DEE8E3) /* 790 */, _ULL(0xFD4B7F886540D893) /* 791 */,
+ _ULL(0x247C20042AA4BFDA) /* 792 */, _ULL(0x096EA1C517D1327C) /* 793 */,
+ _ULL(0xD56966B4361A6685) /* 794 */, _ULL(0x277DA5C31221057D) /* 795 */,
+ _ULL(0x94D59893A43ACFF7) /* 796 */, _ULL(0x64F0C51CCDC02281) /* 797 */,
+ _ULL(0x3D33BCC4FF6189DB) /* 798 */, _ULL(0xE005CB184CE66AF1) /* 799 */,
+ _ULL(0xFF5CCD1D1DB99BEA) /* 800 */, _ULL(0xB0B854A7FE42980F) /* 801 */,
+ _ULL(0x7BD46A6A718D4B9F) /* 802 */, _ULL(0xD10FA8CC22A5FD8C) /* 803 */,
+ _ULL(0xD31484952BE4BD31) /* 804 */, _ULL(0xC7FA975FCB243847) /* 805 */,
+ _ULL(0x4886ED1E5846C407) /* 806 */, _ULL(0x28CDDB791EB70B04) /* 807 */,
+ _ULL(0xC2B00BE2F573417F) /* 808 */, _ULL(0x5C9590452180F877) /* 809 */,
+ _ULL(0x7A6BDDFFF370EB00) /* 810 */, _ULL(0xCE509E38D6D9D6A4) /* 811 */,
+ _ULL(0xEBEB0F00647FA702) /* 812 */, _ULL(0x1DCC06CF76606F06) /* 813 */,
+ _ULL(0xE4D9F28BA286FF0A) /* 814 */, _ULL(0xD85A305DC918C262) /* 815 */,
+ _ULL(0x475B1D8732225F54) /* 816 */, _ULL(0x2D4FB51668CCB5FE) /* 817 */,
+ _ULL(0xA679B9D9D72BBA20) /* 818 */, _ULL(0x53841C0D912D43A5) /* 819 */,
+ _ULL(0x3B7EAA48BF12A4E8) /* 820 */, _ULL(0x781E0E47F22F1DDF) /* 821 */,
+ _ULL(0xEFF20CE60AB50973) /* 822 */, _ULL(0x20D261D19DFFB742) /* 823 */,
+ _ULL(0x16A12B03062A2E39) /* 824 */, _ULL(0x1960EB2239650495) /* 825 */,
+ _ULL(0x251C16FED50EB8B8) /* 826 */, _ULL(0x9AC0C330F826016E) /* 827 */,
+ _ULL(0xED152665953E7671) /* 828 */, _ULL(0x02D63194A6369570) /* 829 */,
+ _ULL(0x5074F08394B1C987) /* 830 */, _ULL(0x70BA598C90B25CE1) /* 831 */,
+ _ULL(0x794A15810B9742F6) /* 832 */, _ULL(0x0D5925E9FCAF8C6C) /* 833 */,
+ _ULL(0x3067716CD868744E) /* 834 */, _ULL(0x910AB077E8D7731B) /* 835 */,
+ _ULL(0x6A61BBDB5AC42F61) /* 836 */, _ULL(0x93513EFBF0851567) /* 837 */,
+ _ULL(0xF494724B9E83E9D5) /* 838 */, _ULL(0xE887E1985C09648D) /* 839 */,
+ _ULL(0x34B1D3C675370CFD) /* 840 */, _ULL(0xDC35E433BC0D255D) /* 841 */,
+ _ULL(0xD0AAB84234131BE0) /* 842 */, _ULL(0x08042A50B48B7EAF) /* 843 */,
+ _ULL(0x9997C4EE44A3AB35) /* 844 */, _ULL(0x829A7B49201799D0) /* 845 */,
+ _ULL(0x263B8307B7C54441) /* 846 */, _ULL(0x752F95F4FD6A6CA6) /* 847 */,
+ _ULL(0x927217402C08C6E5) /* 848 */, _ULL(0x2A8AB754A795D9EE) /* 849 */,
+ _ULL(0xA442F7552F72943D) /* 850 */, _ULL(0x2C31334E19781208) /* 851 */,
+ _ULL(0x4FA98D7CEAEE6291) /* 852 */, _ULL(0x55C3862F665DB309) /* 853 */,
+ _ULL(0xBD0610175D53B1F3) /* 854 */, _ULL(0x46FE6CB840413F27) /* 855 */,
+ _ULL(0x3FE03792DF0CFA59) /* 856 */, _ULL(0xCFE700372EB85E8F) /* 857 */,
+ _ULL(0xA7BE29E7ADBCE118) /* 858 */, _ULL(0xE544EE5CDE8431DD) /* 859 */,
+ _ULL(0x8A781B1B41F1873E) /* 860 */, _ULL(0xA5C94C78A0D2F0E7) /* 861 */,
+ _ULL(0x39412E2877B60728) /* 862 */, _ULL(0xA1265EF3AFC9A62C) /* 863 */,
+ _ULL(0xBCC2770C6A2506C5) /* 864 */, _ULL(0x3AB66DD5DCE1CE12) /* 865 */,
+ _ULL(0xE65499D04A675B37) /* 866 */, _ULL(0x7D8F523481BFD216) /* 867 */,
+ _ULL(0x0F6F64FCEC15F389) /* 868 */, _ULL(0x74EFBE618B5B13C8) /* 869 */,
+ _ULL(0xACDC82B714273E1D) /* 870 */, _ULL(0xDD40BFE003199D17) /* 871 */,
+ _ULL(0x37E99257E7E061F8) /* 872 */, _ULL(0xFA52626904775AAA) /* 873 */,
+ _ULL(0x8BBBF63A463D56F9) /* 874 */, _ULL(0xF0013F1543A26E64) /* 875 */,
+ _ULL(0xA8307E9F879EC898) /* 876 */, _ULL(0xCC4C27A4150177CC) /* 877 */,
+ _ULL(0x1B432F2CCA1D3348) /* 878 */, _ULL(0xDE1D1F8F9F6FA013) /* 879 */,
+ _ULL(0x606602A047A7DDD6) /* 880 */, _ULL(0xD237AB64CC1CB2C7) /* 881 */,
+ _ULL(0x9B938E7225FCD1D3) /* 882 */, _ULL(0xEC4E03708E0FF476) /* 883 */,
+ _ULL(0xFEB2FBDA3D03C12D) /* 884 */, _ULL(0xAE0BCED2EE43889A) /* 885 */,
+ _ULL(0x22CB8923EBFB4F43) /* 886 */, _ULL(0x69360D013CF7396D) /* 887 */,
+ _ULL(0x855E3602D2D4E022) /* 888 */, _ULL(0x073805BAD01F784C) /* 889 */,
+ _ULL(0x33E17A133852F546) /* 890 */, _ULL(0xDF4874058AC7B638) /* 891 */,
+ _ULL(0xBA92B29C678AA14A) /* 892 */, _ULL(0x0CE89FC76CFAADCD) /* 893 */,
+ _ULL(0x5F9D4E0908339E34) /* 894 */, _ULL(0xF1AFE9291F5923B9) /* 895 */,
+ _ULL(0x6E3480F60F4A265F) /* 896 */, _ULL(0xEEBF3A2AB29B841C) /* 897 */,
+ _ULL(0xE21938A88F91B4AD) /* 898 */, _ULL(0x57DFEFF845C6D3C3) /* 899 */,
+ _ULL(0x2F006B0BF62CAAF2) /* 900 */, _ULL(0x62F479EF6F75EE78) /* 901 */,
+ _ULL(0x11A55AD41C8916A9) /* 902 */, _ULL(0xF229D29084FED453) /* 903 */,
+ _ULL(0x42F1C27B16B000E6) /* 904 */, _ULL(0x2B1F76749823C074) /* 905 */,
+ _ULL(0x4B76ECA3C2745360) /* 906 */, _ULL(0x8C98F463B91691BD) /* 907 */,
+ _ULL(0x14BCC93CF1ADE66A) /* 908 */, _ULL(0x8885213E6D458397) /* 909 */,
+ _ULL(0x8E177DF0274D4711) /* 910 */, _ULL(0xB49B73B5503F2951) /* 911 */,
+ _ULL(0x10168168C3F96B6B) /* 912 */, _ULL(0x0E3D963B63CAB0AE) /* 913 */,
+ _ULL(0x8DFC4B5655A1DB14) /* 914 */, _ULL(0xF789F1356E14DE5C) /* 915 */,
+ _ULL(0x683E68AF4E51DAC1) /* 916 */, _ULL(0xC9A84F9D8D4B0FD9) /* 917 */,
+ _ULL(0x3691E03F52A0F9D1) /* 918 */, _ULL(0x5ED86E46E1878E80) /* 919 */,
+ _ULL(0x3C711A0E99D07150) /* 920 */, _ULL(0x5A0865B20C4E9310) /* 921 */,
+ _ULL(0x56FBFC1FE4F0682E) /* 922 */, _ULL(0xEA8D5DE3105EDF9B) /* 923 */,
+ _ULL(0x71ABFDB12379187A) /* 924 */, _ULL(0x2EB99DE1BEE77B9C) /* 925 */,
+ _ULL(0x21ECC0EA33CF4523) /* 926 */, _ULL(0x59A4D7521805C7A1) /* 927 */,
+ _ULL(0x3896F5EB56AE7C72) /* 928 */, _ULL(0xAA638F3DB18F75DC) /* 929 */,
+ _ULL(0x9F39358DABE9808E) /* 930 */, _ULL(0xB7DEFA91C00B72AC) /* 931 */,
+ _ULL(0x6B5541FD62492D92) /* 932 */, _ULL(0x6DC6DEE8F92E4D5B) /* 933 */,
+ _ULL(0x353F57ABC4BEEA7E) /* 934 */, _ULL(0x735769D6DA5690CE) /* 935 */,
+ _ULL(0x0A234AA642391484) /* 936 */, _ULL(0xF6F9508028F80D9D) /* 937 */,
+ _ULL(0xB8E319A27AB3F215) /* 938 */, _ULL(0x31AD9C1151341A4D) /* 939 */,
+ _ULL(0x773C22A57BEF5805) /* 940 */, _ULL(0x45C7561A07968633) /* 941 */,
+ _ULL(0xF913DA9E249DBE36) /* 942 */, _ULL(0xDA652D9B78A64C68) /* 943 */,
+ _ULL(0x4C27A97F3BC334EF) /* 944 */, _ULL(0x76621220E66B17F4) /* 945 */,
+ _ULL(0x967743899ACD7D0B) /* 946 */, _ULL(0xF3EE5BCAE0ED6782) /* 947 */,
+ _ULL(0x409F753600C879FC) /* 948 */, _ULL(0x06D09A39B5926DB6) /* 949 */,
+ _ULL(0x6F83AEB0317AC588) /* 950 */, _ULL(0x01E6CA4A86381F21) /* 951 */,
+ _ULL(0x66FF3462D19F3025) /* 952 */, _ULL(0x72207C24DDFD3BFB) /* 953 */,
+ _ULL(0x4AF6B6D3E2ECE2EB) /* 954 */, _ULL(0x9C994DBEC7EA08DE) /* 955 */,
+ _ULL(0x49ACE597B09A8BC4) /* 956 */, _ULL(0xB38C4766CF0797BA) /* 957 */,
+ _ULL(0x131B9373C57C2A75) /* 958 */, _ULL(0xB1822CCE61931E58) /* 959 */,
+ _ULL(0x9D7555B909BA1C0C) /* 960 */, _ULL(0x127FAFDD937D11D2) /* 961 */,
+ _ULL(0x29DA3BADC66D92E4) /* 962 */, _ULL(0xA2C1D57154C2ECBC) /* 963 */,
+ _ULL(0x58C5134D82F6FE24) /* 964 */, _ULL(0x1C3AE3515B62274F) /* 965 */,
+ _ULL(0xE907C82E01CB8126) /* 966 */, _ULL(0xF8ED091913E37FCB) /* 967 */,
+ _ULL(0x3249D8F9C80046C9) /* 968 */, _ULL(0x80CF9BEDE388FB63) /* 969 */,
+ _ULL(0x1881539A116CF19E) /* 970 */, _ULL(0x5103F3F76BD52457) /* 971 */,
+ _ULL(0x15B7E6F5AE47F7A8) /* 972 */, _ULL(0xDBD7C6DED47E9CCF) /* 973 */,
+ _ULL(0x44E55C410228BB1A) /* 974 */, _ULL(0xB647D4255EDB4E99) /* 975 */,
+ _ULL(0x5D11882BB8AAFC30) /* 976 */, _ULL(0xF5098BBB29D3212A) /* 977 */,
+ _ULL(0x8FB5EA14E90296B3) /* 978 */, _ULL(0x677B942157DD025A) /* 979 */,
+ _ULL(0xFB58E7C0A390ACB5) /* 980 */, _ULL(0x89D3674C83BD4A01) /* 981 */,
+ _ULL(0x9E2DA4DF4BF3B93B) /* 982 */, _ULL(0xFCC41E328CAB4829) /* 983 */,
+ _ULL(0x03F38C96BA582C52) /* 984 */, _ULL(0xCAD1BDBD7FD85DB2) /* 985 */,
+ _ULL(0xBBB442C16082AE83) /* 986 */, _ULL(0xB95FE86BA5DA9AB0) /* 987 */,
+ _ULL(0xB22E04673771A93F) /* 988 */, _ULL(0x845358C9493152D8) /* 989 */,
+ _ULL(0xBE2A488697B4541E) /* 990 */, _ULL(0x95A2DC2DD38E6966) /* 991 */,
+ _ULL(0xC02C11AC923C852B) /* 992 */, _ULL(0x2388B1990DF2A87B) /* 993 */,
+ _ULL(0x7C8008FA1B4F37BE) /* 994 */, _ULL(0x1F70D0C84D54E503) /* 995 */,
+ _ULL(0x5490ADEC7ECE57D4) /* 996 */, _ULL(0x002B3C27D9063A3A) /* 997 */,
+ _ULL(0x7EAEA3848030A2BF) /* 998 */, _ULL(0xC602326DED2003C0) /* 999 */,
+ _ULL(0x83A7287D69A94086) /* 1000 */, _ULL(0xC57A5FCB30F57A8A) /* 1001 */,
+ _ULL(0xB56844E479EBE779) /* 1002 */, _ULL(0xA373B40F05DCBCE9) /* 1003 */,
+ _ULL(0xD71A786E88570EE2) /* 1004 */, _ULL(0x879CBACDBDE8F6A0) /* 1005 */,
+ _ULL(0x976AD1BCC164A32F) /* 1006 */, _ULL(0xAB21E25E9666D78B) /* 1007 */,
+ _ULL(0x901063AAE5E5C33C) /* 1008 */, _ULL(0x9818B34448698D90) /* 1009 */,
+ _ULL(0xE36487AE3E1E8ABB) /* 1010 */, _ULL(0xAFBDF931893BDCB4) /* 1011 */,
+ _ULL(0x6345A0DC5FBBD519) /* 1012 */, _ULL(0x8628FE269B9465CA) /* 1013 */,
+ _ULL(0x1E5D01603F9C51EC) /* 1014 */, _ULL(0x4DE44006A15049B7) /* 1015 */,
+ _ULL(0xBF6C70E5F776CBB1) /* 1016 */, _ULL(0x411218F2EF552BED) /* 1017 */,
+ _ULL(0xCB0C0708705A36A3) /* 1018 */, _ULL(0xE74D14754F986044) /* 1019 */,
+ _ULL(0xCD56D9430EA8280E) /* 1020 */, _ULL(0xC12591D7535F5065) /* 1021 */,
+ _ULL(0xC83223F1720AEF96) /* 1022 */, _ULL(0xC3A0396F7363A51F) /* 1023 */
+};
+
+/**
+ * @file
+ * $Id: TigerHash.cpp,v 1.3 2006/01/08 21:35:13 mickeg Exp $
+ */
diff --git a/dcpp/TigerHash.h b/dcpp/TigerHash.h
new file mode 100644
index 0000000..fd18e35
--- /dev/null
+++ b/dcpp/TigerHash.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2001-2005 Jacek Sieka, arnetheduck on gmail point 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 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.
+ */
+
+#ifndef _TIGER_HASH
+#define _TIGER_HASH
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+class TigerHash {
+public:
+ /** Hash size in bytes */
+ enum { HASH_SIZE = 24 };
+
+ TigerHash() : pos(0) {
+ res[0]=_ULL(0x0123456789ABCDEF);
+ res[1]=_ULL(0xFEDCBA9876543210);
+ res[2]=_ULL(0xF096A5B4C3B2E187);
+ }
+
+ ~TigerHash() {
+ }
+
+ /** Calculates the Tiger hash of the data. */
+ void update(const void* data, size_t len);
+ /** Call once all data has been processed. */
+ u_int8_t* finalize();
+
+ u_int8_t* getResult() {
+
+#ifdef __BIG_ENDIAN__
+ // Convert to little endian
+ for(int j=0; j<HASH_SIZE; j++)
+ convRes[j^7] = ((u_int8_t*)res)[j];
+
+ return (u_int8_t*) convRes;
+#else
+ return (u_int8_t*) res;
+#endif
+ };
+
+ /** State / final hash value */
+ u_int64_t res[3];
+
+ /** Results converted to little endian */
+ u_int8_t convRes[HASH_SIZE];
+private:
+ enum { BLOCK_SIZE = 512/8 };
+ /** 512 bit blocks for the compress function */
+ u_int8_t tmp[512/8];
+ /** Total number of bytes compressed */
+ u_int64_t pos;
+ /** S boxes */
+ static u_int64_t table[];
+
+ void tigerCompress(const u_int64_t* data, u_int64_t state[3]);
+};
+
+#endif // _TIGER_HASH
+
+/**
+ * @file
+ * $Id: TigerHash.h,v 1.3 2006/01/08 21:35:13 mickeg Exp $
+ */
diff --git a/dcpp/TimerManager.cpp b/dcpp/TimerManager.cpp
new file mode 100644
index 0000000..a5e3c19
--- /dev/null
+++ b/dcpp/TimerManager.cpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2001-2005 Jacek Sieka, arnetheduck on gmail point 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 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.
+ */
+
+#include "stdinc.h"
+#include "DCPlusPlus.h"
+
+#include "TimerManager.h"
+
+#ifndef _WIN32
+timeval TimerManager::tv;
+#endif
+
+int TimerManager::run() {
+ int nextMin = 0;
+
+ u_int32_t x = getTick();
+ u_int32_t nextTick = x + 1000;
+
+ while(!s.wait(nextTick > x ? nextTick - x : 0)) {
+ u_int32_t z = getTick();
+ nextTick = z + 1000;
+ fire(TimerManagerListener::Second(), z);
+ if(nextMin++ >= 60) {
+ fire(TimerManagerListener::Minute(), z);
+ nextMin = 0;
+ }
+ x = getTick();
+ }
+
+ return 0;
+}
+
+/**
+ * @file
+ * $Id: TimerManager.cpp,v 1.2 2005/08/21 14:03:43 olof Exp $
+ */
+
diff --git a/dcpp/TimerManager.h b/dcpp/TimerManager.h
new file mode 100644
index 0000000..e27b872
--- /dev/null
+++ b/dcpp/TimerManager.h
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2001-2005 Jacek Sieka, arnetheduck on gmail point 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 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.
+ */
+
+#if !defined(AFX_TIMERMANAGER_H__2172C2AD_D4FD_4B46_A1B2_7959D7359CCD__INCLUDED_)
+#define AFX_TIMERMANAGER_H__2172C2AD_D4FD_4B46_A1B2_7959D7359CCD__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+#include "Thread.h"
+#include "Semaphore.h"
+#include "Speaker.h"
+#include "Singleton.h"
+
+#ifndef _WIN32
+#include <sys/time.h>
+#endif
+
+class TimerManagerListener {
+public:
+ template<int I> struct X { enum { TYPE = I }; };
+
+ typedef X<0> Second;
+ typedef X<1> Minute;
+
+ // We expect everyone to implement this...
+ virtual void on(Second, u_int32_t) throw() { }
+ virtual void on(Minute, u_int32_t) throw() { }
+};
+
+class TimerManager : public Speaker<TimerManagerListener>, public Singleton<TimerManager>, public Thread
+{
+public:
+ static time_t getTime() {
+ return (time_t)time(NULL);
+ }
+ static u_int32_t getTick() {
+#ifdef _WIN32
+ return GetTickCount();
+#else
+ timeval tv2;
+ gettimeofday(&tv2, NULL);
+ return (u_int32_t)((tv2.tv_sec - tv.tv_sec) * 1000 ) + ( (tv2.tv_usec - tv.tv_usec) / 1000);
+#endif
+ };
+private:
+
+ Semaphore s;
+
+ friend class Singleton<TimerManager>;
+ TimerManager() {
+#ifndef _WIN32
+ gettimeofday(&tv, NULL);
+#endif
+ };
+
+ virtual ~TimerManager() throw() {
+ dcassert(listeners.empty());
+ s.signal();
+ join();
+ };
+
+ virtual int run();
+
+#ifndef _WIN32
+ static timeval tv;
+#endif
+};
+
+#define GET_TICK() TimerManager::getTick()
+#define GET_TIME() TimerManager::getTime()
+
+#endif // !defined(AFX_TIMERMANAGER_H__2172C2AD_D4FD_4B46_A1B2_7959D7359CCD__INCLUDED_)
+
+/**
+ * @file
+ * $Id: TimerManager.h,v 1.2 2005/08/21 14:03:43 olof Exp $
+ */
+
diff --git a/dcpp/UploadManager.cpp b/dcpp/UploadManager.cpp
new file mode 100644
index 0000000..0f34537
--- /dev/null
+++ b/dcpp/UploadManager.cpp
@@ -0,0 +1,447 @@
+/*
+ * Copyright (C) 2001-2005 Jacek Sieka, arnetheduck on gmail point 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 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.
+ */
+
+#include "stdinc.h"
+#include "DCPlusPlus.h"
+
+#include "UploadManager.h"
+
+#include "ConnectionManager.h"
+#include "LogManager.h"
+#include "ShareManager.h"
+#include "ClientManager.h"
+#include "FilteredFile.h"
+#include "ZUtils.h"
+#include "ResourceManager.h"
+#include "HashManager.h"
+#include "AdcCommand.h"
+
+static const string UPLOAD_AREA = "Uploads";
+
+UploadManager::UploadManager() throw() : running(0), extra(0), lastGrant(0) {
+ ClientManager::getInstance()->addListener(this);
+ TimerManager::getInstance()->addListener(this);
+}
+
+UploadManager::~UploadManager() throw() {
+ TimerManager::getInstance()->removeListener(this);
+ ClientManager::getInstance()->removeListener(this);
+ while(true) {
+ {
+ Lock l(cs);
+ if(uploads.empty())
+ break;
+ }
+ Thread::sleep(100);
+ }
+}
+
+bool UploadManager::prepareFile(UserConnection* aSource, const string& aType, const string& aFile, int64_t aStartPos, int64_t aBytes, bool listRecursive) {
+ if(aSource->getState() != UserConnection::STATE_GET) {
+ dcdebug("UM:prepFile Wrong state, ignoring\n");
+ return false;
+ }
+
+ dcassert(aFile.size() > 0);
+
+ InputStream* is = NULL;
+ int64_t size = 0;
+
+ bool userlist = false;
+ bool free = false;
+ bool leaves = false;
+ bool partList = false;
+
+ string file;
+ try {
+ if(aType == "file") {
+ file = ShareManager::getInstance()->translateFileName(aFile);
+ userlist = (Util::stricmp(aFile.c_str(), "files.xml.bz2") == 0);
+
+ try {
+ File* f = new File(file, File::READ, File::OPEN);
+
+ size = f->getSize();
+
+ free = userlist || (size <= (int64_t)(SETTING(SET_MINISLOT_SIZE) * 1024) );
+
+ if(aBytes == -1) {
+ aBytes = size - aStartPos;
+ }
+
+ if((aBytes < 0) || ((aStartPos + aBytes) > size)) {
+ aSource->fileNotAvail();
+ delete f;
+ return false;
+ }
+
+ f->setPos(aStartPos);
+
+ is = f;
+
+ if((aStartPos + aBytes) < size) {
+ is = new LimitedInputStream<true>(is, aBytes);
+ }
+
+ } catch(const Exception&) {
+ aSource->fileNotAvail();
+ return false;
+ }
+
+ } else if(aType == "tthl") {
+ // TTH Leaves...
+ MemoryInputStream* mis = ShareManager::getInstance()->getTree(aFile);
+ file = ShareManager::getInstance()->translateFileName(aFile);
+ if(mis == NULL) {
+ aSource->fileNotAvail();
+ return false;
+ }
+
+ size = mis->getSize();
+ aStartPos = 0;
+ is = mis;
+ leaves = true;
+ free = true;
+ } else if(aType == "list") {
+ // Partial file list
+ MemoryInputStream* mis = ShareManager::getInstance()->generatePartialList(aFile, listRecursive);
+ if(mis == NULL) {
+ aSource->fileNotAvail();
+ return false;
+ }
+ // Some old dc++ clients err here...
+ aBytes = -1;
+ size = mis->getSize();
+ aStartPos = 0;
+ is = mis;
+ free = true;
+ partList = true;
+ } else {
+ aSource->fileNotAvail();
+ return false;
+ }
+ } catch(const ShareException&) {
+ aSource->fileNotAvail();
+ return false;
+ }
+
+
+ Lock l(cs);
+
+ bool extraSlot = false;
+
+ if(!aSource->isSet(UserConnection::FLAG_HASSLOT)) {
+ bool hasReserved = (reservedSlots.find(aSource->getUser()) != reservedSlots.end());
+ bool isFavorite = aSource->getUser()->getFavoriteGrantSlot();
+
+ if(!(hasReserved || isFavorite || getFreeSlots() > 0 || getAutoSlot())) {
+ bool supportsFree = aSource->getUser()->isSet(User::DCPLUSPLUS) || aSource->isSet(UserConnection::FLAG_SUPPORTS_MINISLOTS) || !aSource->isSet(UserConnection::FLAG_NMDC);
+ bool allowedFree = aSource->isSet(UserConnection::FLAG_HASEXTRASLOT) || aSource->getUser()->isSet(User::OP) || getFreeExtraSlots() > 0;
+ if(free && supportsFree && allowedFree) {
+ extraSlot = true;
+ } else {
+ delete is;
+ aSource->maxedOut();
+ aSource->disconnect();
+ return false;
+ }
+ }
+
+ setLastGrant(GET_TICK());
+ }
+
+ Upload* u = new Upload();
+ u->setUserConnection(aSource);
+ u->setFile(is);
+ if(aBytes == -1)
+ u->setSize(size);
+ else
+ u->setSize(aStartPos + aBytes);
+
+ u->setStartPos(aStartPos);
+ u->setFileName(file);
+ u->setLocalFileName(file);
+
+ if(userlist)
+ u->setFlag(Upload::FLAG_USER_LIST);
+ if(leaves)
+ u->setFlag(Upload::FLAG_TTH_LEAVES);
+ if(partList)
+ u->setFlag(Upload::FLAG_PARTIAL_LIST);
+
+ dcassert(aSource->getUpload() == NULL);
+ aSource->setUpload(u);
+ uploads.push_back(u);
+
+ if(!aSource->isSet(UserConnection::FLAG_HASSLOT)) {
+ if(extraSlot) {
+ if(!aSource->isSet(UserConnection::FLAG_HASEXTRASLOT)) {
+ aSource->setFlag(UserConnection::FLAG_HASEXTRASLOT);
+ extra++;
+ }
+ } else {
+ if(aSource->isSet(UserConnection::FLAG_HASEXTRASLOT)) {
+ aSource->unsetFlag(UserConnection::FLAG_HASEXTRASLOT);
+ extra--;
+ }
+ aSource->setFlag(UserConnection::FLAG_HASSLOT);
+ running++;
+ }
+ }
+
+ return true;
+}
+
+void UploadManager::on(UserConnectionListener::Get, UserConnection* aSource, const string& aFile, int64_t aResume) throw() {
+ if(prepareFile(aSource, "file", Util::toAdcFile(aFile), aResume, -1)) {
+ aSource->setState(UserConnection::STATE_SEND);
+ aSource->fileLength(Util::toString(aSource->getUpload()->getSize()));
+ }
+}
+
+void UploadManager::onGetBlock(UserConnection* aSource, const string& aFile, int64_t aStartPos, int64_t aBytes, bool z) {
+ if(!z || BOOLSETTING(COMPRESS_TRANSFERS)) {
+ if(prepareFile(aSource, "file", Util::toAdcFile(aFile), aStartPos, aBytes)) {
+ Upload* u = aSource->getUpload();
+ dcassert(u != NULL);
+ if(aBytes == -1)
+ aBytes = u->getSize() - aStartPos;
+
+ dcassert(aBytes >= 0);
+
+ u->setStart(GET_TICK());
+
+ if(z) {
+ u->setFile(new FilteredInputStream<ZFilter, true>(u->getFile()));
+ u->setFlag(Upload::FLAG_ZUPLOAD);
+ }
+
+ aSource->sending(aBytes);
+ aSource->setState(UserConnection::STATE_DONE);
+ aSource->transmitFile(u->getFile());
+ fire(UploadManagerListener::Starting(), u);
+ }
+ }
+}
+
+void UploadManager::on(UserConnectionListener::Send, UserConnection* aSource) throw() {
+ if(aSource->getState() != UserConnection::STATE_SEND) {
+ dcdebug("UM::onSend Bad state, ignoring\n");
+ return;
+ }
+
+ Upload* u = aSource->getUpload();
+ dcassert(u != NULL);
+
+ u->setStart(GET_TICK());
+ aSource->setState(UserConnection::STATE_DONE);
+ aSource->transmitFile(u->getFile());
+ fire(UploadManagerListener::Starting(), u);
+}
+
+void UploadManager::on(UserConnectionListener::BytesSent, UserConnection* aSource, size_t aBytes, size_t aActual) throw() {
+ dcassert(aSource->getState() == UserConnection::STATE_DONE);
+ Upload* u = aSource->getUpload();
+ dcassert(u != NULL);
+ u->addPos(aBytes, aActual);
+}
+
+void UploadManager::on(UserConnectionListener::Failed, UserConnection* aSource, const string& aError) throw() {
+ Upload* u = aSource->getUpload();
+
+ if(u) {
+ aSource->setUpload(NULL);
+ fire(UploadManagerListener::Failed(), u, aError);
+
+ dcdebug("UM::onFailed: Removing upload\n");
+ removeUpload(u);
+ }
+
+ removeConnection(aSource, false);
+}
+
+void UploadManager::on(UserConnectionListener::TransmitDone, UserConnection* aSource) throw() {
+ dcassert(aSource->getState() == UserConnection::STATE_DONE);
+ Upload* u = aSource->getUpload();
+ dcassert(u != NULL);
+
+ aSource->setUpload(NULL);
+ aSource->setState(UserConnection::STATE_GET);
+
+ if(BOOLSETTING(LOG_UPLOADS) && !u->isSet(Upload::FLAG_TTH_LEAVES) && (BOOLSETTING(LOG_FILELIST_TRANSFERS) || !u->isSet(Upload::FLAG_USER_LIST))) {
+ StringMap params;
+ params["source"] = u->getFileName();
+ params["user"] = aSource->getUser()->getNick();
+ params["userip"] = aSource->getRemoteIp();
+ params["hub"] = aSource->getUser()->getLastHubName();
+ params["hubip"] = aSource->getUser()->getLastHubAddress();
+ params["size"] = Util::toString(u->getSize());
+ params["sizeshort"] = Util::formatBytes(u->getSize());
+ params["chunksize"] = Util::toString(u->getTotal());
+ params["chunksizeshort"] = Util::formatBytes(u->getTotal());
+ params["actualsize"] = Util::toString(u->getActual());
+ params["actualsizeshort"] = Util::formatBytes(u->getActual());
+ params["speed"] = Util::formatBytes(u->getAverageSpeed()) + "/s";
+ params["time"] = Util::formatSeconds((GET_TICK() - u->getStart()) / 1000);
+
+ if(u->getTTH() != NULL) {
+ params["tth"] = u->getTTH()->toBase32();
+ }
+ LOG(LogManager::UPLOAD, params);
+ }
+
+ fire(UploadManagerListener::Complete(), u);
+ removeUpload(u);
+}
+
+void UploadManager::removeConnection(UserConnection::Ptr aConn, bool ntd) {
+ dcassert(aConn->getUpload() == NULL);
+ aConn->removeListener(this);
+ if(aConn->isSet(UserConnection::FLAG_HASSLOT)) {
+ running--;
+ aConn->unsetFlag(UserConnection::FLAG_HASSLOT);
+ }
+ if(aConn->isSet(UserConnection::FLAG_HASEXTRASLOT)) {
+ extra--;
+ aConn->unsetFlag(UserConnection::FLAG_HASEXTRASLOT);
+ }
+ ConnectionManager::getInstance()->putUploadConnection(aConn, ntd);
+}
+
+void UploadManager::on(TimerManagerListener::Minute, u_int32_t aTick) throw() {
+ Lock l(cs);
+ for(SlotIter j = reservedSlots.begin(); j != reservedSlots.end();) {
+ if(j->second + 600 * 1000 < aTick) {
+ reservedSlots.erase(j++);
+ } else {
+ ++j;
+ }
+ }
+}
+
+void UploadManager::on(GetListLength, UserConnection* conn) throw() {
+ conn->listLen(ShareManager::getInstance()->getListLenString());
+}
+
+void UploadManager::on(AdcCommand::NTD, UserConnection* aConn, const AdcCommand&) throw() {
+ removeConnection(aConn, true);
+}
+
+void UploadManager::on(AdcCommand::GET, UserConnection* aSource, const AdcCommand& c) throw() {
+ int64_t aBytes = Util::toInt64(c.getParam(3));
+ int64_t aStartPos = Util::toInt64(c.getParam(2));
+ const string& fname = c.getParam(1);
+ const string& type = c.getParam(0);
+ string tmp;
+
+ if(prepareFile(aSource, type, fname, aStartPos, aBytes, c.hasFlag("RE", 4))) {
+ Upload* u = aSource->getUpload();
+ dcassert(u != NULL);
+ if(aBytes == -1)
+ aBytes = u->getSize() - aStartPos;
+
+ dcassert(aBytes >= 0);
+
+ u->setStart(GET_TICK());
+
+ AdcCommand cmd(AdcCommand::CMD_SND);
+ cmd.addParam(c.getParam(0));
+ cmd.addParam(c.getParam(1));
+ cmd.addParam(Util::toString(u->getPos()));
+ cmd.addParam(Util::toString(u->getSize() - u->getPos()));
+
+ if(c.hasFlag("ZL", 4)) {
+ u->setFile(new FilteredInputStream<ZFilter, true>(u->getFile()));
+ u->setFlag(Upload::FLAG_ZUPLOAD);
+ cmd.addParam("ZL1");
+ }
+
+ aSource->send(cmd);
+ aSource->setState(UserConnection::STATE_DONE);
+ aSource->transmitFile(u->getFile());
+ fire(UploadManagerListener::Starting(), u);
+ }
+}
+
+/** @todo fixme */
+void UploadManager::on(AdcCommand::GFI, UserConnection* aSource, const AdcCommand& c) throw() {
+ if(c.getParameters().size() < 2) {
+ aSource->sta(AdcCommand::SEV_RECOVERABLE, AdcCommand::ERROR_PROTOCOL_GENERIC, "Missing parameters");
+ return;
+ }
+
+ const string& type = c.getParam(0);
+ const string& ident = c.getParam(1);
+
+ if(type == "file") {
+ SearchResult::List l;
+ StringList sl;
+
+ if(ident.compare(0, 4, "TTH/") != 0) {
+ aSource->sta(AdcCommand::SEV_RECOVERABLE, AdcCommand::ERROR_PROTOCOL_GENERIC, "Invalid identifier");
+ return;
+ }
+ sl.push_back("TH" + ident.substr(4));
+ ShareManager::getInstance()->search(l, sl, 1);
+ if(l.empty()) {
+ aSource->sta(AdcCommand::SEV_RECOVERABLE, AdcCommand::ERROR_FILE_NOT_AVAILABLE, "Not found");
+ } else {
+ aSource->send(l[0]->toRES(AdcCommand::TYPE_CLIENT));
+ l[0]->decRef();
+ }
+ }
+}
+
+// TimerManagerListener
+void UploadManager::on(TimerManagerListener::Second, u_int32_t) throw() {
+ Lock l(cs);
+ Upload::List ticks;
+
+ for(Upload::Iter i = uploads.begin(); i != uploads.end(); ++i) {
+ ticks.push_back(*i);
+ }
+
+ if(ticks.size() > 0)
+ fire(UploadManagerListener::Tick(), ticks);
+
+}
+
+void UploadManager::on(ClientManagerListener::UserUpdated, const User::Ptr& aUser) throw() {
+ if( (!aUser->isOnline()) &&
+ (aUser->isSet(User::QUIT_HUB)) &&
+ (BOOLSETTING(AUTO_KICK)) ){
+
+ Lock l(cs);
+ for(Upload::Iter i = uploads.begin(); i != uploads.end(); ++i) {
+ Upload* u = *i;
+ if(u->getUser() == aUser) {
+ // Oops...adios...
+ u->getUserConnection()->disconnect();
+ // But let's grant him/her a free slot just in case...
+ if (!u->getUserConnection()->isSet(UserConnection::FLAG_HASEXTRASLOT))
+ reserveSlot(aUser);
+ LogManager::getInstance()->message(STRING(DISCONNECTED_USER) + aUser->getFullNick());
+ }
+ }
+ }
+}
+
+/**
+ * @file
+ * $Id: UploadManager.cpp,v 1.2 2005/08/21 14:03:43 olof Exp $
+ */
diff --git a/dcpp/UploadManager.h b/dcpp/UploadManager.h
new file mode 100644
index 0000000..544a709
--- /dev/null
+++ b/dcpp/UploadManager.h
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2001-2005 Jacek Sieka, arnetheduck on gmail point 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 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.
+ */
+
+#if !defined(AFX_UPLOADMANAGER_H__B0C67119_3445_4208_B5AA_938D4A019703__INCLUDED_)
+#define AFX_UPLOADMANAGER_H__B0C67119_3445_4208_B5AA_938D4A019703__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+#include "UserConnection.h"
+#include "Singleton.h"
+
+#include "ClientManagerListener.h"
+#include "File.h"
+#include "MerkleTree.h"
+
+class Upload : public Transfer, public Flags {
+public:
+ enum Flags {
+ FLAG_USER_LIST = 0x01,
+ FLAG_TTH_LEAVES = 0x02,
+ FLAG_ZUPLOAD = 0x04,
+ FLAG_PARTIAL_LIST = 0x08
+ };
+
+ typedef Upload* Ptr;
+ typedef vector<Ptr> List;
+ typedef List::iterator Iter;
+
+ Upload() : tth(NULL), file(NULL) { };
+ virtual ~Upload() {
+ delete file;
+ delete tth;
+ };
+
+ User::Ptr& getUser() { dcassert(getUserConnection() != NULL); return getUserConnection()->getUser(); };
+
+ GETSET(string, fileName, FileName);
+ GETSET(string, localFileName, LocalFileName);
+ GETSET(TTHValue*, tth, TTH);
+ GETSET(InputStream*, file, File);
+};
+
+class UploadManagerListener {
+public:
+ template<int I> struct X { enum { TYPE = I }; };
+
+ typedef X<0> Complete;
+ typedef X<1> Failed;
+ typedef X<2> Starting;
+ typedef X<3> Tick;
+
+ virtual void on(Starting, Upload*) throw() { };
+ virtual void on(Tick, const Upload::List&) throw() { };
+ virtual void on(Complete, Upload*) throw() { };
+ virtual void on(Failed, Upload*, const string&) throw() { };
+
+};
+
+class UploadManager : private ClientManagerListener, private UserConnectionListener, public Speaker<UploadManagerListener>, private TimerManagerListener, public Singleton<UploadManager>
+{
+public:
+
+ /** @return Number of uploads. */
+ size_t getUploadCount() { Lock l(cs); return uploads.size(); };
+
+ /**
+ * @remarks This is only used in the tray icons. Could be used in
+ * MainFrame too.
+ *
+ * @return Average download speed in Bytes/s
+ */
+ int getAverageSpeed() {
+ Lock l(cs);
+ int avg = 0;
+ for(Upload::Iter i = uploads.begin(); i != uploads.end(); ++i) {
+ Upload* u = *i;
+ avg += (int)u->getRunningAverage();
+ }
+ return avg;
+ }
+
+ /**
+ * @remarks This is defined with GETSET below.
+ * @return Number of running uploads.
+ */
+// int getRunning() { return running; };
+
+ /** @return Number of free slots. */
+ int getFreeSlots() { return max((SETTING(SLOTS) - running), 0); }
+
+ /** @internal */
+ bool getAutoSlot() {
+ /** A 0 in settings means disable */
+ if(SETTING(MIN_UPLOAD_SPEED) == 0)
+ return false;
+ /** Only grant one slot per 30 sec */
+ if(GET_TICK() < getLastGrant() + 30*1000)
+ return false;
+ /** Grant if uploadspeed is less than the threshold speed */
+ return UploadManager::getInstance()->getAverageSpeed() < (SETTING(MIN_UPLOAD_SPEED)*1024);
+ }
+
+ /** @internal */
+ int getFreeExtraSlots() { return max(3 - getExtra(), 0); };
+
+ /** @param aUser Reserve an upload slot for this user and connect. */
+ void reserveSlot(User::Ptr& aUser) {
+ {
+ Lock l(cs);
+ reservedSlots[aUser] = GET_TICK();
+ }
+ if(aUser->isOnline())
+ aUser->connect();
+ }
+
+ /** @param aUser Reserve an upload slot for this user. */
+ void reserveSlot(const User::Ptr& aUser) {
+ {
+ Lock l(cs);
+ reservedSlots[aUser] = GET_TICK();
+ }
+ }
+
+
+ /** @internal */
+ void addConnection(UserConnection::Ptr conn) {
+ conn->addListener(this);
+ conn->setState(UserConnection::STATE_GET);
+ }
+
+ GETSET(int, running, Running);
+ GETSET(int, extra, Extra);
+ GETSET(u_int32_t, lastGrant, LastGrant);
+private:
+ Upload::List uploads;
+ CriticalSection cs;
+
+ typedef HASH_MAP<User::Ptr, u_int32_t, User::HashFunction> SlotMap;
+ typedef SlotMap::iterator SlotIter;
+ SlotMap reservedSlots;
+
+ friend class Singleton<UploadManager>;
+ UploadManager() throw();
+ virtual ~UploadManager() throw();
+
+ void removeConnection(UserConnection::Ptr aConn, bool ntd);
+ void removeUpload(Upload* aUpload) {
+ Lock l(cs);
+ dcassert(find(uploads.begin(), uploads.end(), aUpload) != uploads.end());
+ uploads.erase(find(uploads.begin(), uploads.end(), aUpload));
+ aUpload->setUserConnection(NULL);
+ delete aUpload;
+ }
+
+ // ClientManagerListener
+ virtual void on(ClientManagerListener::UserUpdated, const User::Ptr& aUser) throw();
+
+ // TimerManagerListener
+ virtual void on(TimerManagerListener::Minute, u_int32_t aTick) throw();
+ virtual void on(TimerManagerListener::Second, u_int32_t aTick) throw();
+
+ // UserConnectionListener
+ virtual void on(BytesSent, UserConnection*, size_t, size_t) throw();
+ virtual void on(Failed, UserConnection*, const string&) throw();
+ virtual void on(Get, UserConnection*, const string&, int64_t) throw();
+ virtual void on(GetBlock, UserConnection* conn, const string& line, int64_t resume, int64_t bytes) throw() { onGetBlock(conn, line, resume, bytes, false); }
+ virtual void on(GetZBlock, UserConnection* conn, const string& line, int64_t resume, int64_t bytes) throw() { onGetBlock(conn, line, resume, bytes, true); }
+ virtual void on(Send, UserConnection*) throw();
+ virtual void on(GetListLength, UserConnection* conn) throw();
+ virtual void on(TransmitDone, UserConnection*) throw();
+
+ virtual void on(AdcCommand::GET, UserConnection*, const AdcCommand&) throw();
+ virtual void on(AdcCommand::GFI, UserConnection*, const AdcCommand&) throw();
+ virtual void on(AdcCommand::NTD, UserConnection*, const AdcCommand&) throw();
+
+ void onGetBlock(UserConnection* aSource, const string& aFile, int64_t aResume, int64_t aBytes, bool z);
+ bool prepareFile(UserConnection* aSource, const string& aType, const string& aFile, int64_t aResume, int64_t aBytes, bool listRecursive = false);
+};
+
+#endif // !defined(AFX_UPLOADMANAGER_H__B0C67119_3445_4208_B5AA_938D4A019703__INCLUDED_)
+
+/**
+ * @file
+ * $Id: UploadManager.h,v 1.2 2005/08/21 14:03:43 olof Exp $
+ */
diff --git a/dcpp/User.cpp b/dcpp/User.cpp
new file mode 100644
index 0000000..31d71fa
--- /dev/null
+++ b/dcpp/User.cpp
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 2001-2005 Jacek Sieka, arnetheduck on gmail point 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 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.
+ */
+
+#include "stdinc.h"
+#include "DCPlusPlus.h"
+
+#include "SettingsManager.h"
+#include "ResourceManager.h"
+#include "TimerManager.h"
+
+#include "User.h"
+
+#include "Client.h"
+#include "FavoriteUser.h"
+
+User::~User() throw() {
+ delete favoriteUser;
+}
+
+void User::connect() {
+ RLock<> l(cs);
+ if(client) {
+ client->connect(this);
+ }
+}
+
+const string& User::getClientNick() const {
+ RLock<> l(cs);
+ if(client) {
+ return client->getNick();
+ } else {
+ return SETTING(NICK);
+ }
+}
+
+CID User::getClientCID() const {
+ RLock<> l(cs);
+ if(client) {
+ return client->getMe()->getCID();
+ } else {
+ return CID(SETTING(CLIENT_ID));
+ }
+}
+
+void User::updated(User::Ptr& aUser) {
+ RLock<> l(aUser->cs);
+ if(aUser->client) {
+ aUser->client->updated(aUser);
+ }
+}
+
+const string& User::getClientName() const {
+ RLock<> l(cs);
+ if(client) {
+ return client->getName();
+ } else if(!getLastHubName().empty()) {
+ return getLastHubName();
+ } else {
+ return STRING(OFFLINE);
+ }
+}
+
+string User::getClientAddressPort() const {
+ RLock<> l(cs);
+ if(client) {
+ return client->getAddressPort();
+ } else {
+ return Util::emptyString;
+ }
+}
+
+void User::privateMessage(const string& aMsg) {
+ RLock<> l(cs);
+ if(client) {
+ client->privateMessage(this, aMsg);
+ }
+}
+
+bool User::isClientOp() const {
+ RLock<> l(cs);
+ if(client) {
+ return client->getOp();
+ }
+ return false;
+}
+
+void User::send(const string& aMsg) {
+ RLock<> l(cs);
+ if(client) {
+ client->send(aMsg);
+ }
+}
+
+void User::sendUserCmd(const string& aUserCmd) {
+ RLock<> l(cs);
+ if(client) {
+ client->sendUserCmd(aUserCmd);
+ }
+}
+
+void User::clientMessage(const string& aMsg) {
+ RLock<> l(cs);
+ if(client) {
+ client->hubMessage(aMsg);
+ }
+}
+
+void User::setClient(Client* aClient) {
+ WLock<> l(cs);
+ client = aClient;
+ if(client == NULL) {
+ if (isSet(ONLINE) && isFavoriteUser())
+ setFavoriteLastSeen();
+ unsetFlag(ONLINE);
+ }
+ else {
+ setLastHubAddress(aClient->getIpPort());
+ setLastHubName(aClient->getName());
+ setFlag(ONLINE);
+ unsetFlag(QUIT_HUB);
+ }
+}
+
+void User::getParams(StringMap& ucParams) {
+ ucParams["nick"] = getNick();
+ ucParams["cid"] = getCID().toBase32();
+ ucParams["tag"] = getTag();
+ ucParams["description"] = getDescription();
+ ucParams["email"] = getEmail();
+ ucParams["share"] = Util::toString(getBytesShared());
+ ucParams["shareshort"] = Util::formatBytes(getBytesShared());
+ ucParams["ip"] = getIp();
+}
+
+// favorite user stuff
+void User::setFavoriteUser(FavoriteUser* aUser) {
+ WLock<> l(cs);
+ delete favoriteUser;
+ favoriteUser = aUser;
+}
+
+bool User::isFavoriteUser() const {
+ RLock<> l(cs);
+ return (favoriteUser != NULL);
+}
+
+bool User::getFavoriteGrantSlot() const {
+ RLock<> l(cs);
+ return (favoriteUser != NULL && favoriteUser->isSet(FavoriteUser::FLAG_GRANTSLOT));
+}
+
+void User::setFavoriteGrantSlot(bool grant) {
+ WLock<> l(cs);
+ if (favoriteUser == NULL)
+ return;
+
+ if (grant)
+ favoriteUser->setFlag(FavoriteUser::FLAG_GRANTSLOT);
+ else
+ favoriteUser->unsetFlag(FavoriteUser::FLAG_GRANTSLOT);
+}
+
+void User::setFavoriteLastSeen(u_int32_t anOfflineTime) {
+ WLock<> l(cs);
+ if (favoriteUser != NULL) {
+ if (anOfflineTime != 0)
+ favoriteUser->setLastSeen(anOfflineTime);
+ else
+ favoriteUser->setLastSeen(GET_TIME());
+ }
+}
+
+u_int32_t User::getFavoriteLastSeen() const {
+ RLock<> l(cs);
+ if (favoriteUser != NULL)
+ return favoriteUser->getLastSeen();
+ else
+ return 0;
+}
+
+const string& User::getUserDescription() const {
+ RLock<> l(cs);
+ if (favoriteUser != NULL)
+ return favoriteUser->getDescription();
+ else
+ return Util::emptyString;
+}
+
+void User::setUserDescription(const string& aDescription) {
+ WLock<> l(cs);
+ if (favoriteUser != NULL)
+ favoriteUser->setDescription(aDescription);
+}
+
+StringMap& User::clientEscapeParams(StringMap& sm) const {
+ return client->escapeParams(sm);
+}
+
+/**
+ * @file
+ * $Id: User.cpp,v 1.2 2005/08/21 14:03:43 olof Exp $
+ */
+
diff --git a/dcpp/User.h b/dcpp/User.h
new file mode 100644
index 0000000..39fc378
--- /dev/null
+++ b/dcpp/User.h
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2001-2005 Jacek Sieka, arnetheduck on gmail point 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 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.
+ */
+
+#if !defined(AFX_USER_H__26AA222C_500B_4AD2_A5AA_A594E1A6D639__INCLUDED_)
+#define AFX_USER_H__26AA222C_500B_4AD2_A5AA_A594E1A6D639__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+#include "Util.h"
+#include "Pointer.h"
+#include "CriticalSection.h"
+#include "CID.h"
+
+class Client;
+class FavoriteUser;
+
+/**
+ * A user connected to a hubs.
+ */
+class User : public PointerBase, public Flags
+{
+public:
+ enum {
+ OP_BIT,
+ ONLINE_BIT,
+ DCPLUSPLUS_BIT,
+ PASSIVE_BIT,
+ QUIT_HUB_BIT,
+ HIDDEN_BIT,
+ HUB_BIT,
+ BOT_BIT
+ };
+
+ enum {
+ OP = 1<<OP_BIT,
+ ONLINE = 1<<ONLINE_BIT,
+ DCPLUSPLUS = 1<<DCPLUSPLUS_BIT,
+ PASSIVE = 1<<PASSIVE_BIT,
+ QUIT_HUB = 1<<QUIT_HUB_BIT,
+ HIDDEN = 1<<HIDDEN_BIT,
+ HUB = 1<<HUB_BIT,
+ BOT = 1<<BOT_BIT
+ };
+
+ typedef Pointer<User> Ptr;
+ typedef vector<Ptr> List;
+ typedef List::iterator Iter;
+ typedef HASH_MAP_X(string,Ptr,noCaseStringHash,noCaseStringEq,noCaseStringLess) NickMap;
+ typedef NickMap::iterator NickIter;
+ typedef HASH_MAP_X(CID, Ptr, CID::Hash, equal_to<CID>, less<CID>) CIDMap;
+ typedef CIDMap::iterator CIDIter;
+
+ struct HashFunction {
+ static const size_t bucket_size = 4;
+ static const size_t min_buckets = 8;
+ size_t operator()(const Ptr& x) const { return ((size_t)(&(*x)))/sizeof(User); };
+ bool operator()(const Ptr& a, const Ptr& b) const { return (&(*a)) < (&(*b)); };
+ };
+
+ User(const CID& aCID) : cid(aCID), bytesShared(0), slots(0), udpPort(0), client(NULL), favoriteUser(NULL) { }
+ User(const string& aNick) throw() : nick(aNick), bytesShared(0), slots(0), udpPort(0), client(NULL), favoriteUser(NULL) { }
+
+ virtual ~User() throw();
+
+ void setClient(Client* aClient);
+ void connect();
+ const string& getClientNick() const;
+ CID getClientCID() const;
+ const string& getClientName() const;
+ string getClientAddressPort() const;
+ void privateMessage(const string& aMsg);
+ void clientMessage(const string& aMsg);
+ bool isClientOp() const;
+ void send(const string& msg);
+ void sendUserCmd(const string& aUserCmd);
+
+ string getFullNick() const {
+ string tmp(getNick());
+ tmp += " (";
+ tmp += getClientName();
+ tmp += ")";
+ return tmp;
+ }
+
+ void setBytesShared(const string& aSharing) { setBytesShared(Util::toInt64(aSharing)); };
+
+ bool isOnline() const { return isSet(ONLINE); };
+ bool isClient(Client* aClient) const { return client == aClient; };
+
+ void getParams(StringMap& ucParams);
+
+ // favorite user stuff
+ void setFavoriteUser(FavoriteUser* aUser);
+ bool isFavoriteUser() const;
+ bool getFavoriteGrantSlot() const;
+ void setFavoriteGrantSlot(bool grant);
+ void setFavoriteLastSeen(u_int32_t anOfflineTime = 0);
+ u_int32_t getFavoriteLastSeen() const;
+ const string& getUserDescription() const;
+ void setUserDescription(const string& aDescription);
+
+ static void updated(User::Ptr& aUser);
+
+ StringMap& clientEscapeParams(StringMap& sm) const;
+
+ GETSET(string, connection, Connection);
+ GETSET(string, nick, Nick);
+ GETSET(string, email, Email);
+ GETSET(string, description, Description);
+ GETSET(string, tag, Tag);
+ GETSET(string, lastHubAddress, LastHubAddress);
+ GETSET(string, lastHubName, LastHubName);
+ GETSET(string, ip, Ip);
+ GETSET(CID, cid, CID);
+ GETSET(int64_t, bytesShared, BytesShared);
+ GETSET(int, slots, Slots);
+ GETSET(short, udpPort, UDPPort);
+private:
+ mutable RWLock<> cs;
+
+ User(const User&);
+ User& operator=(const User&);
+
+ Client* client;
+ FavoriteUser* favoriteUser;
+};
+
+#endif // !defined(AFX_USER_H__26AA222C_500B_4AD2_A5AA_A594E1A6D639__INCLUDED_)
+
+/**
+ * @file
+ * $Id: User.h,v 1.2 2005/08/21 14:03:43 olof Exp $
+ */
diff --git a/dcpp/UserCommand.h b/dcpp/UserCommand.h
new file mode 100644
index 0000000..9b70ea3
--- /dev/null
+++ b/dcpp/UserCommand.h
@@ -0,0 +1,83 @@
+/*
+* Copyright (C) 2001-2005 Jacek Sieka, arnetheduck on gmail point 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 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.
+*/
+
+#if !defined(AFX_USERCOMMAND_H__75858D5D_F12F_40D0_B127_5DDED226C098__INCLUDED_)
+#define AFX_USERCOMMAND_H__75858D5D_F12F_40D0_B127_5DDED226C098__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+#include "Util.h"
+
+class UserCommand : public Flags {
+public:
+ typedef vector<UserCommand> List;
+ typedef List::iterator Iter;
+
+ enum {
+ TYPE_SEPARATOR,
+ TYPE_RAW,
+ TYPE_RAW_ONCE,
+ TYPE_CLEAR = 255
+ };
+
+ enum {
+ CONTEXT_HUB = 0x01,
+ CONTEXT_CHAT = 0x02,
+ CONTEXT_SEARCH = 0x04,
+ CONTEXT_FILELIST = 0x08,
+ CONTEXT_MASK = CONTEXT_HUB | CONTEXT_CHAT | CONTEXT_SEARCH | CONTEXT_FILELIST
+ };
+
+ enum {
+ FLAG_NOSAVE = 0x01
+ };
+
+ UserCommand() : cid(0), type(0), ctx(0) { };
+ UserCommand(int aId, int aType, int aCtx, int aFlags, const string& aName, const string& aCommand, const string& aHub) throw()
+ : Flags(aFlags), cid(aId), type(aType), ctx(aCtx), name(aName), command(aCommand), hub(aHub) { };
+
+ UserCommand(const UserCommand& rhs) : Flags(rhs), cid(rhs.cid), type(rhs.type),
+ ctx(rhs.ctx), name(rhs.name), command(rhs.command), hub(rhs.hub)
+ {
+
+ }
+
+ UserCommand& operator=(const UserCommand& rhs) {
+ cid = rhs.cid; type = rhs.type; ctx = rhs.ctx;
+ name = rhs.name; command = rhs.command; hub = rhs.hub;
+ *((Flags*)this) = rhs;
+ return *this;
+ }
+
+ GETSET(int, cid, Id);
+ GETSET(int, type, Type);
+ GETSET(int, ctx, Ctx);
+ GETSET(string, name, Name);
+ GETSET(string, command, Command);
+ GETSET(string, hub, Hub);
+};
+
+#endif
+
+/**
+ * @file
+ * $Id: UserCommand.h,v 1.2 2005/08/21 14:03:43 olof Exp $
+ */
+
diff --git a/dcpp/UserConnection.cpp b/dcpp/UserConnection.cpp
new file mode 100644
index 0000000..1bef135
--- /dev/null
+++ b/dcpp/UserConnection.cpp
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2001-2005 Jacek Sieka, arnetheduck on gmail point 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 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.
+ */
+
+#include "stdinc.h"
+#include "DCPlusPlus.h"
+
+#include "UserConnection.h"
+
+#include "StringTokenizer.h"
+#include "AdcCommand.h"
+
+const string UserConnection::FEATURE_GET_ZBLOCK = "GetZBlock";
+const string UserConnection::FEATURE_MINISLOTS = "MiniSlots";
+const string UserConnection::FEATURE_XML_BZLIST = "XmlBZList";
+const string UserConnection::FEATURE_ADCGET = "ADCGet";
+const string UserConnection::FEATURE_ZLIB_GET = "ZLIG";
+const string UserConnection::FEATURE_TTHL = "TTHL";
+const string UserConnection::FEATURE_TTHF = "TTHF";
+
+const string UserConnection::FILE_NOT_AVAILABLE = "File Not Available";
+
+const string UserConnection::UPLOAD = "Upload";
+const string UserConnection::DOWNLOAD = "Download";
+
+void Transfer::updateRunningAverage() {
+ u_int32_t tick = GET_TICK();
+ if(tick > lastTick) {
+ u_int32_t diff = tick - lastTick;
+ int64_t tot = getTotal();
+ if(diff == 0) {
+ // No time passed, don't update runningAverage;
+ } else if( ((tick - getStart()) < AVG_PERIOD) ) {
+ runningAverage = getAverageSpeed();
+ } else {
+ int64_t bdiff = tot - last;
+ int64_t avg = bdiff * (int64_t)1000 / diff;
+ if(diff > AVG_PERIOD) {
+ runningAverage = avg;
+ } else {
+ // Weighted average...
+ runningAverage = ((avg * diff) + (runningAverage*(AVG_PERIOD-diff)))/AVG_PERIOD;
+ }
+ }
+ last = tot;
+ }
+ lastTick = tick;
+}
+
+void UserConnection::on(BufferedSocketListener::Line, const string& aLine) throw () {
+
+ if(aLine.length() < 2)
+ return;
+ dcdebug("r:");
+ dcdebug(aLine.c_str());
+ dcdebug("\n");
+
+ if(aLine[0] == 'C' && !isSet(FLAG_NMDC)) {
+ dispatch(aLine);
+ return;
+ } else if(aLine[0] == '$') {
+ setFlag(FLAG_NMDC);
+ } else {
+ // We shouldn't be here?
+ dcdebug("Unknown UserConnection command: %.50s\n", aLine.c_str());
+ return;
+ }
+ string cmd;
+ string param;
+
+ string::size_type x;
+
+ if( (x = aLine.find(' ')) == string::npos) {
+ cmd = aLine;
+ } else {
+ cmd = aLine.substr(0, x);
+ param = aLine.substr(x+1);
+ }
+
+ if(cmd == "$MyNick") {
+ if(!param.empty())
+ fire(UserConnectionListener::MyNick(), this, Text::acpToUtf8(param));
+ } else if(cmd == "$Direction") {
+ x = param.find(" ");
+ if(x != string::npos) {
+ fire(UserConnectionListener::Direction(), this, param.substr(0, x), param.substr(x+1));
+ }
+ } else if(cmd == "$Error") {
+ if(Util::stricmp(param.c_str(), FILE_NOT_AVAILABLE) == 0 ||
+ param.rfind(/*path/file*/" no more exists") != string::npos) {
+ fire(UserConnectionListener::FileNotAvailable(), this);
+ } else {
+ fire(UserConnectionListener::Failed(), this, param);
+ }
+ } else if(cmd == "$FileLength") {
+ if(!param.empty())
+ fire(UserConnectionListener::FileLength(), this, Util::toInt64(param));
+ } else if(cmd == "$GetListLen") {
+ fire(UserConnectionListener::GetListLength(), this);
+ } else if(cmd == "$Get") {
+ x = param.find('$');
+ if(x != string::npos) {
+ fire(UserConnectionListener::Get(), this, Text::acpToUtf8(param.substr(0, x)), Util::toInt64(param.substr(x+1)) - (int64_t)1);
+ }
+ } else if(cmd == "$GetZBlock" || cmd == "$UGetZBlock" || cmd == "$UGetBlock") {
+ string::size_type i = param.find(' ');
+ if(i == string::npos)
+ return;
+ int64_t start = Util::toInt64(param.substr(0, i));
+ if(start < 0) {
+ disconnect();
+ return;
+ }
+ i++;
+ string::size_type j = param.find(' ', i);
+ if(j == string::npos)
+ return;
+ int64_t bytes = Util::toInt64(param.substr(i, j-i));
+ string name = param.substr(j+1);
+ if(cmd == "$GetZBlock")
+ name = Text::acpToUtf8(name);
+ if(cmd == "$UGetBlock") {
+ fire(UserConnectionListener::GetBlock(), this, name, start, bytes);
+ } else {
+ fire(UserConnectionListener::GetZBlock(), this, name, start, bytes);
+ }
+ } else if(cmd == "$Key") {
+ if(!param.empty())
+ fire(UserConnectionListener::Key(), this, param);
+ } else if(cmd == "$Lock") {
+ if(!param.empty()) {
+ x = param.find(" Pk=");
+ if(x != string::npos) {
+ fire(UserConnectionListener::CLock(), this, param.substr(0, x), param.substr(x + 4));
+ } else {
+ // Workaround for faulty linux clients...
+ x = param.find(' ');
+ if(x != string::npos) {
+ setFlag(FLAG_INVALIDKEY);
+ fire(UserConnectionListener::CLock(), this, param.substr(0, x), Util::emptyString);
+ } else {
+ fire(UserConnectionListener::CLock(), this, param, Util::emptyString);
+ }
+ }
+ }
+ } else if(cmd == "$Send") {
+ fire(UserConnectionListener::Send(), this);
+ } else if(cmd == "$Sending") {
+ int64_t bytes = -1;
+ if(!param.empty())
+ bytes = Util::toInt64(param);
+ fire(UserConnectionListener::Sending(), this, bytes);
+ } else if(cmd == "$MaxedOut") {
+ fire(UserConnectionListener::MaxedOut(), this);
+ } else if(cmd == "$Supports") {
+ if(!param.empty()) {
+ fire(UserConnectionListener::Supports(), this, StringTokenizer<string>(param, ' ').getTokens());
+ }
+ } else if(cmd.compare(0, 4, "$ADC") == 0) {
+ dispatch(aLine, true);
+ } else {
+ dcdebug("Unknown NMDC command: %.50s\n", aLine.c_str());
+ }
+}
+
+void UserConnection::on(BufferedSocketListener::Failed, const string& aLine) throw() {
+ setState(STATE_UNCONNECTED);
+ fire(UserConnectionListener::Failed(), this, aLine);
+}
+
+/**
+ * @file
+ * $Id: UserConnection.cpp,v 1.3 2005/11/20 23:30:30 olof Exp $
+ */
diff --git a/dcpp/UserConnection.h b/dcpp/UserConnection.h
new file mode 100644
index 0000000..e4e0827
--- /dev/null
+++ b/dcpp/UserConnection.h
@@ -0,0 +1,405 @@
+/*
+ * Copyright (C) 2001-2005 Jacek Sieka, arnetheduck on gmail point 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 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.
+ */
+
+#if !defined(AFX_USERCONNECTION_H__52BFD1A0_9924_4C07_BAFA_FB9682884841__INCLUDED_)
+#define AFX_USERCONNECTION_H__52BFD1A0_9924_4C07_BAFA_FB9682884841__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+#include "TimerManager.h"
+
+#include "BufferedSocket.h"
+#include "CriticalSection.h"
+#include "File.h"
+#include "User.h"
+#include "AdcCommand.h"
+
+class UserConnection;
+
+class UserConnectionListener {
+public:
+ template<int I> struct X { enum { TYPE = I }; };
+
+ typedef X<0> BytesSent;
+ typedef X<1> Connected;
+ typedef X<2> Data;
+ typedef X<3> Failed;
+ typedef X<4> CLock;
+ typedef X<5> Key;
+ typedef X<6> Direction;
+ typedef X<7> Get;
+ typedef X<8> GetBlock;
+ typedef X<9> GetZBlock;
+ typedef X<10> Sending;
+ typedef X<11> FileLength;
+ typedef X<12> Send;
+ typedef X<13> GetListLength;
+ typedef X<14> MaxedOut;
+ typedef X<15> ModeChange;
+ typedef X<16> MyNick;
+ typedef X<17> TransmitDone;
+ typedef X<18> Supports;
+ typedef X<19> FileNotAvailable;
+ typedef X<20> ADCGet;
+ typedef X<21> ADCSnd;
+ typedef X<22> ADCSta;
+
+ virtual void on(BytesSent, UserConnection*, size_t, size_t) throw() { }
+ virtual void on(Connected, UserConnection*) throw() { }
+ virtual void on(Data, UserConnection*, const u_int8_t*, size_t) throw() { }
+ virtual void on(Failed, UserConnection*, const string&) throw() { }
+ virtual void on(CLock, UserConnection*, const string&, const string&) throw() { }
+ virtual void on(Key, UserConnection*, const string&) throw() { }
+ virtual void on(Direction, UserConnection*, const string&, const string&) throw() { }
+ virtual void on(Get, UserConnection*, const string&, int64_t) throw() { }
+ virtual void on(GetBlock, UserConnection*, const string&, int64_t, int64_t) throw() { }
+ virtual void on(GetZBlock, UserConnection*, const string&, int64_t, int64_t) throw() { }
+ virtual void on(Sending, UserConnection*, int64_t) throw() { }
+ virtual void on(FileLength, UserConnection*, int64_t) throw() { }
+ virtual void on(Send, UserConnection*) throw() { }
+ virtual void on(GetListLength, UserConnection*) throw() { }
+ virtual void on(MaxedOut, UserConnection*) throw() { }
+ virtual void on(ModeChange, UserConnection*) throw() { }
+ virtual void on(MyNick, UserConnection*, const string&) throw() { }
+ virtual void on(TransmitDone, UserConnection*) throw() { }
+ virtual void on(Supports, UserConnection*, const StringList&) throw() { }
+ virtual void on(FileNotAvailable, UserConnection*) throw() { }
+
+ virtual void on(AdcCommand::SUP, UserConnection*, const AdcCommand&) throw() { }
+ virtual void on(AdcCommand::INF, UserConnection*, const AdcCommand&) throw() { }
+ virtual void on(AdcCommand::NTD, UserConnection*, const AdcCommand&) throw() { }
+ virtual void on(AdcCommand::GET, UserConnection*, const AdcCommand&) throw() { }
+ virtual void on(AdcCommand::SND, UserConnection*, const AdcCommand&) throw() { }
+ virtual void on(AdcCommand::STA, UserConnection*, const AdcCommand&) throw() { }
+ virtual void on(AdcCommand::RES, UserConnection*, const AdcCommand&) throw() { }
+ virtual void on(AdcCommand::GFI, UserConnection*, const AdcCommand&) throw() { }
+};
+
+class ConnectionQueueItem;
+
+class Transfer {
+public:
+ Transfer() : userConnection(NULL), start(0), lastTick(GET_TICK()), runningAverage(0),
+ last(0), actual(0), pos(0), startPos(0), size(-1) { };
+ virtual ~Transfer() { };
+
+ int64_t getPos() const { return pos; };
+ void setPos(int64_t aPos) { pos = aPos; };
+
+ void resetPos() { pos = getStartPos(); };
+ void setStartPos(int64_t aPos) { startPos = aPos; pos = aPos; };
+ int64_t getStartPos() const { return startPos; }
+
+ void addPos(int64_t aBytes, int64_t aActual) { pos += aBytes; actual+= aActual; };
+
+ enum { AVG_PERIOD = 30000 };
+ void updateRunningAverage();
+
+ int64_t getTotal() const { return getPos() - getStartPos(); };
+ int64_t getActual() const { return actual; };
+
+ int64_t getSize() const { return size; };
+ void setSize(int64_t aSize) { size = aSize; };
+ void setSize(const string& aSize) { setSize(Util::toInt64(aSize)); };
+
+ int64_t getAverageSpeed() const {
+ int64_t diff = (int64_t)(GET_TICK() - getStart());
+ return (diff > 0) ? (getTotal() * (int64_t)1000 / diff) : 0;
+ }
+
+ int64_t getSecondsLeft() {
+ updateRunningAverage();
+ int64_t avg = getRunningAverage();
+ return (avg > 0) ? ((getSize() - getPos()) / avg) : 0;
+ }
+
+ int64_t getBytesLeft() const {
+ return getSize() - getPos();
+ }
+
+ GETSET(UserConnection*, userConnection, UserConnection);
+ GETSET(u_int32_t, start, Start);
+ GETSET(u_int32_t, lastTick, LastTick);
+ GETSET(int64_t, runningAverage, RunningAverage);
+private:
+ Transfer(const Transfer&);
+ Transfer& operator=(const Transfer&);
+
+ /** Bytes on last avg update */
+ int64_t last;
+ /** Total actual bytes transfered this session (compression?) */
+ int64_t actual;
+ /** Write position in file */
+ int64_t pos;
+ /** Starting position */
+ int64_t startPos;
+ /** Target size of this transfer */
+ int64_t size;
+
+};
+
+class ServerSocket;
+class Upload;
+class Download;
+
+class UserConnection : public Speaker<UserConnectionListener>,
+ private BufferedSocketListener, public Flags, private CommandHandler<UserConnection>
+{
+public:
+ friend class ConnectionManager;
+
+ typedef UserConnection* Ptr;
+ typedef vector<Ptr> List;
+ typedef List::iterator Iter;
+
+ static const string FEATURE_GET_ZBLOCK;
+ static const string FEATURE_MINISLOTS;
+ static const string FEATURE_XML_BZLIST;
+ static const string FEATURE_ADCGET;
+ static const string FEATURE_ZLIB_GET;
+ static const string FEATURE_TTHL;
+ static const string FEATURE_TTHF;
+
+ static const string FILE_NOT_AVAILABLE;
+
+ enum Modes {
+ MODE_COMMAND = BufferedSocket::MODE_LINE,
+ MODE_DATA = BufferedSocket::MODE_DATA
+ };
+
+ enum Flags {
+ FLAG_NMDC = 0x01,
+ FLAG_UPLOAD = FLAG_NMDC << 1,
+ FLAG_DOWNLOAD = FLAG_UPLOAD << 1,
+ FLAG_INCOMING = FLAG_DOWNLOAD << 1,
+ FLAG_HASSLOT = FLAG_INCOMING << 1,
+ FLAG_HASEXTRASLOT = FLAG_HASSLOT << 1,
+ FLAG_INVALIDKEY = FLAG_HASEXTRASLOT << 1,
+ FLAG_SUPPORTS_GETZBLOCK = FLAG_INVALIDKEY << 1,
+ FLAG_SUPPORTS_MINISLOTS = FLAG_SUPPORTS_GETZBLOCK << 1,
+ FLAG_SUPPORTS_XML_BZLIST = FLAG_SUPPORTS_MINISLOTS << 1,
+ FLAG_SUPPORTS_ADCGET = FLAG_SUPPORTS_XML_BZLIST << 1,
+ FLAG_SUPPORTS_ZLIB_GET = FLAG_SUPPORTS_ADCGET << 1,
+ FLAG_SUPPORTS_TTHL = FLAG_SUPPORTS_ZLIB_GET << 1,
+ FLAG_SUPPORTS_TTHF = FLAG_SUPPORTS_TTHL << 1
+ };
+
+ enum States {
+ // ConnectionManager
+ STATE_UNCONNECTED,
+ STATE_CONNECT,
+
+ // Handshake
+ STATE_SUPNICK, // ADC: SUP, Nmdc: $Nick
+ STATE_INF,
+ STATE_LOCK,
+ STATE_DIRECTION,
+ STATE_KEY,
+
+ // UploadManager
+ STATE_GET,
+ STATE_SEND,
+ STATE_DONE,
+ // DownloadManager
+ STATE_FILELENGTH,
+ STATE_TREE
+
+ };
+
+ short getNumber() { return (short)((((size_t)this)>>2) & 0x7fff); };
+
+ // NMDC stuff
+ void myNick(const string& aNick) { send("$MyNick " + Text::utf8ToAcp(aNick) + '|'); }
+ void lock(const string& aLock, const string& aPk) { send ("$Lock " + aLock + " Pk=" + aPk + '|'); }
+ void key(const string& aKey) { send("$Key " + aKey + '|'); }
+ void direction(const string& aDirection, int aNumber) { send("$Direction " + aDirection + " " + Util::toString(aNumber) + '|'); }
+ void get(const string& aFile, int64_t aResume) { send("$Get " + aFile + "$" + Util::toString(aResume + 1) + '|'); }; // No acp - utf conversion here...
+ void getZBlock(const string& aFile, int64_t aResume, int64_t aBytes, bool utf8) { send((utf8 ? "$UGetZBlock " : "$GetZBlock ") + Util::toString(aResume) + ' ' + Util::toString(aBytes) + ' ' + aFile + '|'); };
+ void uGetBlock(const string& aFile, int64_t aResume, int64_t aBytes) { send("$UGetBlock " + Util::toString(aResume) + ' ' + Util::toString(aBytes) + ' ' + aFile + '|'); }
+ void fileLength(const string& aLength) { send("$FileLength " + aLength + '|'); }
+ void startSend() { send("$Send|"); }
+ void sending(int64_t bytes) { send(bytes == -1 ? string("$Sending|") : "$Sending " + Util::toString(bytes) + "|"); };
+ void error(const string& aError) { send("$Error " + aError + '|'); };
+ void listLen(const string& aLength) { send("$ListLen " + aLength + '|'); };
+ void maxedOut() { isSet(FLAG_NMDC) ? send("$MaxedOut|") : sta(AdcCommand::SEV_RECOVERABLE, AdcCommand::ERROR_SLOTS_FULL, "Slots full"); };
+ void fileNotAvail() { isSet(FLAG_NMDC) ? send("$Error " + FILE_NOT_AVAILABLE + "|") : sta(AdcCommand::SEV_RECOVERABLE, AdcCommand::ERROR_FILE_NOT_AVAILABLE, FILE_NOT_AVAILABLE); }
+
+ // ADC Stuff
+ void sup(const StringList& features) {
+ AdcCommand c(AdcCommand::CMD_SUP);
+ for(StringIterC i = features.begin(); i != features.end(); ++i)
+ c.addParam(*i);
+ send(c);
+ }
+ void inf(bool withToken) {
+ AdcCommand c(AdcCommand::CMD_INF);
+ c.addParam("CI", SETTING(CLIENT_ID));
+ if(withToken) {
+ c.addParam("TO", getToken());
+ }
+ send(c);
+ }
+ void get(const string& aType, const string& aName, const int64_t aStart, const int64_t aBytes) { send(AdcCommand(AdcCommand::CMD_GET).addParam(aType).addParam(aName).addParam(Util::toString(aStart)).addParam(Util::toString(aBytes))); }
+ void snd(const string& aType, const string& aName, const int64_t aStart, const int64_t aBytes) { send(AdcCommand(AdcCommand::CMD_SND).addParam(aType).addParam(aName).addParam(Util::toString(aStart)).addParam(Util::toString(aBytes))); }
+ void ntd() { send(AdcCommand(AdcCommand::CMD_NTD)); }
+ void sta(AdcCommand::Severity sev, AdcCommand::Error err, const string& desc) { send(AdcCommand(AdcCommand::CMD_STA).addParam(Util::toString(100 * sev + err)).addParam(desc)); }
+
+ void send(const AdcCommand& c) { send(c.toString(isSet(FLAG_NMDC), isSet(FLAG_SUPPORTS_ADCGET))); }
+
+ void supports(const StringList& feat) {
+ string x;
+ for(StringList::const_iterator i = feat.begin(); i != feat.end(); ++i) {
+ x+= *i + ' ';
+ }
+ send("$Supports " + x + '|');
+ }
+ void setDataMode(int64_t aBytes = -1) { dcassert(socket); socket->setDataMode(aBytes); }
+ void setLineMode() { dcassert(socket); socket->setLineMode(); };
+
+ void connect(const string& aServer, short aPort) throw(SocketException) {
+ socket->connect(aServer, aPort);
+ }
+
+ void accept(const ServerSocket& aServer) throw(SocketException) {
+ socket->accept(aServer);
+ }
+
+ void disconnect() { if(socket) socket->disconnect(); };
+ void transmitFile(InputStream* f) {
+ socket->transmitFile(f);
+ };
+
+ const string& getDirectionString() {
+ dcassert(isSet(FLAG_UPLOAD) ^ isSet(FLAG_DOWNLOAD));
+ return isSet(FLAG_UPLOAD) ? UPLOAD : DOWNLOAD;
+ }
+
+ User::Ptr& getUser() { return user; };
+
+ string getRemoteIp() const { return socket->getIp(); }
+ Download* getDownload() { dcassert(isSet(FLAG_DOWNLOAD)); return download; };
+ void setDownload(Download* d) { dcassert(isSet(FLAG_DOWNLOAD)); download = d; };
+ Upload* getUpload() { dcassert(isSet(FLAG_UPLOAD)); return upload; };
+ void setUpload(Upload* u) { dcassert(isSet(FLAG_UPLOAD)); upload = u; };
+
+ void handle(AdcCommand::SUP t, const AdcCommand& c) {
+ fire(t, this, c);
+ }
+ void handle(AdcCommand::INF t, const AdcCommand& c) {
+ fire(t, this, c);
+ }
+ void handle(AdcCommand::GET t, const AdcCommand& c) {
+ fire(t, this, c);
+ }
+ void handle(AdcCommand::SND t, const AdcCommand& c) {
+ fire(t, this, c);
+ }
+ void handle(AdcCommand::STA t, const AdcCommand& c) {
+ fire(t, this, c);
+ }
+ void handle(AdcCommand::NTD t, const AdcCommand& c) {
+ fire(t, this, c);
+ }
+ void handle(AdcCommand::RES t, const AdcCommand& c) {
+ fire(t, this, c);
+ }
+ void handle(AdcCommand::GFI t, const AdcCommand& c) {
+ fire(t, this, c);
+ }
+
+ // Ignore any other ADC commands for now
+ template<typename T>
+ void handle(T , const AdcCommand& ) {
+ }
+
+ GETSET(string, nick, Nick);
+ GETSET(string, token, Token);
+ GETSET(ConnectionQueueItem*, cqi, CQI);
+ GETSET(States, state, State);
+ GETSET(u_int32_t, lastActivity, LastActivity);
+ GETSET(Download*, tempDownload, TempDownload);
+
+private:
+ BufferedSocket* socket;
+ User::Ptr user;
+
+ static const string UPLOAD, DOWNLOAD;
+
+ union {
+ Download* download;
+ Upload* upload;
+ };
+
+ // We only want ConnectionManager to create this...
+ UserConnection() throw(SocketException) : cqi(NULL), state(STATE_UNCONNECTED), lastActivity(0),
+ socket(BufferedSocket::getSocket(0)), download(NULL) {
+
+ socket->addListener(this);
+ };
+
+ virtual ~UserConnection() throw() {
+ socket->removeListener(this);
+ removeListeners();
+ BufferedSocket::putSocket(socket);
+ };
+ friend struct DeleteFunction<UserConnection*>;
+
+ UserConnection(const UserConnection&);
+ UserConnection& operator=(const UserConnection&);
+
+ void setUser(const User::Ptr& aUser) {
+ user = aUser;
+ };
+
+ void onLine(const string& aLine) throw();
+
+ void send(const string& aString) {
+ lastActivity = GET_TICK();
+ socket->write(aString);
+ }
+
+ virtual void on(Connected) throw() {
+ lastActivity = GET_TICK();
+ fire(UserConnectionListener::Connected(), this);
+ }
+ virtual void on(Line, const string&) throw();
+ virtual void on(Data, u_int8_t* data, size_t len) throw() {
+ lastActivity = GET_TICK();
+ fire(UserConnectionListener::Data(), this, data, len);
+ }
+ virtual void on(BytesSent, size_t bytes, size_t actual) throw() {
+ lastActivity = GET_TICK();
+ fire(UserConnectionListener::BytesSent(), this, bytes, actual);
+ }
+ virtual void on(ModeChange) throw() {
+ lastActivity = GET_TICK();
+ fire(UserConnectionListener::ModeChange(), this);
+ }
+ virtual void on(TransmitDone) throw() { fire(UserConnectionListener::TransmitDone(), this); }
+ virtual void on(Failed, const string&) throw();
+};
+
+#endif // !defined(AFX_USERCONNECTION_H__52BFD1A0_9924_4C07_BAFA_FB9682884841__INCLUDED_)
+
+/**
+ * @file
+ * $Id: UserConnection.h,v 1.2 2005/08/21 14:03:43 olof Exp $
+ */
+
diff --git a/dcpp/Util.cpp b/dcpp/Util.cpp
new file mode 100644
index 0000000..e3eff6f
--- /dev/null
+++ b/dcpp/Util.cpp
@@ -0,0 +1,882 @@
+/*
+ * Copyright (C) 2001-2005 Jacek Sieka, arnetheduck on gmail point 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 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.
+ */
+#include "stdinc.h"
+#include "DCPlusPlus.h"
+
+#include "Util.h"
+#include "File.h"
+
+#include "SettingsManager.h"
+#include "ResourceManager.h"
+#include "StringTokenizer.h"
+#include "SettingsManager.h"
+#include "version.h"
+
+#ifndef _WIN32
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <sys/utsname.h>
+#include <ctype.h>
+#endif
+#include <locale.h>
+
+#include "CID.h"
+
+#include "FastAlloc.h"
+
+#ifndef _DEBUG
+FastCriticalSection FastAllocBase::cs;
+#endif
+
+string Util::emptyString;
+wstring Util::emptyStringW;
+tstring Util::emptyStringT;
+
+bool Util::away = false;
+string Util::awayMsg;
+time_t Util::awayTime;
+
+Util::CountryList Util::countries;
+string Util::appPath;
+
+static void sgenrand(unsigned long seed);
+
+void Util::initialize() {
+ setlocale(LC_ALL, "");
+
+ Text::initialize();
+
+ sgenrand((unsigned long)time(NULL));
+
+#ifdef _WIN32
+ TCHAR buf[MAX_PATH+1];
+ GetModuleFileName(NULL, buf, MAX_PATH);
+ appPath = Text::fromT(buf);
+ appPath.erase(appPath.rfind('\\') + 1);
+#else // _WIN32
+ char* home = getenv("HOME");
+ if (home) {
+ appPath = Text::fromT(home);
+ appPath += "/.dc++/";
+ }
+#endif // _WIN32
+
+ try {
+ // This product includes GeoIP data created by MaxMind, available from http://maxmind.com/
+ // Updates at http://www.maxmind.com/app/geoip_country
+ string file = Util::getAppPath() + "GeoIpCountryWhois.csv";
+ string data = File(file, File::READ, File::OPEN).read();
+
+ const char* start = data.c_str();
+ string::size_type linestart = 0;
+ string::size_type comma1 = 0;
+ string::size_type comma2 = 0;
+ string::size_type comma3 = 0;
+ string::size_type comma4 = 0;
+ string::size_type lineend = 0;
+ CountryIter last = countries.end();
+ u_int32_t startIP = 0;
+ u_int32_t endIP = 0, endIPprev = 0;
+
+ for(;;) {
+ comma1 = data.find(',', linestart);
+ if(comma1 == string::npos) break;
+ comma2 = data.find(',', comma1 + 1);
+ if(comma2 == string::npos) break;
+ comma3 = data.find(',', comma2 + 1);
+ if(comma3 == string::npos) break;
+ comma4 = data.find(',', comma3 + 1);
+ if(comma4 == string::npos) break;
+ lineend = data.find('\n', comma4);
+ if(lineend == string::npos) break;
+
+ startIP = Util::toUInt32(start + comma2 + 2);
+ endIP = Util::toUInt32(start + comma3 + 2);
+ u_int16_t* country = (u_int16_t*)(start + comma4 + 2);
+ if((startIP-1) != endIPprev)
+ last = countries.insert(last, make_pair((startIP-1), (u_int16_t)16191));
+ last = countries.insert(last, make_pair(endIP, *country));
+
+ endIPprev = endIP;
+ linestart = lineend + 1;
+ }
+ } catch(const FileException&) {
+ }
+}
+
+string Util::getConfigPath() {
+#ifdef _WIN32
+ return getAppPath();
+#else
+ char* home = getenv("HOME");
+ if (home) {
+#ifdef __APPLE__
+ return string(home) + "/Library/Application Support/Mac DC++/";
+#else
+ return string(home) + "/.dc++/";
+#endif // __APPLE__
+ }
+ return emptyString;
+#endif // _WIN32
+}
+
+string Util::validateMessage(string tmp, bool reverse, bool checkNewLines) {
+ string::size_type i = 0;
+
+ if(reverse) {
+ while( (i = tmp.find("&#36;", i)) != string::npos) {
+ tmp.replace(i, 5, "$");
+ i++;
+ }
+ i = 0;
+ while( (i = tmp.find("&#124;", i)) != string::npos) {
+ tmp.replace(i, 6, "|");
+ i++;
+ }
+ i = 0;
+ while( (i = tmp.find("&amp;", i)) != string::npos) {
+ tmp.replace(i, 5, "&");
+ i++;
+ }
+ if(checkNewLines) {
+ // Check all '<' and '|' after newlines...
+ i = 0;
+ while( (i = tmp.find('\n', i)) != string::npos) {
+ if(i + 1 < tmp.length()) {
+ if(tmp[i+1] == '[' || tmp[i+1] == '<') {
+ tmp.insert(i+1, "- ");
+ i += 2;
+ }
+ }
+ i++;
+ }
+ }
+ } else {
+ i = 0;
+ while( (i = tmp.find("&amp;", i)) != string::npos) {
+ tmp.replace(i, 1, "&amp;");
+ i += 4;
+ }
+ i = 0;
+ while( (i = tmp.find("&#36;", i)) != string::npos) {
+ tmp.replace(i, 1, "&amp;");
+ i += 4;
+ }
+ i = 0;
+ while( (i = tmp.find("&#124;", i)) != string::npos) {
+ tmp.replace(i, 1, "&amp;");
+ i += 4;
+ }
+ i = 0;
+ while( (i = tmp.find('$', i)) != string::npos) {
+ tmp.replace(i, 1, "&#36;");
+ i += 4;
+ }
+ i = 0;
+ while( (i = tmp.find('|', i)) != string::npos) {
+ tmp.replace(i, 1, "&#124;");
+ i += 5;
+ }
+ }
+ return tmp;
+}
+
+#ifdef _WIN32
+static const char badChars[] = {
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
+ 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30,
+ 31, '<', '>', '/', '"', '|', '?', '*', 0
+};
+#else
+
+static const char badChars[] = {
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
+ 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30,
+ 31, '<', '>', '\\', '"', '|', '?', '*', 0
+};
+#endif
+
+/**
+ * Replaces all strange characters in a file with '_'
+ * @todo Check for invalid names such as nul and aux...
+ */
+string Util::validateFileName(string tmp) {
+ string::size_type i = 0;
+
+ // First, eliminate forbidden chars
+ while( (i = tmp.find_first_of(badChars, i)) != string::npos) {
+ tmp[i] = '_';
+ i++;
+ }
+
+ // Then, eliminate all ':' that are not the second letter ("c:\...")
+ i = 0;
+ while( (i = tmp.find(':', i)) != string::npos) {
+ if(i == 1) {
+ i++;
+ continue;
+ }
+ tmp[i] = '_';
+ i++;
+ }
+
+ // Remove the .\ that doesn't serve any purpose
+ i = 0;
+ while( (i = tmp.find("\\.\\", i)) != string::npos) {
+ tmp.erase(i+1, 2);
+ }
+ i = 0;
+ while( (i = tmp.find("/./", i)) != string::npos) {
+ tmp.erase(i+1, 2);
+ }
+
+ // Remove any double \\ that are not at the beginning of the path...
+ i = 1;
+ while( (i = tmp.find("\\\\", i)) != string::npos) {
+ tmp.erase(i+1, 1);
+ }
+ i = 1;
+ while( (i = tmp.find("//", i)) != string::npos) {
+ tmp.erase(i+1, 1);
+ }
+
+ // And last, but not least, the infamous ..\! ...
+ i = 0;
+ while( ((i = tmp.find("\\..\\", i)) != string::npos) ) {
+ tmp[i + 1] = '_';
+ tmp[i + 2] = '_';
+ tmp[i + 3] = '_';
+ i += 2;
+ }
+ i = 0;
+ while( ((i = tmp.find("/../", i)) != string::npos) ) {
+ tmp[i + 1] = '_';
+ tmp[i + 2] = '_';
+ tmp[i + 3] = '_';
+ i += 2;
+ }
+
+ return tmp;
+}
+
+string Util::getShortTimeString() {
+ char buf[255];
+ time_t _tt = time(NULL);
+ tm* _tm = localtime(&_tt);
+ if(_tm == NULL) {
+ strcpy(buf, "xx:xx");
+ } else {
+ strftime(buf, 254, SETTING(TIME_STAMPS_FORMAT).c_str(), _tm);
+ }
+ return buf;
+}
+
+/**
+ * Decodes a URL the best it can...
+ * Default ports:
+ * http:// -> port 80
+ * dchub:// -> port 411
+ */
+void Util::decodeUrl(const string& url, string& aServer, u_int16_t& aPort, string& aFile) {
+ // First, check for a protocol: xxxx://
+ string::size_type i = 0, j, k;
+
+ aServer = emptyString;
+ aFile = emptyString;
+
+ if( (j=url.find("://", i)) != string::npos) {
+ // Protocol found
+ string protocol = url.substr(0, j);
+ i = j + 3;
+
+ if(protocol == "http") {
+ aPort = 80;
+ } else if(protocol == "dchub") {
+ aPort = 411;
+ }
+ }
+
+ if( (j=url.find('/', i)) != string::npos) {
+ // We have a filename...
+ aFile = url.substr(j);
+ }
+
+ if( (k=url.find(':', i)) != string::npos) {
+ // Port
+ if(j == string::npos) {
+ aPort = (short)Util::toInt(url.substr(k+1));
+ } else if(k < j) {
+ aPort = (short)Util::toInt(url.substr(k+1, j-k-1));
+ }
+ } else {
+ k = j;
+ }
+
+ if(k == string::npos)
+ aServer = url;
+ else
+ aServer = url.substr(i, k-i);
+}
+
+string Util::getAwayMessage() {
+ return (formatTime(awayMsg.empty() ? SETTING(DEFAULT_AWAY_MESSAGE) : awayMsg, awayTime)) + " <DC++ v" VERSIONSTRING ">";
+}
+string Util::formatBytes(int64_t aBytes) {
+ char buf[64];
+ if(aBytes < 1024) {
+ sprintf(buf, "%d %s", (int)(aBytes&0xffffffff), CSTRING(B));
+ } else if(aBytes < 1024*1024) {
+ sprintf(buf, "%.02f %s", (double)aBytes/(1024.0), CSTRING(KiB));
+ } else if(aBytes < 1024*1024*1024) {
+ sprintf(buf, "%.02f %s", (double)aBytes/(1024.0*1024.0), CSTRING(MiB));
+ } else if(aBytes < (int64_t)1024*1024*1024*1024) {
+ sprintf(buf, "%.02f %s", (double)aBytes/(1024.0*1024.0*1024.0), CSTRING(GiB));
+ } else if(aBytes < (int64_t)1024*1024*1024*1024*1024) {
+ sprintf(buf, "%.02f %s", (double)aBytes/(1024.0*1024.0*1024.0*1024.0), CSTRING(TiB));
+ } else {
+ sprintf(buf, "%.02f %s", (double)aBytes/(1024.0*1024.0*1024.0*1024.0*1024.0), CSTRING(PIB));
+ }
+
+ return buf;
+}
+
+string Util::formatExactSize(int64_t aBytes) {
+#ifdef _WIN32
+ TCHAR buf[64];
+ TCHAR number[64];
+ NUMBERFMT nf;
+ _stprintf(number, _T("%I64d"), aBytes);
+ TCHAR Dummy[16];
+
+ /*No need to read these values from the system because they are not
+ used to format the exact size*/
+ nf.NumDigits = 0;
+ nf.LeadingZero = 0;
+ nf.NegativeOrder = 0;
+ nf.lpDecimalSep = _T(",");
+
+ GetLocaleInfo( LOCALE_SYSTEM_DEFAULT, LOCALE_SGROUPING, Dummy, 16 );
+ nf.Grouping = _tstoi(Dummy);
+ GetLocaleInfo( LOCALE_SYSTEM_DEFAULT, LOCALE_STHOUSAND, Dummy, 16 );
+ nf.lpThousandSep = Dummy;
+
+ GetNumberFormat(LOCALE_USER_DEFAULT, 0, number, &nf, buf, sizeof(buf)/sizeof(buf[0]));
+
+ _stprintf(buf, _T("%s %s"), buf, CTSTRING(B));
+ return Text::fromT(buf);
+#else
+ char buf[64];
+ sprintf(buf, "%'lld", aBytes);
+ sprintf(buf, "%s %s", buf, CSTRING(B));
+ return buf;
+#endif
+}
+
+string Util::getLocalIp() {
+ string tmp;
+
+ char buf[256];
+ gethostname(buf, 255);
+ hostent* he = gethostbyname(buf);
+ if(he == NULL || he->h_addr_list[0] == 0)
+ return Util::emptyString;
+ sockaddr_in dest;
+ int i = 0;
+
+ // We take the first ip as default, but if we can find a better one, use it instead...
+ memcpy(&(dest.sin_addr), he->h_addr_list[i++], he->h_length);
+ tmp = inet_ntoa(dest.sin_addr);
+ if(Util::isPrivateIp(tmp) || strncmp(tmp.c_str(), "169", 3) == 0) {
+ while(he->h_addr_list[i]) {
+ memcpy(&(dest.sin_addr), he->h_addr_list[i], he->h_length);
+ string tmp2 = inet_ntoa(dest.sin_addr);
+ if(!Util::isPrivateIp(tmp2) && strncmp(tmp2.c_str(), "169", 3) != 0) {
+ tmp = tmp2;
+ }
+ i++;
+ }
+ }
+ return tmp;
+}
+
+bool Util::isPrivateIp(string const& ip) {
+ struct in_addr addr;
+
+ addr.s_addr = inet_addr(ip.c_str());
+
+ if (addr.s_addr != INADDR_NONE) {
+ unsigned long haddr = ntohl(addr.s_addr);
+ return ((haddr & 0xff000000) == 0x0a000000 || // 10.0.0.0/8
+ (haddr & 0xff000000) == 0x7f000000 || // 127.0.0.0/8
+ (haddr & 0xfff00000) == 0xac100000 || // 172.16.0.0/12
+ (haddr & 0xffff0000) == 0xc0a80000); // 192.168.0.0/16
+ }
+ return false;
+}
+
+typedef const u_int8_t* ccp;
+static wchar_t utf8ToLC(ccp& str) {
+ wchar_t c = 0;
+ if(str[0] & 0x80) {
+ if(str[0] & 0x40) {
+ if(str[0] & 0x20) {
+ if(str[1] == 0 || str[2] == 0 ||
+ !((((unsigned char)str[1]) & ~0x3f) == 0x80) ||
+ !((((unsigned char)str[2]) & ~0x3f) == 0x80))
+ {
+ str++;
+ return 0;
+ }
+ c = ((wchar_t)(unsigned char)str[0] & 0xf) << 12 |
+ ((wchar_t)(unsigned char)str[1] & 0x3f) << 6 |
+ ((wchar_t)(unsigned char)str[2] & 0x3f);
+ str += 3;
+ } else {
+ if(str[1] == 0 ||
+ !((((unsigned char)str[1]) & ~0x3f) == 0x80))
+ {
+ str++;
+ return 0;
+ }
+ c = ((wchar_t)(unsigned char)str[0] & 0x1f) << 6 |
+ ((wchar_t)(unsigned char)str[1] & 0x3f);
+ str += 2;
+ }
+ } else {
+ str++;
+ return 0;
+ }
+ } else {
+ wchar_t c = Text::asciiToLower((char)str[0]);
+ str++;
+ return c;
+ }
+
+ return Text::toLower(c);
+}
+
+string::size_type Util::findSubString(const string& aString, const string& aSubString, string::size_type start) throw() {
+ if(aString.length() < start)
+ return (string::size_type)string::npos;
+
+ if(aString.length() - start < aSubString.length())
+ return (string::size_type)string::npos;
+
+ if(aSubString.empty())
+ return 0;
+
+ // Hm, should start measure in characters or in bytes? bytes for now...
+ const u_int8_t* tx = (const u_int8_t*)aString.c_str() + start;
+ const u_int8_t* px = (const u_int8_t*)aSubString.c_str();
+
+ const u_int8_t* end = tx + aString.length() - start - aSubString.length() + 1;
+
+ wchar_t wp = utf8ToLC(px);
+
+ while(tx < end) {
+ const u_int8_t* otx = tx;
+ if(wp == utf8ToLC(tx)) {
+ const u_int8_t* px2 = px;
+ const u_int8_t* tx2 = tx;
+
+ for(;;) {
+ if(*px2 == 0)
+ return otx - (u_int8_t*)aString.c_str();
+
+ if(utf8ToLC(px2) != utf8ToLC(tx2))
+ break;
+ }
+ }
+ }
+ return (string::size_type)string::npos;
+}
+
+int Util::stricmp(const char* a, const char* b) {
+ while(*a) {
+ wchar_t ca = 0, cb = 0;
+ int na = Text::utf8ToWc(a, ca);
+ int nb = Text::utf8ToWc(b, cb);
+ ca = Text::toLower(ca);
+ cb = Text::toLower(cb);
+ if(ca != cb) {
+ return (int)ca - (int)cb;
+ }
+ a += abs(na);
+ b += abs(nb);
+ }
+ wchar_t ca = 0, cb = 0;
+ Text::utf8ToWc(a, ca);
+ Text::utf8ToWc(b, cb);
+
+ return (int)Text::toLower(ca) - (int)Text::toLower(cb);
+}
+
+int Util::strnicmp(const char* a, const char* b, size_t n) {
+ const char* end = a + n;
+ while(*a && a < end) {
+ wchar_t ca = 0, cb = 0;
+ int na = Text::utf8ToWc(a, ca);
+ int nb = Text::utf8ToWc(b, cb);
+ ca = Text::toLower(ca);
+ cb = Text::toLower(cb);
+ if(ca != cb) {
+ return (int)ca - (int)cb;
+ }
+ a += abs(na);
+ b += abs(nb);
+ }
+ wchar_t ca = 0, cb = 0;
+ Text::utf8ToWc(a, ca);
+ Text::utf8ToWc(b, cb);
+ return (a >= end) ? 0 : ((int)Text::toLower(ca) - (int)Text::toLower(cb));
+}
+
+string Util::encodeURI(const string& aString, bool reverse) {
+ // reference: rfc2396
+ string tmp = aString;
+ if(reverse) {
+ string::size_type idx;
+ for(idx = 0; idx < tmp.length(); ++idx) {
+ if(tmp.length() > idx + 2 && tmp[idx] == '%' && isxdigit(tmp[idx+1]) && isxdigit(tmp[idx+2])) {
+ tmp[idx] = fromHexEscape(tmp.substr(idx+1,2));
+ tmp.erase(idx+1, 2);
+ } else { // reference: rfc1630, magnet-uri draft
+ if(tmp[idx] == '+')
+ tmp[idx] = ' ';
+ }
+ }
+ } else {
+ const string disallowed = ";/?:@&=+$," // reserved
+ "<>#%\" " // delimiters
+ "{}|\\^[]`"; // unwise
+ string::size_type idx, loc;
+ for(idx = 0; idx < tmp.length(); ++idx) {
+ if(tmp[idx] == ' ') {
+ tmp[idx] = '+';
+ } else {
+ if(tmp[idx] <= 0x1F || tmp[idx] >= 0x7f || (loc = disallowed.find_first_of(tmp[idx])) != string::npos) {
+ tmp.replace(idx, 1, toHexEscape(tmp[idx]));
+ idx+=2;
+ }
+ }
+ }
+ }
+ return tmp;
+}
+
+/**
+ * This function takes a string and a set of parameters and transforms them according to
+ * a simple formatting rule, similar to strftime. In the message, every parameter should be
+ * represented by %[name]. It will then be replaced by the corresponding item in
+ * the params stringmap. After that, the string is passed through strftime with the current
+ * date/time and then finally written to the log file. If the parameter is not present at all,
+ * it is removed from the string completely...
+ */
+string Util::formatParams(const string& msg, StringMap& params) {
+ string result = msg;
+
+ string::size_type i, j, k;
+ i = 0;
+ while (( j = result.find("%[", i)) != string::npos) {
+ if( (result.size() < j + 2) || ((k = result.find(']', j + 2)) == string::npos) ) {
+ break;
+ }
+ string name = result.substr(j + 2, k - j - 2);
+ StringMapIter smi = params.find(name);
+ if(smi == params.end()) {
+ result.erase(j, k-j + 1);
+ i = j;
+ } else {
+ if(smi->second.find('%') != string::npos) {
+ string tmp = smi->second; // replace all % in params with %% for strftime
+ string::size_type m = 0;
+ while(( m = tmp.find('%', m)) != string::npos) {
+ tmp.replace(m, 1, "%%");
+ m+=2;
+ }
+ result.replace(j, k-j + 1, tmp);
+ i = j + tmp.size();
+ } else {
+ result.replace(j, k-j + 1, smi->second);
+ i = j + smi->second.size();
+ }
+ }
+ }
+
+ result = formatTime(result, time(NULL));
+
+ return result;
+}
+
+/** Fix for wide formatting bug in wcsftime in the ms c lib for multibyte encodings of unicode in singlebyte locales */
+string fixedftime(const string& format, struct tm* t) {
+ string ret = format;
+ const char codes[] = "aAbBcdHIjmMpSUwWxXyYzZ%";
+
+ char tmp[4];
+ tmp[0] = '%';
+ tmp[1] = tmp[2] = tmp[3] = 0;
+
+ StringMap sm;
+ AutoArray<char> buf(1024);
+ for(size_t i = 0; i < sizeof(codes); ++i) {
+ tmp[1] = codes[i];
+ tmp[2] = 0;
+ strftime(buf, 1024-1, tmp, t);
+ sm[tmp] = buf;
+
+ tmp[1] = '#';
+ tmp[2] = codes[i];
+ strftime(buf, 1024-1, tmp, t);
+ sm[tmp] = buf;
+ }
+
+ for(StringMapIter i = sm.begin(); i != sm.end(); ++i) {
+ for(string::size_type j = ret.find(i->first); j != string::npos; j = ret.find(i->first, j)) {
+ ret.replace(j, i->first.length(), i->second);
+ j += i->second.length() - i->first.length();
+ }
+ }
+
+ return ret;
+}
+
+string Util::formatTime(const string &msg, const time_t t) {
+ if (!msg.empty()) {
+ size_t bufsize = msg.size() + 256;
+ struct tm* loc = localtime(&t);
+
+ if(!loc) {
+ return Util::emptyString;
+ }
+#if _WIN32
+ AutoArray<TCHAR> buf(bufsize);
+
+ if(!_tcsftime(buf, bufsize-1, Text::toT(msg).c_str(), loc)) {
+ return fixedftime(msg, loc);
+ }
+
+ return Text::fromT(tstring(buf));
+#else
+ // will this give wide representations for %a and %A?
+ // surely win32 can't have a leg up on linux/unixen in this area. - Todd
+ AutoArray<char> buf(bufsize);
+
+ while(!strftime(buf, bufsize-1, msg.c_str(), loc)) {
+ bufsize+=64;
+ buf = new char[bufsize];
+ }
+
+ return string(buf);
+#endif
+ }
+ return Util::emptyString;
+}
+
+/* Below is a high-speed random number generator with much
+ better granularity than the CRT one in msvc...(no, I didn't
+ write it...see copyright) */
+/* Copyright (C) 1997 Makoto Matsumoto and Takuji Nishimura.
+ Any feedback is very welcome. For any question, comments,
+ see http://www.math.keio.ac.jp/matumoto/emt.html or email
+ matumoto@math.keio.ac.jp */
+/* Period parameters */
+#define N 624
+#define M 397
+#define MATRIX_A 0x9908b0df /* constant vector a */
+#define UPPER_MASK 0x80000000 /* most significant w-r bits */
+#define LOWER_MASK 0x7fffffff /* least significant r bits */
+
+/* Tempering parameters */
+#define TEMPERING_MASK_B 0x9d2c5680
+#define TEMPERING_MASK_C 0xefc60000
+#define TEMPERING_SHIFT_U(y) (y >> 11)
+#define TEMPERING_SHIFT_S(y) (y << 7)
+#define TEMPERING_SHIFT_T(y) (y << 15)
+#define TEMPERING_SHIFT_L(y) (y >> 18)
+
+static unsigned long mt[N]; /* the array for the state vector */
+static int mti=N+1; /* mti==N+1 means mt[N] is not initialized */
+
+/* initializing the array with a NONZERO seed */
+static void sgenrand(unsigned long seed) {
+ /* setting initial seeds to mt[N] using */
+ /* the generator Line 25 of Table 1 in */
+ /* [KNUTH 1981, The Art of Computer Programming */
+ /* Vol. 2 (2nd Ed.), pp102] */
+ mt[0]= seed & 0xffffffff;
+ for (mti=1; mti<N; mti++)
+ mt[mti] = (69069 * mt[mti-1]) & 0xffffffff;
+}
+
+u_int32_t Util::rand() {
+ unsigned long y;
+ static unsigned long mag01[2]={0x0, MATRIX_A};
+ /* mag01[x] = x * MATRIX_A for x=0,1 */
+
+ if (mti >= N) { /* generate N words at one time */
+ int kk;
+
+ if (mti == N+1) /* if sgenrand() has not been called, */
+ sgenrand(4357); /* a default initial seed is used */
+
+ for (kk=0;kk<N-M;kk++) {
+ y = (mt[kk]&UPPER_MASK)|(mt[kk+1]&LOWER_MASK);
+ mt[kk] = mt[kk+M] ^ (y >> 1) ^ mag01[y & 0x1];
+ }
+ for (;kk<N-1;kk++) {
+ y = (mt[kk]&UPPER_MASK)|(mt[kk+1]&LOWER_MASK);
+ mt[kk] = mt[kk+(M-N)] ^ (y >> 1) ^ mag01[y & 0x1];
+ }
+ y = (mt[N-1]&UPPER_MASK)|(mt[0]&LOWER_MASK);
+ mt[N-1] = mt[M-1] ^ (y >> 1) ^ mag01[y & 0x1];
+
+ mti = 0;
+ }
+
+ y = mt[mti++];
+ y ^= TEMPERING_SHIFT_U(y);
+ y ^= TEMPERING_SHIFT_S(y) & TEMPERING_MASK_B;
+ y ^= TEMPERING_SHIFT_T(y) & TEMPERING_MASK_C;
+ y ^= TEMPERING_SHIFT_L(y);
+
+ return y;
+}
+
+string Util::getOsVersion() {
+#ifdef _WIN32
+ string os;
+
+ OSVERSIONINFOEX ver;
+ memset(&ver, 0, sizeof(OSVERSIONINFOEX));
+ ver.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
+
+ if(!GetVersionEx((OSVERSIONINFO*)&ver)) {
+ ver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+ if(!GetVersionEx((OSVERSIONINFO*)&ver)) {
+ os = "Windows (version unknown)";
+ }
+ }
+
+ if(os.empty()) {
+ if(ver.dwPlatformId != VER_PLATFORM_WIN32_NT) {
+ os = "Win9x/ME/Junk";
+ } else if(ver.dwMajorVersion == 4) {
+ os = "WinNT4";
+ } else if(ver.dwMajorVersion == 5) {
+ if(ver.dwMinorVersion == 0) {
+ os = "Win2000";
+ } else if(ver.dwMinorVersion == 1) {
+ os = "WinXP";
+ } else if(ver.dwMinorVersion == 2) {
+ os = "Win2003";
+ } else {
+ os = "Unknown WinNT5";
+ }
+
+ if(ver.wProductType & VER_NT_WORKSTATION)
+ os += " Pro";
+ else if(ver.wProductType & VER_NT_SERVER)
+ os += " Server";
+ else if(ver.wProductType & VER_NT_DOMAIN_CONTROLLER)
+ os += " DC";
+ }
+
+ if(ver.wServicePackMajor != 0) {
+ os += "SP";
+ os += Util::toString(ver.wServicePackMajor);
+ if(ver.wServicePackMinor != 0) {
+ os += '.';
+ os += Util::toString(ver.wServicePackMinor);
+ }
+ }
+ }
+
+ return os;
+
+#else // _WIN32
+ utsname n;
+
+ if(uname(&n) != 0) {
+ return "unix (unknown version)";
+ }
+
+ return string(n.sysname) + " " + string(n.release) + " (" + string(n.machine) + ")";
+
+#endif // _WIN32
+}
+
+/* getIpCountry
+ This function returns the country(Abbreviation) of an ip
+ for exemple: it returns "PT", whitch standards for "Portugal"
+ more info: http://www.maxmind.com/app/csv
+*/
+string Util::getIpCountry (string IP) {
+ if (BOOLSETTING(GET_USER_COUNTRY)) {
+ dcassert(count(IP.begin(), IP.end(), '.') == 3);
+
+ //e.g IP 23.24.25.26 : w=23, x=24, y=25, z=26
+ string::size_type a = IP.find('.');
+ string::size_type b = IP.find('.', a+1);
+ string::size_type c = IP.find('.', b+2);
+
+ u_int32_t ipnum = (Util::toUInt32(IP.c_str()) << 24) |
+ (Util::toUInt32(IP.c_str() + a + 1) << 16) |
+ (Util::toUInt32(IP.c_str() + b + 1) << 8) |
+ (Util::toUInt32(IP.c_str() + c + 1) );
+
+ CountryIter i = countries.lower_bound(ipnum);
+
+ if(i != countries.end()) {
+ return string((char*)&(i->second), 2);
+ }
+ }
+
+ return Util::emptyString; //if doesn't returned anything already, something is wrong...
+}
+
+string Util::toDOS(const string& tmp) {
+ if(tmp.empty())
+ return Util::emptyString;
+
+ string tmp2(tmp);
+
+ if(tmp2[0] == '\r' && (tmp2.size() == 1 || tmp2[1] != '\n')) {
+ tmp2.insert(1, "\n");
+ }
+ for(string::size_type i = 1; i < tmp2.size() - 1; ++i) {
+ if(tmp2[i] == '\r' && tmp2[i+1] != '\n') {
+ // Mac ending
+ tmp2.insert(i+1, "\n");
+ i++;
+ } else if(tmp2[i] == '\n' && tmp2[i-1] != '\r') {
+ // Unix encoding
+ tmp2.insert(i, "\r");
+ i++;
+ }
+ }
+ return tmp2;
+}
+
+/**
+ * @file
+ * $Id: Util.cpp,v 1.2 2005/08/21 14:03:43 olof Exp $
+ */
+
diff --git a/dcpp/Util.h b/dcpp/Util.h
new file mode 100644
index 0000000..635c459
--- /dev/null
+++ b/dcpp/Util.h
@@ -0,0 +1,592 @@
+/*
+ * Copyright (C) 2001-2005 Jacek Sieka, arnetheduck on gmail point 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 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.
+ */
+
+#ifndef UTIL_H
+#define UTIL_H
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+#ifndef _WIN32
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <stdlib.h>
+#endif
+
+#include "Text.h"
+
+template<typename T, bool flag> struct ReferenceSelector {
+ typedef T ResultType;
+};
+template<typename T> struct ReferenceSelector<T,true> {
+ typedef const T& ResultType;
+};
+
+template<typename T> class IsOfClassType {
+public:
+ template<typename U> static char check(int U::*);
+ template<typename U> static float check(...);
+public:
+ enum { Result = sizeof(check<T>(0)) };
+};
+
+template<typename T> struct TypeTraits {
+ typedef IsOfClassType<T> ClassType;
+ typedef ReferenceSelector<T, ((ClassType::Result == 1) || (sizeof(T) > sizeof(char*)) ) > Selector;
+ typedef typename Selector::ResultType ParameterType;
+};
+
+#define GETSET(type, name, name2) \
+private: type name; \
+public: TypeTraits<type>::ParameterType get##name2() const { return name; } \
+ void set##name2(TypeTraits<type>::ParameterType a##name2) { name = a##name2; }
+
+#define LIT(x) x, (sizeof(x)-1)
+
+/** Evaluates op(pair<T1, T2>.first, compareTo) */
+template<class T1, class T2, class op = equal_to<T1> >
+class CompareFirst {
+public:
+ CompareFirst(const T1& compareTo) : a(compareTo) { };
+ bool operator()(const pair<T1, T2>& p) { return op()(p.first, a); };
+private:
+ CompareFirst& operator=(const CompareFirst&);
+ const T1& a;
+};
+
+/** Evaluates op(pair<T1, T2>.second, compareTo) */
+template<class T1, class T2, class op = equal_to<T2> >
+class CompareSecond {
+public:
+ CompareSecond(const T2& compareTo) : a(compareTo) { };
+ bool operator()(const pair<T1, T2>& p) { return op()(p.second, a); };
+private:
+ CompareSecond& operator=(const CompareSecond&);
+ const T2& a;
+};
+
+template<class T>
+struct PointerHash {
+#if _MSC_VER >= 1300
+ static const size_t bucket_size = 4;
+ static const size_t min_buckets = 8;
+#endif
+ size_t operator()(const T* a) const { return ((size_t)a)/sizeof(T); };
+ bool operator()(const T* a, const T* b) { return a < b; };
+};
+template<>
+struct PointerHash<void> {
+ size_t operator()(const void* a) const { return ((size_t)a)>>2; };
+};
+
+/**
+ * Compares two values
+ * @return -1 if v1 < v2, 0 if v1 == v2 and 1 if v1 > v2
+ */
+template<typename T1>
+inline int compare(const T1& v1, const T1& v2) { return (v1 < v2) ? -1 : ((v1 == v2) ? 0 : 1); }
+
+class Flags {
+ public:
+ typedef int MaskType;
+
+ Flags() : flags(0) { };
+ Flags(const Flags& rhs) : flags(rhs.flags) { };
+ Flags(MaskType f) : flags(f) { };
+ bool isSet(MaskType aFlag) const { return (flags & aFlag) == aFlag; };
+ bool isAnySet(MaskType aFlag) const { return (flags & aFlag) != 0; };
+ void setFlag(MaskType aFlag) { flags |= aFlag; };
+ void unsetFlag(MaskType aFlag) { flags &= ~aFlag; };
+ Flags& operator=(const Flags& rhs) { flags = rhs.flags; return *this; };
+ private:
+ MaskType flags;
+};
+
+template<typename T>
+class AutoArray {
+ typedef T* TPtr;
+public:
+ explicit AutoArray(TPtr t) : p(t) { };
+ explicit AutoArray(size_t size) : p(new T[size]) { };
+ ~AutoArray() { delete[] p; };
+ operator TPtr() { return p; };
+ AutoArray& operator=(TPtr t) { delete[] p; p = t; return *this; };
+private:
+ AutoArray(const AutoArray&);
+ AutoArray& operator=(const AutoArray&);
+
+ TPtr p;
+};
+
+class Util
+{
+public:
+ static tstring emptyStringT;
+ static string emptyString;
+ static wstring emptyStringW;
+
+ static void initialize();
+
+ /**
+ * Get the path to the application executable.
+ * This is mainly intended for use on Windows.
+ *
+ * @return Path to executable file.
+ */
+ static string getAppPath() { return appPath; }
+
+ static string getAppName() {
+#ifdef _WIN32
+ TCHAR buf[MAX_PATH+1];
+ DWORD x = GetModuleFileName(NULL, buf, MAX_PATH);
+ return Text::wideToUtf8(wstring(buf, x));
+#else // _WIN32
+ char buf[PATH_MAX + 1];
+ int n;
+ n = readlink("/proc/self/exe", buf, PATH_MAX);
+ if (n == -1) {
+ return emptyString;
+ }
+ buf[n] = '\0';
+ return string(buf);
+#endif // _WIN32
+ }
+
+ /**
+ * Get the path to where the applications settings.
+ *
+ * @return Path to settings directory.
+ */
+ static string getConfigPath();
+
+ /**
+ * Get the directory for temporary files.
+ *
+ * @return Path to temp directory.
+ */
+ static string getTempPath() {
+#ifdef _WIN32
+ TCHAR buf[MAX_PATH + 1];
+ DWORD x = GetTempPath(MAX_PATH, buf);
+ return Text::wideToUtf8(wstring(buf, x));
+#else
+ return "/tmp/";
+#endif
+ }
+
+ /**
+ * Get the directory to the application resources.
+ *
+ * @todo On non Windows system this still returns the path to the
+ * configuration directory. Later this will be completed with a patch for
+ * Mac OS X. And the Linux(?) implementation is also wrong right now.
+ * @return Path to resource directory.
+ */
+ static string getDataPath() {
+#ifdef _WIN32
+ return getAppPath();
+#else
+ char* home = getenv("HOME");
+ if (home) {
+ return string(home) + "/.dc++/";
+ }
+ return emptyString;
+#endif
+ }
+
+ static string translateError(int aError) {
+#ifdef _WIN32
+ LPVOID lpMsgBuf;
+ FormatMessage(
+ FORMAT_MESSAGE_ALLOCATE_BUFFER |
+ FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL,
+ aError,
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
+ (LPTSTR) &lpMsgBuf,
+ 0,
+ NULL
+ );
+ string tmp = Text::fromT((LPCTSTR)lpMsgBuf);
+ // Free the buffer.
+ LocalFree( lpMsgBuf );
+ string::size_type i = 0;
+
+ while( (i = tmp.find_first_of("\r\n", i)) != string::npos) {
+ tmp.erase(i, 1);
+ }
+ return tmp;
+#else // _WIN32
+ return strerror(aError);
+#endif // _WIN32
+ }
+
+ static string getFilePath(const string& path) {
+ string::size_type i = path.rfind(PATH_SEPARATOR);
+ return (i != string::npos) ? path.substr(0, i + 1) : path;
+ }
+ static string getFileName(const string& path) {
+ string::size_type i = path.rfind(PATH_SEPARATOR);
+ return (i != string::npos) ? path.substr(i + 1) : path;
+ }
+ static string getFileExt(const string& path) {
+ string::size_type i = path.rfind('.');
+ return (i != string::npos) ? path.substr(i) : Util::emptyString;
+ }
+ static string getLastDir(const string& path) {
+ string::size_type i = path.rfind(PATH_SEPARATOR);
+ if(i == string::npos)
+ return Util::emptyString;
+ string::size_type j = path.rfind(PATH_SEPARATOR, i-1);
+ return (j != string::npos) ? path.substr(j+1, i-j-1) : path;
+ }
+
+ static wstring getFilePath(const wstring& path) {
+ wstring::size_type i = path.rfind(PATH_SEPARATOR);
+ return (i != wstring::npos) ? path.substr(0, i + 1) : path;
+ }
+ static wstring getFileName(const wstring& path) {
+ wstring::size_type i = path.rfind(PATH_SEPARATOR);
+ return (i != wstring::npos) ? path.substr(i + 1) : path;
+ }
+ static wstring getFileExt(const wstring& path) {
+ wstring::size_type i = path.rfind('.');
+ return (i != wstring::npos) ? path.substr(i) : Util::emptyStringW;
+ }
+ static wstring getLastDir(const wstring& path) {
+ wstring::size_type i = path.rfind(PATH_SEPARATOR);
+ if(i == wstring::npos)
+ return Util::emptyStringW;
+ wstring::size_type j = path.rfind(PATH_SEPARATOR, i-1);
+ return (j != wstring::npos) ? path.substr(j+1, i-j-1) : path;
+ }
+
+ static void decodeUrl(const string& aUrl, string& aServer, u_int16_t& aPort, string& aFile);
+ static string validateFileName(string aFile);
+
+ static string formatBytes(const string& aString) {
+ return formatBytes(toInt64(aString));
+ }
+
+ static string toDOS(const string& tmp);
+
+ static string getShortTimeString();
+
+ static string getTimeString() {
+ char buf[64];
+ time_t _tt;
+ time(&_tt);
+ tm* _tm = localtime(&_tt);
+ if(_tm == NULL) {
+ strcpy(buf, "xx:xx:xx");
+ } else {
+ strftime(buf, 64, "%X", _tm);
+ }
+ return buf;
+ }
+
+ static string toAdcFile(const string& file) {
+ if(file == "files.xml.bz2" || file == "MyList.DcLst")
+ return file;
+
+ string ret;
+ ret.reserve(file.length() + 1);
+ ret += '/';
+ ret += file;
+ for(string::size_type i = 0; i < ret.length(); ++i) {
+ if(ret[i] == '\\') {
+ ret[i] = '/';
+ }
+ }
+ return ret;
+ }
+ static string toNmdcFile(const string& file) {
+ if(file.empty())
+ return Util::emptyString;
+
+ string ret(file.substr(1));
+ for(string::size_type i = 0; i < ret.length(); ++i) {
+ if(ret[i] == '/') {
+ ret[i] = '\\';
+ }
+ }
+ return ret;
+ }
+
+ static string formatBytes(int64_t aBytes);
+
+ static string formatExactSize(int64_t aBytes);
+
+ static string formatSeconds(int64_t aSec) {
+ char buf[64];
+ sprintf(buf, "%01lu:%02d:%02d", (unsigned long)(aSec / (60*60)), (int)((aSec / 60) % 60), (int)(aSec % 60));
+ return buf;
+ }
+
+ static string formatParams(const string& msg, StringMap& params);
+ static string formatTime(const string &msg, const time_t t);
+
+ static int64_t toInt64(const string& aString) {
+#ifdef _WIN32
+ return _atoi64(aString.c_str());
+#else
+ return atoll(aString.c_str());
+#endif
+ }
+
+ static int toInt(const string& aString) {
+ return atoi(aString.c_str());
+ }
+ static u_int32_t toUInt32(const string& str) {
+ return toUInt32(str.c_str());
+ }
+ static u_int32_t toUInt32(const char* c) {
+ return (u_int32_t)atoi(c);
+ }
+
+ static double toDouble(const string& aString) {
+ // Work-around for atof and locales...
+ lconv* lv = localeconv();
+ string::size_type i = aString.find_last_of(".,");
+ if(i != string::npos && aString[i] != lv->decimal_point[0]) {
+ string tmp(aString);
+ tmp[i] = lv->decimal_point[0];
+ return atof(tmp.c_str());
+ }
+ return atof(aString.c_str());
+ }
+
+ static float toFloat(const string& aString) {
+ return (float)toDouble(aString.c_str());
+ }
+
+ static string toString(short val) {
+ char buf[8];
+ sprintf(buf, "%d", (int)val);
+ return buf;
+ }
+ static string toString(unsigned short val) {
+ char buf[8];
+ sprintf(buf, "%u", (unsigned int)val);
+ return buf;
+ }
+ static string toString(int val) {
+ char buf[16];
+ sprintf(buf, "%d", val);
+ return buf;
+ }
+ static string toString(unsigned int val) {
+ char buf[16];
+ sprintf(buf, "%u", val);
+ return buf;
+ }
+ static string toString(long val) {
+ char buf[32];
+ sprintf(buf, "%ld", val);
+ return buf;
+ }
+ static string toString(unsigned long val) {
+ char buf[32];
+ sprintf(buf, "%lu", val);
+ return buf;
+ }
+ static string toString(long long val) {
+ char buf[32];
+#ifdef _MSC_VER
+ sprintf(buf, "%I64d", val);
+#else
+ sprintf(buf, "%lld", val);
+#endif
+ return buf;
+ }
+ static string toString(unsigned long long val) {
+ char buf[32];
+#ifdef _MSC_VER
+ sprintf(buf, "%I64u", val);
+#else
+ sprintf(buf, "%llu", val);
+#endif
+ return buf;
+ }
+ static string toString(double val) {
+ char buf[16];
+ sprintf(buf, "%0.2f", val);
+ return buf;
+ }
+ static string toHexEscape(char val) {
+ char buf[sizeof(int)*2+1+1];
+ sprintf(buf, "%%%X", val&0x0FF);
+ return buf;
+ }
+ static char fromHexEscape(const string aString) {
+ unsigned int res = 0;
+ sscanf(aString.c_str(), "%X", &res);
+ return static_cast<char>(res);
+ }
+
+ static string encodeURI(const string& /*aString*/, bool reverse = false);
+ static string getLocalIp();
+ static bool isPrivateIp(string const& ip);
+ /**
+ * Case insensitive substring search.
+ * @return First position found or string::npos
+ */
+ static string::size_type findSubString(const string& aString, const string& aSubString, string::size_type start = 0) throw();
+
+ /* Utf-8 versions of strnicmp and stricmp, unicode char code order (!) */
+ static int stricmp(const char* a, const char* b);
+ static int strnicmp(const char* a, const char* b, size_t n);
+
+ static int stricmp(const wchar_t* a, const wchar_t* b) {
+ while(*a && Text::toLower(*a) == Text::toLower(*b))
+ ++a, ++b;
+ return ((int)Text::toLower(*a)) - ((int)Text::toLower(*b));
+ }
+ static int strnicmp(const wchar_t* a, const wchar_t* b, size_t n) {
+ while(n && *a && Text::toLower(*a) == Text::toLower(*b))
+ --n, ++a, ++b;
+
+ return n == 0 ? 0 : ((int)Text::toLower(*a)) - ((int)Text::toLower(*b));
+ }
+
+ static int stricmp(const string& a, const string& b) { return stricmp(a.c_str(), b.c_str()); };
+ static int strnicmp(const string& a, const string& b, size_t n) { return strnicmp(a.c_str(), b.c_str(), n); };
+ static int stricmp(const wstring& a, const wstring& b) { return stricmp(a.c_str(), b.c_str()); };
+ static int strnicmp(const wstring& a, const wstring& b, size_t n) { return strnicmp(a.c_str(), b.c_str(), n); };
+
+ static string validateMessage(string tmp, bool reverse, bool checkNewLines = true);
+
+ static string getOsVersion();
+
+ static string getIpCountry (string IP);
+
+ static bool getAway() { return away; };
+ static void setAway(bool aAway) {
+ away = aAway;
+ if (away)
+ awayTime = time(NULL);
+ };
+ static string getAwayMessage();
+
+ static void setAwayMessage(const string& aMsg) { awayMsg = aMsg; };
+
+ static u_int32_t rand();
+ static u_int32_t rand(u_int32_t high) { return rand() % high; };
+ static u_int32_t rand(u_int32_t low, u_int32_t high) { return rand(high-low) + low; };
+ static double randd() { return ((double)rand()) / ((double)0xffffffff); };
+
+private:
+ static string appPath;
+ static string dataPath;
+ static bool away;
+ static string awayMsg;
+ static time_t awayTime;
+
+ typedef map<u_int32_t, u_int16_t> CountryList;
+ typedef CountryList::iterator CountryIter;
+
+ static CountryList countries;
+
+};
+
+/** Case insensitive hash function for strings */
+struct noCaseStringHash {
+#if _MSC_VER < 1300
+ enum {bucket_size = 4};
+ enum {min_buckets = 8};
+#else
+ static const size_t bucket_size = 4;
+ static const size_t min_buckets = 8;
+#endif // _MSC_VER == 1200
+
+ size_t operator()(const string* s) const {
+ return operator()(*s);
+ }
+
+ size_t operator()(const string& s) const {
+ size_t x = 0;
+ const char* end = s.data() + s.size();
+ for(const char* str = s.data(); str < end; ) {
+ wchar_t c = 0;
+ int n = Text::utf8ToWc(str, c);
+ if(n < 0) {
+ x = x*32 - x + '_';
+ str += abs(n);
+ } else {
+ x = x*32 - x + (size_t)Text::toLower(c);
+ str += n;
+ }
+ }
+ return x;
+ }
+
+ size_t operator()(const wstring* s) const {
+ return operator()(*s);
+ }
+ size_t operator()(const wstring& s) const {
+ size_t x = 0;
+ const wchar_t* y = s.data();
+ wstring::size_type j = s.size();
+ for(wstring::size_type i = 0; i < j; ++i) {
+ x = x*31 + (size_t)Text::toLower(y[i]);
+ }
+ return x;
+ }
+};
+
+/** Case insensitive string comparison */
+struct noCaseStringEq {
+ bool operator()(const string* a, const string* b) const {
+ return a == b || Util::stricmp(*a, *b) == 0;
+ }
+ bool operator()(const string& a, const string& b) const {
+ return Util::stricmp(a, b) == 0;
+ }
+ bool operator()(const wstring* a, const wstring* b) const {
+ return a == b || Util::stricmp(*a, *b) == 0;
+ }
+ bool operator()(const wstring& a, const wstring& b) const {
+ return Util::stricmp(a, b) == 0;
+ }
+};
+
+/** Case insensitive string ordering */
+struct noCaseStringLess {
+ bool operator()(const string* a, const string* b) const {
+ return Util::stricmp(*a, *b) < 0;
+ }
+ bool operator()(const string& a, const string& b) const {
+ return Util::stricmp(a, b) < 0;
+ }
+ bool operator()(const wstring* a, const wstring* b) const {
+ return Util::stricmp(*a, *b) < 0;
+ }
+ bool operator()(const wstring& a, const wstring& b) const {
+ return Util::stricmp(a, b) < 0;
+ }
+};
+
+#endif // UTIL_H
+
+/**
+ * @file
+ * $Id: Util.h,v 1.2 2005/08/21 14:03:43 olof Exp $
+ */
diff --git a/dcpp/ZUtils.cpp b/dcpp/ZUtils.cpp
new file mode 100644
index 0000000..d84c7c7
--- /dev/null
+++ b/dcpp/ZUtils.cpp
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2001-2005 Jacek Sieka, arnetheduck on gmail point 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 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.
+ */
+
+#include "stdinc.h"
+#include "DCPlusPlus.h"
+
+#include "ZUtils.h"
+#include "Exception.h"
+#include "ResourceManager.h"
+
+const double ZFilter::MIN_COMPRESSION_LEVEL = 0.9;
+
+ZFilter::ZFilter() : totalIn(0), totalOut(0), compressing(true) {
+ memset(&zs, 0, sizeof(zs));
+
+ if(deflateInit(&zs, 3) != Z_OK) {
+ throw Exception(STRING(COMPRESSION_ERROR));
+ }
+}
+
+ZFilter::~ZFilter() {
+ dcdebug("ZFilter end, %ld/%ld = %.04f\n", zs.total_out, zs.total_in, (float)zs.total_out / max((float)zs.total_in, (float)1));
+ deflateEnd(&zs);
+}
+
+bool ZFilter::operator()(const void* in, size_t& insize, void* out, size_t& outsize) {
+ if(outsize == 0)
+ return 0;
+
+ zs.avail_in = insize;
+ zs.next_in = (Bytef*)in;
+ zs.avail_out = outsize;
+ zs.next_out = (Bytef*)out;
+
+ if(insize == 0) {
+ int err = ::deflate(&zs, Z_FINISH);
+ if(err != Z_OK && err != Z_STREAM_END)
+ throw Exception(STRING(COMPRESSION_ERROR));
+
+ outsize = outsize - zs.avail_out;
+ insize = insize - zs.avail_in;
+ totalOut += outsize;
+ totalIn += insize;
+ return err == Z_OK;
+ } else {
+ int err = ::deflate(&zs, Z_NO_FLUSH);
+ if(err != Z_OK)
+ throw Exception(STRING(COMPRESSION_ERROR));
+
+ outsize = outsize - zs.avail_out;
+ insize = insize - zs.avail_in;
+ totalOut += outsize;
+ totalIn += insize;
+ return true;
+ }
+}
+
+UnZFilter::UnZFilter() {
+ memset(&zs, 0, sizeof(zs));
+
+ if(inflateInit(&zs) != Z_OK)
+ throw Exception(STRING(DECOMPRESSION_ERROR));
+
+}
+
+UnZFilter::~UnZFilter() {
+ dcdebug("UnZFilter end, %ld/%ld = %.04f\n", zs.total_out, zs.total_in, (float)zs.total_out / max((float)zs.total_in, (float)1));
+ inflateEnd(&zs);
+}
+
+bool UnZFilter::operator()(const void* in, size_t& insize, void* out, size_t& outsize) {
+ if(outsize == 0)
+ return 0;
+
+ zs.avail_in = insize;
+ zs.next_in = (Bytef*)in;
+ zs.avail_out = outsize;
+ zs.next_out = (Bytef*)out;
+
+ int err = ::inflate(&zs, Z_NO_FLUSH);
+
+ // see zlib/contrib/minizip/unzip.c, Z_BUF_ERROR means we should have padded
+ // with a dummy byte if at end of stream - since we don't do this it's not a real
+ // error
+ if(!(err == Z_OK || err == Z_STREAM_END || (err == Z_BUF_ERROR && in == NULL)))
+ throw Exception(STRING(DECOMPRESSION_ERROR));
+
+ outsize = outsize - zs.avail_out;
+ insize = insize - zs.avail_in;
+ return err == Z_OK;
+}
+
+/**
+ * @file
+ * $Id: ZUtils.cpp,v 1.2 2005/08/21 14:03:43 olof Exp $
+ */
diff --git a/dcpp/ZUtils.h b/dcpp/ZUtils.h
new file mode 100644
index 0000000..62128e3
--- /dev/null
+++ b/dcpp/ZUtils.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2001-2005 Jacek Sieka, arnetheduck on gmail point 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 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.
+ */
+
+#ifndef _Z_UTILS
+#define _Z_UTILS
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+#ifdef _WIN32
+#include "../zlib/zlib.h"
+#else
+#include <zlib.h>
+#endif
+
+class ZFilter {
+public:
+ /** Compression will automatically be turned off if below this... */
+ static const double MIN_COMPRESSION_LEVEL;
+
+ ZFilter();
+ ~ZFilter();
+ /**
+ * Compress data.
+ * @param in Input data
+ * @param insize Input size (Set to 0 to indicate that no more data will follow)
+ * @param out Output buffer
+ * @param outsize Output size, set to compressed size on return.
+ * @return True if there's more processing to be done
+ */
+ bool operator()(const void* in, size_t& insize, void* out, size_t& outsize);
+private:
+ z_stream zs;
+ int64_t totalIn;
+ int64_t totalOut;
+ bool compressing;
+};
+
+class UnZFilter {
+public:
+ UnZFilter();
+ ~UnZFilter();
+ /**
+ * Decompress data.
+ * @param in Input data
+ * @param insize Input size (Set to 0 to indicate that no more data will follow)
+ * @param out Output buffer
+ * @param outsize Output size, set to decompressed size on return.
+ * @return True if there's more processing to be done
+ */
+ bool operator()(const void* in, size_t& insize, void* out, size_t& outsize);
+private:
+ z_stream zs;
+};
+
+class CRC32Filter {
+public:
+ CRC32Filter() : crc(crc32(0, NULL, 0)) { }
+ // TODO 64-bits?
+ void operator()(const void* buf, size_t len) { crc = crc32(crc, (const Bytef*)buf, (uInt)len); }
+ u_int32_t getValue() const { return crc; }
+private:
+ u_int32_t crc;
+};
+
+#endif // _Z_UTILS
+
+/**
+ * @file
+ * $Id: ZUtils.h,v 1.2 2005/08/21 14:03:43 olof Exp $
+ */
diff --git a/dcpp/atomic-ppc.h b/dcpp/atomic-ppc.h
new file mode 100644
index 0000000..91c6e2b
--- /dev/null
+++ b/dcpp/atomic-ppc.h
@@ -0,0 +1,94 @@
+#ifndef POWERPC_ATOMIC_H
+#define POWERPC_ATOMIC_H
+
+#ifdef __64BIT__
+# define LPARX "ldarx"
+# define CMPP "cmpd"
+# define STPCX "stdcx."
+#else
+# define LPARX "lwarx"
+# define CMPP "cmpw"
+# define STPCX "stwcx."
+#endif
+
+typedef struct _atomic_t
+{ int counter; } atomic_t;
+
+inline atomic_t ATOMIC_INIT(volatile long &v)
+{
+ atomic_t tmp;
+ tmp.counter = (int)v;
+ return tmp;
+}
+
+
+inline int atomic_test_and_set_int(volatile int *ptr, int expected, int newval)
+{
+ register int tmp;
+ register int ret;
+ asm volatile("lwarx %0,0,%2\n"
+ "cmpw %0,%3\n"
+ "bne- $+20\n"
+ "stwcx. %4,0,%2\n"
+ "bne- $-16\n"
+ "li %1,1\n"
+ "b $+8\n"
+ "li %1,0\n"
+ : "=&r" (tmp), "=&r" (ret)
+ : "r" (ptr), "r" (expected), "r" (newval)
+ : "cc", "memory");
+ return ret;
+}
+
+
+inline int atomic_test_and_set_ptr(volatile void *ptr, void *expected, void *newval)
+{
+ register void *tmp;
+ register int ret;
+ asm volatile(LPARX" %0,0,%2\n"
+ CMPP" %0,%3\n"
+ "bne- $+20\n"
+ STPCX" %4,0,%2\n"
+ "bne- $-16\n"
+ "li %1,1\n"
+ "b $+8\n"
+ "li %1,0\n"
+ : "=&r" (tmp), "=&r" (ret)
+ : "r" (ptr), "r" (expected), "r" (newval)
+ : "cc", "memory");
+ return ret;
+}
+
+inline int atomic_inc(volatile atomic_t *ptr)
+{
+ register int ret;
+ register int one = 1;
+ asm volatile("lwarx %0, 0, %1\n"
+ "add %0, %2, %0\n"
+ "stwcx. %0, 0, %1\n"
+ "bne- $-12\n"
+ : "=&r" (ret)
+ : "r" (ptr), "r" (one)
+ : "cc", "memory");
+ return ret;
+}
+
+inline int atomic_dec(volatile atomic_t *ptr)
+{
+ register int ret;
+ register int one = -1;
+ asm volatile("lwarx %0, 0, %1\n"
+ "add %0, %2, %0\n"
+ "stwcx. %0, 0, %1\n"
+ "bne- $-12\n"
+ : "=&r" (ret)
+ : "r" (ptr), "r" (one)
+ : "cc", "memory");
+ return ret;
+}
+
+#undef LPARX
+#undef CMPP
+#undef STPCX
+
+#endif
diff --git a/dcpp/atomic-x86_64.h b/dcpp/atomic-x86_64.h
new file mode 100644
index 0000000..1e3211d
--- /dev/null
+++ b/dcpp/atomic-x86_64.h
@@ -0,0 +1,39 @@
+#ifndef X86_64_ATOMIC_H
+#define X86_64_ATOMIC_H
+
+typedef struct _atomic_t
+{
+ int counter;
+} atomic_t;
+
+inline atomic_t ATOMIC_INIT(volatile long &v)
+{
+ atomic_t tmp;
+ tmp.counter = (int)v;
+ return tmp;
+}
+
+inline int atomic_inc(volatile atomic_t *ptr)
+{
+ unsigned char ret;
+ asm volatile("lock incl %0\n"
+ "setne %1"
+ : "=m" (ptr->counter), "=qm" (ret)
+ : "m" (ptr->counter)
+ : "memory");
+ return static_cast<int>(ret);
+}
+
+inline int atomic_dec(volatile atomic_t *ptr)
+{
+ unsigned char ret;
+ asm volatile("lock decl %0\n"
+ "setne %1"
+ : "=m" (ptr->counter), "=qm" (ret)
+ : "m" (ptr->counter)
+ : "memory");
+ return static_cast<int>(ret);
+}
+
+
+#endif
diff --git a/dcpp/atomic.h b/dcpp/atomic.h
new file mode 100644
index 0000000..423d41e
--- /dev/null
+++ b/dcpp/atomic.h
@@ -0,0 +1,56 @@
+#ifndef I386_ATOMIC_H
+#define I386_ATOMIC_H
+
+typedef struct _atomic_t
+{
+ int counter;
+} atomic_t;
+
+inline atomic_t ATOMIC_INIT(volatile long &v)
+{
+ atomic_t tmp;
+ tmp.counter = (int)v;
+ return tmp;
+}
+
+inline int atomic_inc(volatile atomic_t *ptr)
+{
+ unsigned char ret;
+ asm volatile("lock incl %0\n"
+ "setne %1"
+ : "=m" (ptr->counter), "=qm" (ret)
+ : "m" (ptr->counter)
+ : "memory");
+ return static_cast<int>(ret);
+}
+
+inline int atomic_dec(volatile atomic_t *ptr)
+{
+ unsigned char ret;
+ asm volatile("lock decl %0\n"
+ "setne %1"
+ : "=m" (ptr->counter), "=qm" (ret)
+ : "m" (ptr->counter)
+ : "memory");
+ return static_cast<int>(ret);
+}
+
+inline int atomic_test_and_set_int(volatile int *ptr, int expected, int newval)
+{
+ unsigned char ret;
+ asm volatile("lock cmpxchgl %2,%3\n"
+ "sete %1\n"
+ : "=a" (newval), "=qm" (ret)
+ : "r" (newval), "m" (*ptr), "0" (expected)
+ : "memory");
+ return static_cast<int>(ret);
+}
+
+inline int atomic_test_and_set_ptr(volatile void *ptr, void *expected, void *newval)
+{
+ return atomic_test_and_set_int(reinterpret_cast<volatile int *>(ptr),
+ reinterpret_cast<int>(expected),
+ reinterpret_cast<int>(newval));
+}
+
+#endif
diff --git a/dcpp/config.h b/dcpp/config.h
new file mode 100644
index 0000000..624c162
--- /dev/null
+++ b/dcpp/config.h
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2001-2005 Jacek Sieka, arnetheduck on gmail point 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 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.
+ */
+
+#ifndef CONFIG_H
+#define CONFIG_H
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+#ifdef HAVE_CONFIG_H
+#include "autoconf.h"
+#endif
+
+// Changing this number will change the maximum number of simultaneous users
+// we can handle (when using select)...
+//#define FD_SETSIZE 4096
+
+// Remove this line if hashes are not available in your stl
+#define HAVE_HASH 1
+
+// This enables stlport's debug mode (and slows it down to a crawl...)
+//# define _STLP_DEBUG 1
+
+// --- Shouldn't have to change anything under here...
+
+#ifndef _REENTRANT
+# define _REENTRANT 1
+#endif
+
+#ifdef HAVE_STLPORT
+# ifndef _DEBUG
+# define _STLP_NO_EXCEPTIONS 1
+# endif
+#endif
+
+#ifdef _MSC_VER
+# pragma warning(disable: 4711) // function 'xxx' selected for automatic inline expansion
+# pragma warning(disable: 4786) // identifier was truncated to '255' characters in the debug information
+# pragma warning(disable: 4290) // C++ Exception Specification ignored
+# pragma warning(disable: 4127) // constant expression
+# pragma warning(disable: 4710) // function not inlined
+
+# if _MSC_VER == 1200 || _MSC_VER == 1300 || _MSC_VER == 1310
+
+typedef signed char int8_t;
+typedef signed short int16_t;
+typedef signed long int32_t;
+typedef signed __int64 int64_t;
+
+typedef unsigned char u_int8_t;
+typedef unsigned short u_int16_t;
+typedef unsigned long u_int32_t;
+typedef unsigned __int64 u_int64_t;
+
+# endif
+
+#endif
+
+#if defined(_MSC_VER)
+#define _LL(x) x##ll
+#define _ULL(x) x##ull
+#define I64_FMT "%I64d"
+#elif defined(SIZEOF_LONG) && SIZEOF_LONG == 8
+#define _LL(x) x##l
+#define _ULL(x) x##ul
+#define I64_FMT "%ld"
+#else
+#define _LL(x) x##ll
+#define _ULL(x) x##ull
+#define I64_FMT "%lld"
+#endif
+
+#ifdef _WIN32
+
+# define PATH_SEPARATOR '\\'
+# define PATH_SEPARATOR_STR "\\"
+
+#else
+
+# define PATH_SEPARATOR '/'
+# define PATH_SEPARATOR_STR "/"
+
+#endif
+
+#ifdef _MSC_VER
+
+# ifndef CDECL
+# define CDECL _cdecl
+# endif
+
+#else // _MSC_VER
+
+# ifndef CDECL
+# define CDECL
+# endif
+
+#endif // _MSC_VER
+
+#define BZ_NO_STDIO
+
+#ifdef _WIN32
+# define _WIN32_WINNT 0x0501
+# define _WIN32_IE 0x0500
+#endif
+
+#endif // CONFIG_H
+
+/**
+ * @file
+ * $Id: config.h,v 1.3 2005/09/26 18:23:22 olof Exp $
+ */
diff --git a/dcpp/stdinc.cpp b/dcpp/stdinc.cpp
new file mode 100644
index 0000000..356c3a3
--- /dev/null
+++ b/dcpp/stdinc.cpp
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2001-2005 Jacek Sieka, arnetheduck on gmail point 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 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.
+ */
+
+#include "stdinc.h"
+
+#ifdef HAS_STLPORT
+
+#if (_STLPORT_VERSION != 0x462)
+#error STLPort not correctly installed, read compile.txt
+#endif
+
+#ifndef _STLP_NO_IOSTREAMS
+#error You're not using the STLPort from the DC++ homepage, that uses a different configuration than the original one. Remove this line only if you know what you're doing.
+#endif
+
+#endif // HAS_STLPORT
+
+/**
+ * @file
+ * $Id: stdinc.cpp,v 1.2 2005/08/21 14:03:43 olof Exp $
+ */
diff --git a/dcpp/stdinc.h b/dcpp/stdinc.h
new file mode 100644
index 0000000..0b47d53
--- /dev/null
+++ b/dcpp/stdinc.h
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2001-2005 Jacek Sieka, arnetheduck on gmail point 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 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.
+ */
+
+#ifndef STDINC_H
+#define STDINC_H
+
+#include "config.h"
+
+#ifdef _WIN32
+
+#define STRICT
+#define WIN32_LEAN_AND_MEAN
+#define _WTL_NO_CSTRING
+#define _ATL_NO_OPENGL
+#define _ATL_NO_MSIMG
+#define _ATL_NO_COM
+#define _ATL_NO_HOSTING
+#define _ATL_NO_OLD_NAMES
+
+#include <Winsock2.h>
+
+#include <windows.h>
+#include <crtdbg.h>
+#include <tchar.h>
+
+#else
+#include <unistd.h>
+#endif
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <memory.h>
+#include <sys/types.h>
+#include <time.h>
+#include <locale.h>
+
+#include <algorithm>
+#include <vector>
+#include <string>
+#include <map>
+#include <set>
+#include <deque>
+#include <list>
+#include <utility>
+
+// Use maps if hash_maps aren't available
+#ifdef HAVE_HASH
+# ifdef HAVE_STLPORT
+# define HASH_MAP_X(key, type, hfunc, eq, order) hash_map<key, type, hfunc, eq >
+# define HASH_MULTIMAP_X(key, type, hfunc, eq, order) hash_multimap<key, type, hfunc, eq >
+# elif defined(__GLIBCPP__) || defined(__GLIBCXX__) // Using GNU C++ library?
+# define HASH_MAP_X(key, type, hfunc, eq, order) hash_map<key, type, hfunc, eq >
+# define HASH_MULTIMAP_X(key, type, hfunc, eq, order) hash_multimap<key, type, hfunc, eq >
+# elif defined(_MSC_VER) // Assume the msvc 7.x stl
+# define HASH_MAP_X(key, type, hfunc, eq, order) hash_map<key, type, hfunc >
+# define HASH_MULTIMAP_X(key, type, hfunc, eq, order) hash_multimap<key, type, hfunc >
+# else
+# error Unknown STL, hashes need to be configured
+# endif
+
+# define HASH_SET hash_set
+# define HASH_MAP hash_map
+# define HASH_MULTIMAP hash_multimap
+
+#else // HAVE_HASH
+
+# define HASH_SET set
+# define HASH_MAP map
+# define HASH_MAP_X(key, type, hfunc, eq, order) map<key, type, order >
+# define HASH_MULTIMAP multimap
+# define HASH_MULTIMAP_X(key, type, hfunc, eq, order) multimap<key, type, order >
+
+#endif // HAVE_HASH
+
+
+#ifdef HAVE_STLPORT
+using namespace _STL;
+#include <hash_map>
+
+#elif defined(__GLIBCPP__) || defined(__GLIBCXX__) // Using GNU C++ library?
+#include <ext/hash_map>
+
+using namespace std;
+using namespace __gnu_cxx;
+
+// GNU C++ library doesn't have hash(std::string) or hash(long long int)
+namespace __gnu_cxx {
+ template<> struct hash<std::string> {
+ size_t operator()(const std::string& x) const
+ { return hash<const char*>()(x.c_str()); }
+ };
+ template<> struct hash<long long int> {
+ size_t operator()(long long int x) const { return x; }
+ };
+}
+#else // __GLIBCPP__
+
+#include <hash_map>
+using namespace std;
+using namespace stdext;
+
+#endif // __GLIBCPP__
+
+#endif // STDINC_H
+
+/**
+ * @file
+ * $Id: stdinc.h,v 1.2 2005/08/21 14:03:43 olof Exp $
+ */
diff --git a/dcpp/version.h b/dcpp/version.h
new file mode 100644
index 0000000..836b212
--- /dev/null
+++ b/dcpp/version.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2001-2005 Jacek Sieka, arnetheduck on gmail point 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 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.
+ */
+
+#define APPNAME "DC++"
+#define VERSIONSTRING "0.674"
+#define VERSIONFLOAT 0.674
+
+/* Update the .rc file as well... */
+
+/**
+ * @file
+ * $Id: version.h,v 1.2 2005/08/21 14:03:43 olof Exp $
+ */
diff --git a/debian/changelog b/debian/changelog
new file mode 100644
index 0000000..0f0608a
--- /dev/null
+++ b/debian/changelog
@@ -0,0 +1,58 @@
+dc-qt (0.2.0.alpha-4.2) unstable; urgency=low
+
+ * Non-maintainer upload.
+ * Update boost-linking patch, to link with boost1.53 (Closes: #709254)
+
+ -- Dmitrijs Ledkovs <dmitrij.ledkov@ubuntu.com> Fri, 05 Jul 2013 23:23:01 +0100
+
+dc-qt (0.2.0.alpha-4.1) unstable; urgency=low
+
+ * Non-maintainer upload.
+ * Fix build with gcc-4.4 using patch from Martin Michlmayr (Closes:
+ 504956)
+
+ -- Christoph Egger <christoph@debian.org> Fri, 25 Dec 2009 14:30:32 +0100
+
+dc-qt (0.2.0.alpha-4) unstable; urgency=medium
+
+ * Acknowledge NMU (Thanks to Moritz)
+ * Add build-dependency against zlib1g-dev to avoid FTBFS
+ (Closes: #477028) (Thanks to Sune for the patch and Sebastian
+ for the patch)
+ * Bump standards version to 3.7.3 (no changes needed)
+ * Bump debhelper level to 6 (no changes needed)
+ * Remove obsolete directory /usr/sbin in the package
+ * Now use Homepage field in debian/control
+ * Change Apps/Net to Applications/Network/File Transfer in debian/menu
+ according to new policy
+
+ -- Steffen Joeris <white@debian.org> Tue, 06 May 2008 11:45:30 +0000
+
+dc-qt (0.2.0.alpha-3.1) unstable; urgency=low
+
+ * Non-maintainer upload.
+ * Fix GCC 4.3 compatibility, patch by Cyril Boulebois (Closes: #455636)
+
+ -- Moritz Muehlenhoff <jmm@debian.org> Wed, 19 Mar 2008 21:18:04 +0100
+
+dc-qt (0.2.0.alpha-3) unstable; urgency=low
+
+ * Fix FTBFS with newer boost libraries (Closes: #425074)
+ Thanks to William Grant
+
+ -- Steffen Joeris <white@debian.org> Sat, 19 May 2007 22:49:34 +1000
+
+dc-qt (0.2.0.alpha-2) unstable; urgency=low
+
+ * Include 20-gcc-fix.patch to avoid FTBFS with newer gcc version
+ (Closes: #417495) Thanks to Martin Michlmayr
+ * Change my maintainer address
+
+ -- Steffen Joeris <white@debian.org> Fri, 06 Apr 2007 12:57:59 +1000
+
+dc-qt (0.2.0.alpha-1) unstable; urgency=low
+
+ * Initial release (Closes: #382349)
+
+ -- Steffen Joeris <steffen.joeris@skolelinux.de> Fri, 11 Aug 2006 13:50:52 +1000
+
diff --git a/debian/compat b/debian/compat
new file mode 100644
index 0000000..1e8b314
--- /dev/null
+++ b/debian/compat
@@ -0,0 +1 @@
+6
diff --git a/debian/control b/debian/control
new file mode 100644
index 0000000..8f6569a
--- /dev/null
+++ b/debian/control
@@ -0,0 +1,24 @@
+Source: dc-qt
+Section: kde
+Priority: optional
+Maintainer: Steffen Joeris <white@debian.org>
+Build-Depends: cdbs, debhelper (>= 5), libqt4-dev, libboost-dev, libboost-filesystem-dev, libboost-thread-dev, scons, libboost-program-options-dev, libbz2-dev, zlib1g-dev
+Standards-Version: 3.7.3
+Homepage: http://dc-qt.sourceforge.net
+
+Package: dc-qt
+Architecture: any
+Depends: ${shlibs:Depends}, ${misc:Depends}
+Description: GUI frontend for the dc protocol
+ dc-qt is a qt front-end for the dctc program. dctc handles all
+ communication with dc hubs and clients, while dc-qt presents an
+ interface that has many of the features of the original directconnect
+ client, plus some really useful improvements.
+ .
+ It is intended for peer-based file-sharing. In practise it works better than
+ gnutella and other similar systems as it allows dc hubs (servers)
+ administators to require clients to share specified amount of data. The
+ amount is usually based on type of client's connection and it is used not
+ to hurt or exclude anybody but to make file sharing "fair play".
+ .
+ dc-qt is still alpha, so some care has to be taken - try it out!
diff --git a/debian/copyright b/debian/copyright
new file mode 100644
index 0000000..dc7af56
--- /dev/null
+++ b/debian/copyright
@@ -0,0 +1,31 @@
+This package was debianized by Steffen Joeris <steffen.joeris@skolelinux.de> on
+Fri, 11 Aug 2006 13:50:52 +1000.
+
+It was downloaded from http://dc-qt.sourceforge.net
+
+Upstream Authors: Rikard Björklind (olof@users.sourceforge.net)
+ Mikael Gransell (mickeg@users.sourceforge.net)
+ Arsenij Vodjanov (arsenij@gmail.com)
+
+
+Copyright: (C) 2001-2005 Jacek Sieka, arnetheduck on gmail point com
+ j_s@telia.com
+ (C) 1997 Makoto Matsumoto and Takuji Nishimura
+
+License:
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License, version 2
+as published by the Free Software Foundation.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANDABILITY of FITNESS FOR A PARTICULAR PURPOSE.
+See the GNU General Public License for details.
+
+On Debian GNU/Linux systems, the complete text of the GNU General
+Public License can be found in /usr/share/common-licenses/GPL.
+
+
+The Debian packaging is (C) 2006, Steffen Joeris <steffen.joeris@skolelinux.de> and
+is licensed under the GPL, see `/usr/share/common-licenses/GPL'.
diff --git a/debian/dc-qt.desktop b/debian/dc-qt.desktop
new file mode 100644
index 0000000..172ef44
--- /dev/null
+++ b/debian/dc-qt.desktop
@@ -0,0 +1,8 @@
+[Desktop Entry]
+Name=dc-qt
+Comment=GUI for Direct Connect Protocol
+Exec=dc-qt
+Icon=/usr/share/pixmaps/dc-qt.xpm
+Terminal=false
+Type=Application
+Categories=Application;Network;Qt;
diff --git a/debian/dc-qt.xpm b/debian/dc-qt.xpm
new file mode 100644
index 0000000..7ede41b
--- /dev/null
+++ b/debian/dc-qt.xpm
@@ -0,0 +1,48 @@
+/* XPM */
+static char *dcgui_my2[] = {
+/* columns rows colors chars-per-pixel */
+"32 32 10 1",
+" c black",
+". c gray20",
+"X c #7F0000",
+"o c red",
+"O c yellow",
+"+ c gray60",
+"@ c #b2b2b2",
+"# c gray80",
+"$ c gray90",
+"% c None",
+/* pixels */
+"%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%",
+"%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%",
+"%% %%%%%%%%%%%%% %%%%%",
+"% %%%%%%%%%% %%",
+"% OOOOOOOO %%%%%% .OOOOOO. %",
+"% OOOOOOOOOO %%%% .OOOOOOOOOO %",
+"% OOOOOOOOOOO %% OOOOO..OOO. %",
+"% OOO. .oOoO % .OOO. .. %%",
+"% OOO. % .OOOO Ooo. %% %",
+"% OoO. %%% .OOo. .OoO. %%%%%%%%%",
+"% OOO. %%% .Ooo. .OoO %%%%%%%%%",
+"% oOO. %%%% OoO. .Ooo %%%%%%%%%%",
+"% ooo. %%%% oOO. .oOO %%%%%%%%%%",
+"% oOo. %%% .ooo. .ooo %%%%%%%%%",
+"% ooo. %%% .oOo. .ooO. %%%%%%%%%",
+"% oOO. .Oooo Ooo. %%%% %",
+"% ooo.. .oooo % oooo#+ ... %",
+"% oooooooooooo.%% .ooo++ ..ooo %",
+"% oooooooooo..%%%% .oooXXooooo %",
+"% o#@@@@@@@.#@%%%%#@..#@oooo. %",
+"% @@@@@@@@.@@%%%%@@%.@@.. %%",
+"%%%@@%%%%@@%@@%%%%@@%%@@%%%%%%%%",
+"%%%##%%%%##%##%%%%##%%##%%%%%%%%",
+"%%%########%##%%%%##%%##%%%%%%%%",
+"%%%%#######%########%%##%%%%%%%%",
+"%%%%%%%%%%$%$$$$$$$$%%$$%%%%%%%%",
+"%%%%%%%%%%$%%%%%%%%%%%%%%%%%%%%%",
+"%%%$$$$$$$$%%%%%%%%%%%%%%%%%%%%%",
+"%%%$$$$$$$$%%%%%%%%%%%%%%%%%%%%%",
+"%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%",
+"%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%",
+"%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%"
+};
diff --git a/debian/dirs b/debian/dirs
new file mode 100644
index 0000000..e772481
--- /dev/null
+++ b/debian/dirs
@@ -0,0 +1 @@
+usr/bin
diff --git a/debian/menu b/debian/menu
new file mode 100644
index 0000000..c52a01b
--- /dev/null
+++ b/debian/menu
@@ -0,0 +1,7 @@
+?package(dc-qt):needs="X11" \
+ section="Applications/Network/File Transfer" \
+ hints="KDE" \
+ title="DC-Qt" \
+ longtitle="GUI for Direct Connect Protocol" \
+ command="/usr/bin/dc-qt" \
+ icon="/usr/share/pixmaps/dc-qt.xpm"
diff --git a/debian/patches/10-path.patch b/debian/patches/10-path.patch
new file mode 100644
index 0000000..07b139c
--- /dev/null
+++ b/debian/patches/10-path.patch
@@ -0,0 +1,14 @@
+--- mainwindow.cpp.orig 2006-08-11 14:45:37.000000000 +1000
++++ ui/mainwindow.cpp 2006-08-11 14:48:38.000000000 +1000
+@@ -192,6 +192,11 @@
+ {
+ path = dir+"/../Resources/backend";
+ }
++ // Debian path to the backend
++ else if(QFile::exists("/usr/bin/dc-backend"))
++ {
++ path = "/usr/bin/dc-backend";
++ }
+ else
+ QMessageBox::critical(this,"Error","Could not find backend executable (installation problem?).");
+
diff --git a/debian/patches/20-gcc-fix.patch b/debian/patches/20-gcc-fix.patch
new file mode 100644
index 0000000..084f9cc
--- /dev/null
+++ b/debian/patches/20-gcc-fix.patch
@@ -0,0 +1,29 @@
+--- commandhandlers.cpp~ 2007-04-06 12:54:22.000000000 +1000
++++ dc-qt-0.2.0.alpha/backend/commandhandlers.cpp 2007-04-06 12:54:40.000000000 +1000
+@@ -551,4 +551,4 @@
+ driver->stopSender();
+ }
+
+-}
+\ No newline at end of file
++}
+--- socketmanager.cpp.orig 2007-04-06 12:53:16.000000000 +1000
++++ dc-qt-0.2.0.alpha/rpcdriver/socketmanager.cpp 2007-04-06 12:53:37.000000000 +1000
+@@ -1,5 +1,7 @@
+ #include "socketmanager.h"
+
++#include <sys/types.h>
++#include <cstdio>
+
+ using namespace std;
+
+--- Util.h.orig 2007-04-06 12:50:38.000000000 +1000
++++ dc-qt-0.2.0.alpha/dcpp/Util.h 2007-04-06 12:50:56.000000000 +1000
+@@ -28,6 +28,7 @@
+ #include <sys/types.h>
+ #include <unistd.h>
+ #include <stdlib.h>
++#include <climits>
+ #endif
+
+ #include "Text.h"
diff --git a/debian/patches/30-libboost-linking.patch b/debian/patches/30-libboost-linking.patch
new file mode 100644
index 0000000..178f057
--- /dev/null
+++ b/debian/patches/30-libboost-linking.patch
@@ -0,0 +1,34 @@
+Description: link with boost_system
+Author: Dmitrijs Ledkovs <dmitrij.ledkov@ubuntu.com>
+
+--- dc-qt-0.2.0.alpha.orig/ui/ui.pro
++++ dc-qt-0.2.0.alpha/ui/ui.pro
+@@ -78,5 +78,5 @@ CONFIG += qt release
+ INCPATH += . ../rpcdriver ../ ../backend /usr/include
+ RESOURCES += res.qrc
+ unix{
+- LIBS = -L../rpcdriver -L/usr/local/lib -lrpc -lboost_thread -lboost_program_options
++ LIBS = -L../rpcdriver -L/usr/local/lib -lrpc -lboost_thread -lboost_program_options -lboost_system
+ }
+--- dc-qt-0.2.0.alpha.orig/backend/SConscript
++++ dc-qt-0.2.0.alpha/backend/SConscript
+@@ -3,7 +3,7 @@
+ Import('env')
+
+ ## Copy the parent environment and att some libraries and linker paths
+-backende = env.Copy(CPPPATH = ['#rpcdriver/', '#dcpp'], LIBS = ['dcpp', 'rpc', 'boost_thread', 'pthread', 'z', 'bz2', 'boost_program_options','boost_filesystem'], LIBPATH = ['/usr/local/lib', '#rpcdriver', '#dcpp'])
++backende = env.Copy(CPPPATH = ['#rpcdriver/', '#dcpp'], LIBS = ['dcpp', 'rpc', 'boost_thread', 'pthread', 'z', 'bz2', 'boost_program_options','boost_filesystem','boost_system'], LIBPATH = ['/usr/local/lib', '#rpcdriver', '#dcpp'])
+
+ ## Build the backend executable
+ backende.Program('backend', Split('main.cpp commandhandlers.cpp ClientNotifier.cpp SessionManager.cpp Session.cpp TransferManager.cpp Selecter.cpp filelog.cpp SettingsMapper.cpp'))
+--- dc-qt-0.2.0.alpha.orig/rpcdriver/SConscript
++++ dc-qt-0.2.0.alpha/rpcdriver/SConscript
+@@ -3,7 +3,7 @@ Import('env')
+
+ rpce = env.Copy(CCFLAGS = '-g -O0 -Wall')
+ rpce.Append(CXXFLAGS = '-I. -I/usr/local/include')
+-rpce.Append(LIBS = 'libboost_thread')
++rpce.Append(LIBS = ['boost_thread', 'boost_system'])
+
+ rpce.Program('server',Split('socket.cpp socketmanager.cpp datainputstream.cpp commanddispatcher.cpp rpcdriver.cpp main.cpp dataoutputstream.cpp'))
+
diff --git a/debian/patches/40_gcc43_compat.diff b/debian/patches/40_gcc43_compat.diff
new file mode 100644
index 0000000..f42f337
--- /dev/null
+++ b/debian/patches/40_gcc43_compat.diff
@@ -0,0 +1,32 @@
+--- a/dcpp/ShareManager.cpp
++++ b/dcpp/ShareManager.cpp
+@@ -42,6 +42,7 @@
+ #endif
+
+ #include <limits>
++#include <memory>
+
+ ShareManager::ShareManager() : hits(0), listLen(0), bzXmlListLen(0),
+ xmlDirty(true), nmdcDirty(false), refreshDirs(false), update(false), initial(true), listN(0), lFile(NULL),
+--- a/rpcdriver/datainputstream.cpp
++++ b/rpcdriver/datainputstream.cpp
+@@ -16,6 +16,8 @@
+
+ #include <boost/scoped_array.hpp>
+
++#include <cstring>
++
+ using std::string;
+
+ namespace rpc
+--- a/rpcdriver/outputbuffer.h
++++ b/rpcdriver/outputbuffer.h
+@@ -6,6 +6,8 @@
+ using namespace std;
+ #include <boost/shared_ptr.hpp>
+
++#include <cstring>
++
+ namespace rpc {
+
+ /**
diff --git a/debian/patches/50_gcc44_compat.diff b/debian/patches/50_gcc44_compat.diff
new file mode 100644
index 0000000..aa2f712
--- /dev/null
+++ b/debian/patches/50_gcc44_compat.diff
@@ -0,0 +1,12 @@
+diff -ur ../dc-qt-0.2.0.alpha.old/ui/blockallocator.h ./ui/blockallocator.h
+--- a/ui/blockallocator.h 2009-12-25 15:04:06.435792622 +0100
++++ b/ui/blockallocator.h 2009-12-25 15:04:29.946791296 +0100
+@@ -1,6 +1,7 @@
+ #ifndef __blockallocator_h__
+ #define __blockallocator_h__
+
++#include <stdint.h>
+ #include <vector>
+ #include <iostream>
+
+Nur in ./ui: blockallocator.h~.
diff --git a/debian/rules b/debian/rules
new file mode 100755
index 0000000..d759d81
--- /dev/null
+++ b/debian/rules
@@ -0,0 +1,28 @@
+#!/usr/bin/make -f
+
+include /usr/share/cdbs/1/class/langcore.mk
+include /usr/share/cdbs/1/rules/simple-patchsys.mk
+include /usr/share/cdbs/1/class/qmake.mk
+include /usr/share/cdbs/1/rules/debhelper.mk
+
+SCONS = scons
+
+DEB_BUILDDIR = $(CURDIR)/ui
+
+QMAKE = qmake-qt4
+
+DEB_INSTALL_DOCS_ALL =
+
+common-configure-arch::
+ $(SCONS)
+
+clean::
+ -$(SCONS) -c
+ rm -f .sconsign.dblite
+
+common-install-arch::
+ $(SCONS) install PREFIX=$(CURDIR)/debian/dc-qt/usr
+ mv $(CURDIR)/debian/dc-qt/usr/bin/backend $(CURDIR)/debian/dc-qt/usr/bin/dc-backend
+ mv $(CURDIR)/debian/dc-qt/usr/bin/ui $(CURDIR)/debian/dc-qt/usr/bin/dc-qt
+ install -D -m 644 $(CURDIR)/debian/dc-qt.xpm $(CURDIR)/debian/dc-qt/usr/share/pixmaps/dc-qt.xpm
+ install -D -m 644 $(CURDIR)/debian/dc-qt.desktop $(CURDIR)/debian/dc-qt/usr/share/applications/dc-qt.desktop
diff --git a/rpcdriver/SConscript b/rpcdriver/SConscript
new file mode 100644
index 0000000..252a543
--- /dev/null
+++ b/rpcdriver/SConscript
@@ -0,0 +1,10 @@
+import os
+Import('env')
+
+rpce = env.Copy(CCFLAGS = '-g -O0 -Wall')
+rpce.Append(CXXFLAGS = '-I. -I/usr/local/include')
+rpce.Append(LIBS = 'libboost_thread')
+
+rpce.Program('server',Split('socket.cpp socketmanager.cpp datainputstream.cpp commanddispatcher.cpp rpcdriver.cpp main.cpp dataoutputstream.cpp'))
+
+rpce.Library('rpc', Split('socket.cpp socketmanager.cpp datainputstream.cpp commanddispatcher.cpp rpcdriver.cpp dataoutputstream.cpp')) \ No newline at end of file
diff --git a/rpcdriver/commanddispatcher.cpp b/rpcdriver/commanddispatcher.cpp
new file mode 100644
index 0000000..92517bb
--- /dev/null
+++ b/rpcdriver/commanddispatcher.cpp
@@ -0,0 +1,266 @@
+/*
+ * CommandDispatcher.cpp
+ * RpcDriver
+ *
+ * Created by Mikael Gransell on 1/31/06.
+ * Copyright 2006 __MyCompanyName__. All rights reserved.
+ *
+ */
+
+#include <iostream>
+using namespace std;
+
+#include "commanddispatcher.h"
+#include "rpcexception.h"
+#include "types.h"
+
+namespace rpc {
+
+void CommandDispatcher::setClientAuthenticated(int clientId,bool authenticated)
+{
+ if(authenticated) authenticatedClients.insert(clientId);
+ else authenticatedClients.erase(clientId);
+}
+
+bool CommandDispatcher::isClientAuthenticated(int clientId)
+{
+ if(!requireAuthentication) return true;
+ return authenticatedClients.find(clientId)!=authenticatedClients.end();
+}
+
+void rpc::CommandDispatcher::registerCommand( RpcCommandHandlerPtr cmd )
+{
+ // Lock the mutex
+ boost::mutex::scoped_lock lock( handlerListMutex );
+
+ // Add the item. If the item does not exist a new list will be created
+ // and the item added to it.
+ registeredCommands[cmd->getCmdName()].push_back( cmd );
+}
+
+void rpc::CommandDispatcher::handleCommand( int sender, CmdInputBufferPtr cmd )
+{
+ try {
+
+ // Create a DataInputStream for easy reading
+ DataInputStreamPtr cmdStream( new DataInputStream( cmd ) );
+
+ // Will hold the command
+ list<boost::any> params;
+
+ // The the id of the sender
+ params.push_back( sender );
+ // Get name of the command.
+ // This should allways be the first param in the command stream
+ string cmdName = retrieveCommandName( cmdStream );
+ //cout << "handleCommand: " << cmdName << endl;
+
+ // Add the name as the first param in the list
+ params.push_back( cmdName );
+ // Parse the params of the command.
+ // retriveCommandName should have advanced the buffer to the first param
+ parseCommandParams( cmdStream, params );
+
+ // Add the command to the buffer
+ commandBuffer.enqueue( params );
+
+ // Signal waiting thread
+ commandAvailable.notify_one();
+ }
+ catch( const RpcException& rpcExcept ) {
+
+ cout << "Could not handle command. Error: " << rpcExcept.getReason() << endl;
+ }
+}
+
+void rpc::CommandDispatcher::waitForCommand()
+{
+ try {
+
+ // To monitor the addition of commands in the incomming buffer
+ boost::mutex::scoped_lock lock( commandMonitor );
+
+ bool stop = false;
+ while( !stop ) {
+
+ // Wait for something to get placed in the queue
+ commandAvailable.wait( lock );
+
+ // Handle all the commands in the list
+ while( !commandBuffer.empty() ) {
+
+ try {
+
+ // Get first command
+ list<boost::any> cmdParams = commandBuffer.dequeue();
+
+ // Second should be the id of the client that sent this command
+ int sender = boost::any_cast<int>(cmdParams.front());
+ cmdParams.pop_front();
+ // First element should be the command name
+ string cmdName = boost::any_cast<string>(cmdParams.front());
+ cmdParams.pop_front();
+ if(!requireAuthentication || isClientAuthenticated(sender) || cmdName=="authenticate" )
+ dispatchCommand( cmdName, sender, cmdParams );
+ }
+ catch( const boost::bad_any_cast& castExcept ) {
+
+ cout << "Could not cast first parameter to command name. Error: " <<
+ castExcept.what() << endl;
+ }
+ catch( const std::out_of_range& rangeExcept ) {
+
+ cout << "Could not get command. Error: " << rangeExcept.what() << endl;
+ }
+ catch( const RpcException& except ) {
+
+ cout << "Could not dispatch command. Error: " << except.getReason() << endl;
+ }
+ }
+ }
+ }
+ catch( std::bad_alloc& allocExcept ) {
+
+ cout << "Could not allocate memory for buffer. Error: " << allocExcept.what() << endl;
+ }
+ catch( const boost::lock_error& lkExcept ) {
+
+ cout << "Could not lock command monitor mutex. Error: " << lkExcept.what() << endl;
+ }
+ catch(...) {
+ // Make sure we catch everything so that we dont fuck up the entire app
+ cout << "Unknown exception about to leave thread." << endl;
+ }
+}
+
+boost::any CommandDispatcher::getNextParam(DataInputStreamPtr paramStream,
+ list<boost::any>& params)
+{
+ // Hold the next argument
+ boost::any nextParam;
+ int size;
+ list<boost::any> listElements;
+ // Read the first byte containing the type of the next param
+ char type = paramStream->readByte();
+
+ switch( type ) {
+
+ case eRpcParamTypeInt:
+ nextParam = paramStream->readInt();
+ break;
+
+ case eRpcParamTypeByte:
+ nextParam = paramStream->readByte();
+ break;
+
+ case eRpcParamTypeShort:
+ nextParam = paramStream->readShort();
+ break;
+
+ case eRpcParamTypeBoolean:
+ nextParam = paramStream->readBool();
+ break;
+
+ case eRpcParamTypeLong:
+ nextParam = paramStream->readLong();
+ break;
+
+ case eRpcParamTypeString:
+ nextParam = paramStream->readUTF();
+ break;
+ case eRpcParamTypeList:
+ size = paramStream->readInt();
+
+ while(size--) listElements.push_back(getNextParam(paramStream,listElements));
+ nextParam = listElements;
+ break;
+ default:
+ // Could not match parameter type so we bail
+ throw RpcException("Invalid parameter type");
+ }
+ return nextParam;
+}
+
+void CommandDispatcher::parseCommandParams( DataInputStreamPtr paramStream,
+ list<boost::any>& params )
+{
+ try {
+
+ // Keep reading data as long as we have something more that the size byte
+ while( paramStream->remainingBytes() > 1 ) {
+ boost::any nextParam = getNextParam(paramStream,params);
+
+ // Add param to our list
+ params.push_back( nextParam );
+ }
+ }
+ catch( const RpcException& rpcExcept ) {
+
+ cout << "Could not read data. Error: " << rpcExcept.getReason() << endl;
+ throw;
+ }
+}
+
+string CommandDispatcher::retrieveCommandName( DataInputStreamPtr cmdStream )
+{
+ string cmdName = "";
+
+ // Read a string from the stream
+ try {
+ cmdName = cmdStream->readUTF();
+ }
+ catch( const RpcException& except ) {
+
+ cout << "Could not read command name from stream" << endl;
+ throw;
+ }
+
+ return cmdName;
+}
+
+void CommandDispatcher::dispatchCommand( const string& cmdName,
+ int sender,
+ const list<boost::any>& params )
+{
+ try {
+ // Lock access to the list
+ boost::mutex::scoped_lock lock( handlerListMutex );
+
+ // Find the list of command handlers that correspond to this command
+ map<string, RpcCommandHandlerList>::iterator it = registeredCommands.find( cmdName );
+
+ if( it != registeredCommands.end() ) {
+
+ // Iterate the list of commands and notify them of the command
+ RpcCommandHandlerList::iterator cmdIt = it->second.begin();
+ while( cmdIt != it->second.end() ) {
+
+ // We need at least numParams amount of params so that
+ // we dont index outside the list
+ if( (*cmdIt)->getNumParams() <= params.size() ) {
+ // Notify
+ (*cmdIt)->handleCommand( sender, params );
+ }
+ else {
+ cout << "Invalid number of params: " << cmdName << endl;
+ }
+
+ // Next command
+ ++cmdIt;
+ }
+ }
+ else {
+
+ // Maybe log something here.
+ cout << "Could not find any handlers for command: " <<
+ cmdName <<
+ endl;
+ }
+ }
+ catch( const boost::lock_error& e ) {
+
+ cout << "Could not lock mutex" << endl;
+ }
+}
+
+}
diff --git a/rpcdriver/commanddispatcher.h b/rpcdriver/commanddispatcher.h
new file mode 100644
index 0000000..f6a5f01
--- /dev/null
+++ b/rpcdriver/commanddispatcher.h
@@ -0,0 +1,130 @@
+
+
+#ifndef _COMMAND_DISPATCHER_H_
+#define _COMMAND_DISPATCHER_H_
+
+#include <boost/shared_ptr.hpp>
+#include <boost/utility.hpp>
+#include <boost/thread/mutex.hpp>
+#include <boost/thread/condition.hpp>
+#include <boost/any.hpp>
+
+#include <string>
+#include <map>
+#include <list>
+#include <set>
+
+#include "inputbuffer.h"
+#include "datainputstream.h"
+#include "protectedbuffer.h"
+#include "rpccommandhandler.h"
+
+namespace rpc {
+
+/**
+ * Class that parses and forwards commands that are received in
+ * the RpcDriver.
+ */
+class CommandDispatcher : private boost::noncopyable
+{
+public:
+
+ typedef std::list<RpcCommandHandlerPtr> RpcCommandHandlerList;
+
+ CommandDispatcher() : requireAuthentication(false) { }
+ ~CommandDispatcher() { }
+
+ /**
+ * Register a command with us so that it receives notifications
+ * when a command that carries its name is received.
+ * @param pCmdHandler Command to register.
+ */
+ void registerCommand( RpcCommandHandlerPtr pCmdHandler );
+
+ /**
+ * Handle a command that has been received in the RpcDriver. This includes
+ * parsing the parameters and notifying all RpcCommandHandlers that listen
+ * for this type of command.
+ * @param sender Id of the client that sent this command.
+ * @param cmd Input buffer containing the data of the command. This should
+ * be in the form of <cmd name>, <param>, <param>, ...
+ */
+ void handleCommand( int sender, CmdInputBufferPtr cmd );
+
+ /**
+ * Tell the thread to stop executing.
+ */
+ void abort();
+
+ /**
+ * Wait for commands to start coming in. This should be the entry point
+ * for the thread that sits and handles commands.
+ */
+ void waitForCommand();
+
+ void setClientAuthenticated(int clientId,bool authenticated);
+ bool isClientAuthenticated(int clientId);
+ void enableAuthenticationChecking(bool yes) {requireAuthentication=yes;}
+
+private:
+
+ /**
+ * Parse the suplied params and put them in a variant.
+ * @param params The parameters to be parsed.
+ * @return The resulting variant constructed from the params.
+ */
+ void parseCommandParams( DataInputStreamPtr paramStream, std::list<boost::any>& params );
+ boost::any getNextParam(DataInputStreamPtr paramStream,
+ std::list<boost::any>& params) ;
+
+ /**
+ * Retreive the name of the command from the input buffer representing
+ * the command. It will also advance the buffer past the command name
+ * so that others can keep reading to get the rest of the params.
+ * @param cmd Contains all the data for the command.
+ */
+ std::string retrieveCommandName( DataInputStreamPtr cmdStream );
+
+ /**
+ * Send the command specified by cmdName to all the registerd
+ * RpcCommandHandlers and pass a variant containing the params along.
+ * @param cmdName Name of the command.
+ * @param sender Id of the client that sent this command.
+ * @param params Variant containing parameters for the command.
+ */
+ void dispatchCommand( const std::string& cmdName,
+ int sender,
+ const std::list<boost::any>& params );
+
+ /// Make sure that two threads don't access the map of commands at
+ /// the same time.
+ boost::mutex handlerListMutex;
+
+ /// Mutex used in a condition to make the receiver thread wait
+ /// for data to arrive in the outgoing queue
+ boost::mutex commandMonitor;
+
+ /// Condition used to wait for data to be present in the command queue.
+ /// Used together with m_queueMonitor
+ boost::condition commandAvailable;
+
+ /// Map containing all the commands that have registered wich maps
+ /// a command name to a list of RpcCommandHandlers registerd for that name.
+ std::map< std::string, RpcCommandHandlerList> registeredCommands;
+
+ /// Buffer holding the commands that are waiting to be handled
+ ProtectedBuffer< std::list<boost::any> > commandBuffer;
+
+ /// Set this to enable authentication checking
+ bool requireAuthentication;
+
+ /// Holds authenticated clients
+ std::set<int> authenticatedClients;
+};
+
+/// Smart pointer class for convinience
+typedef boost::shared_ptr<CommandDispatcher> CommandDispatcherPtr;
+
+}
+
+#endif
diff --git a/rpcdriver/datainputstream.cpp b/rpcdriver/datainputstream.cpp
new file mode 100644
index 0000000..ea38df2
--- /dev/null
+++ b/rpcdriver/datainputstream.cpp
@@ -0,0 +1,106 @@
+//
+// C++ Implementation: datainputstream
+//
+// Description:
+//
+//
+// Author: Rikard Bjorklind <olof@linux.nu>, (C) 2006
+//
+// Copyright: See COPYING file that comes with this distribution
+//
+//
+#include "datainputstream.h"
+#include <netinet/in.h>
+
+#include "rpcexception.h"
+
+#include <boost/scoped_array.hpp>
+
+using std::string;
+
+namespace rpc
+{
+
+DataInputStream::DataInputStream( CmdInputBufferPtr buf ) : data( buf )
+{
+}
+
+int DataInputStream::readInt()
+{
+ checkRead( 4 );
+ int i = *(reinterpret_cast<int*>(data->curPtr()));
+ i = ntohl(i);
+ data->advance( 4 );
+ return i;
+}
+
+char DataInputStream::readByte()
+{
+ checkRead( 1 );
+ char ch = *(data->curPtr());
+ data->advance( 1 );
+ return ch;
+}
+
+short DataInputStream::readShort()
+{
+ checkRead( 2 );
+ short s = *(reinterpret_cast<short*>(data->curPtr()));
+ s = ntohs(s);
+ data->advance( 2 );
+ return s;
+}
+
+bool DataInputStream::readBool()
+{
+ checkRead( 1 );
+ char ch = *(data->curPtr());
+ data->advance( 1 );
+ return ch!=0;
+}
+
+int64 DataInputStream::readLong()
+{
+ checkRead( 8 );
+ uint32_t l1 = *(reinterpret_cast<uint32_t*>(data->curPtr()));
+ data->advance( 4 );
+ uint32_t l2 = *(reinterpret_cast<uint32_t*>(data->curPtr()));
+ data->advance( 4 );
+
+ l1 = ntohl(l1);
+ l2 = ntohl(l2);
+
+ int64 l = ((int64)l1<<32)+l2;
+
+ return l;
+}
+
+string DataInputStream::readUTF()
+{
+ // one int for length, rest is the string
+ int length = readInt();
+ checkRead( length );
+ // Allocate space for string
+ boost::scoped_array<char> str( new char[length+1] );
+ // Copy date from buffer
+ memcpy( str.get(), data->curPtr(), length );
+ // Null-terminate
+ str.get()[length] = '\0';
+ // Create string to return
+ string ret = str.get();
+ // Advance the buffer past the string
+ data->advance( length );
+
+ return ret;
+}
+
+DataInputStream::~DataInputStream()
+{}
+
+void DataInputStream::checkRead( int i )
+{
+ if(data->remaining() < i) throw RpcException( "EndOfStream" );
+}
+
+}
+
diff --git a/rpcdriver/datainputstream.h b/rpcdriver/datainputstream.h
new file mode 100644
index 0000000..7649285
--- /dev/null
+++ b/rpcdriver/datainputstream.h
@@ -0,0 +1,58 @@
+//
+// C++ Interface: datainputstream
+//
+// Description:
+//
+//
+// Author: Rikard Bjorklind <olof@linux.nu>, (C) 2006
+//
+// Copyright: See COPYING file that comes with this distribution
+//
+//
+#ifndef RPCDATAINPUTSTREAM_H
+#define RPCDATAINPUTSTREAM_H
+
+#include <string>
+
+#include <boost/shared_ptr.hpp>
+
+#include "inputbuffer.h"
+#include "types.h"
+
+namespace rpc {
+
+/**
+ @author Rikard Bjorklind <olof@linux.nu>
+*/
+class DataInputStream{
+public:
+ DataInputStream(CmdInputBufferPtr buf);
+
+ int readInt();
+ char readByte();
+ short readShort();
+ bool readBool();
+ int64 readLong();
+
+ /**
+ * Read a string from the internal buffer and advance its
+ * possition to the end of the string.
+ */
+ std::string readUTF();
+
+ /**
+ * Check how many bytes remain in the internal buffer.
+ */
+ int remainingBytes() const { return data->remaining(); }
+
+ ~DataInputStream();
+private:
+ void checkRead(int);
+ CmdInputBufferPtr data;
+};
+
+typedef boost::shared_ptr<DataInputStream> DataInputStreamPtr;
+
+}
+
+#endif
diff --git a/rpcdriver/dataoutputstream.cpp b/rpcdriver/dataoutputstream.cpp
new file mode 100644
index 0000000..eaa99dd
--- /dev/null
+++ b/rpcdriver/dataoutputstream.cpp
@@ -0,0 +1,49 @@
+#include "dataoutputstream.h"
+#include <netinet/in.h>
+
+namespace rpc {
+
+ void DataOutputStream::writeInt(int32 i)
+{
+ i = htonl(i);
+ out->write(&i,4);
+}
+
+void DataOutputStream::writeByte(int8 b)
+{
+ out->write(&b,1);
+}
+
+void DataOutputStream::writeShort(int16 s)
+{
+ s = htons(s);
+ out->write(&s,2);
+}
+
+void DataOutputStream::writeBool(bool b)
+{
+ char temp = static_cast<char>(b);
+ out->write(&temp,1);
+}
+
+void DataOutputStream::writeLong(int64 l)
+{
+ int l1 = *((int*)&l);
+ unsigned int l2 = ((unsigned int*)&l)[1];
+
+ l1 = htonl(l1);
+ l2 = htonl(l2);
+ l = ((int64)l1<<32)+l2;
+ out->write(&l,8);
+}
+
+void DataOutputStream::writeUTF(const std::string& str)
+{
+ // length sent is the length of the string, no null termination
+ int len = str.size();
+ int nlen = htonl(len);
+ out->write(&nlen,4);
+ out->write(str.c_str(),len);
+}
+
+}
diff --git a/rpcdriver/dataoutputstream.h b/rpcdriver/dataoutputstream.h
new file mode 100644
index 0000000..8a3abc6
--- /dev/null
+++ b/rpcdriver/dataoutputstream.h
@@ -0,0 +1,43 @@
+#ifndef dataoutputstream_H__
+#define dataoutputstream_H__
+
+#include "outputbuffer.h"
+#include "types.h"
+#include <string>
+
+namespace rpc {
+
+ /**
+ * Provides easy writing of primitive data type values.
+ *
+ */
+class DataOutputStream
+{
+ public:
+ DataOutputStream(OutputBuffer* aBuffer) : out(aBuffer) {}
+
+ /*! Writes a 32 bit signed integer */
+ void writeInt(int32 i);
+
+ /*! Writes a 8 bit signed byte */
+ void writeByte(int8 b);
+
+ /*! Writes a 16 bit signed short */
+ void writeShort(int16 s);
+
+ /*! Writes a 8 bit boolean value */
+ void writeBool(bool b);
+
+ /*! Writes a 64 bit signed integer */
+ void writeLong(int64 l);
+
+ /*! Writes a string. The string must be encoded using UTF-8, this method does not perform any conversion. */
+ void writeUTF(const std::string& str);
+
+ private:
+ OutputBuffer* out;
+};
+
+}
+
+#endif
diff --git a/rpcdriver/inputbuffer.h b/rpcdriver/inputbuffer.h
new file mode 100644
index 0000000..77f8578
--- /dev/null
+++ b/rpcdriver/inputbuffer.h
@@ -0,0 +1,83 @@
+//
+// C++ Interface: inputbuffer
+//
+// Description:
+//
+//
+// Author: Rikard Bjorklind <olof@linux.nu>, (C) 2006
+//
+// Copyright: See COPYING file that comes with this distribution
+//
+//
+#ifndef RPCINPUTBUFFER_H
+#define RPCINPUTBUFFER_H
+
+#include <boost/scoped_array.hpp>
+#include <boost/shared_ptr.hpp>
+#include <boost/utility.hpp>
+
+namespace rpc
+{
+
+/**
+ @author Rikard Bjorklind <olof@linux.nu>
+*/
+template<typename T>
+class InputBuffer : private boost::noncopyable
+{
+public:
+ explicit InputBuffer(int aSize) : size( aSize ), buf( new char[size] ), p( buf.get() )
+ {
+ }
+
+ T* bufPtr()
+ {
+ return (T*)buf.get();
+ }
+
+ T* curPtr()
+ {
+ return reinterpret_cast<T*>(p);
+ }
+
+ void advance(int bytes)
+ {
+ p += bytes;
+ }
+
+ bool filled() const
+ {
+ return p == ( buf.get() + size );
+ }
+
+ int remaining() const
+ {
+ return ( buf.get() + size ) - p;
+ }
+
+ /**
+ * Reset the pointer to the next data item.
+ */
+ void reset()
+ {
+ // Reset the pointer so that we start at the begining of the buffer
+ p = buf.get();
+ }
+
+ ~InputBuffer()
+ {
+ }
+
+ int getSize() const {return size;}
+
+private:
+ const int size;
+ boost::scoped_array<char> buf;
+ char* p; // points to the next free place
+};
+
+typedef boost::shared_ptr< InputBuffer<char> > CmdInputBufferPtr;
+
+}
+
+#endif
diff --git a/rpcdriver/main.cpp b/rpcdriver/main.cpp
new file mode 100644
index 0000000..55ffaf7
--- /dev/null
+++ b/rpcdriver/main.cpp
@@ -0,0 +1,62 @@
+#include <iostream>
+
+#include <boost/bind.hpp>
+#include <boost/thread/thread.hpp>
+
+#include <list>
+#include <string>
+
+#include "rpcdriver.h"
+
+using namespace rpc;
+using namespace std;
+
+rpc::RpcDriver* rd;
+
+class HelloHandler : public RpcCommandHandler
+{
+public:
+ HelloHandler(const std::string& cmdName) : RpcCommandHandler(cmdName,
+ NULL,
+ 1) {}
+ virtual void handleCommand( int clientId,
+ const std::list<boost::any>& params )
+{
+
+
+std::cout << "Handle hello" << std::endl;
+// Send a reply
+
+ rpc::CmdPtr cmd(new list<boost::any>);
+ cmd->push_back( string("hoho") );
+ list<boost::any> list1;
+ for(int i = 0; i < 10;i++) {
+ list<boost::any> list2;
+ for(int j=0;j<5;j++) list2.push_back( i*j );
+ list1.push_back(list2);
+ }
+ cmd->push_back( 10 );
+ cmd->push_back( list1 );
+ rd->queueCommand( -1, cmd );
+}
+};
+
+int main (int argc, char * const argv[]) {
+
+ try {
+
+ // Create instance of RpcDriver
+ rpc::RpcServerDriver driver( 6161 );
+ rd = &driver;
+ std::cout << "Server started." << std::endl;
+
+ RpcCommandHandlerPtr hptr(new HelloHandler("hello"));
+ driver.registerCommand(hptr);
+
+ driver.waitForCompletion();
+ }
+ catch(...) {
+ std::cout << "Exception reached all the way to end of main";
+ }
+ return 0;
+}
diff --git a/rpcdriver/outputbuffer.h b/rpcdriver/outputbuffer.h
new file mode 100644
index 0000000..ca1d0bf
--- /dev/null
+++ b/rpcdriver/outputbuffer.h
@@ -0,0 +1,57 @@
+#ifndef outputbuffer_H__
+#define outputbuffer_H__
+
+#include <stdlib.h>
+#include <iostream>
+using namespace std;
+#include <boost/shared_ptr.hpp>
+
+namespace rpc {
+
+/**
+ * \brief A data accumulator.
+ * This class is esentially a wrapper around a byte array which provides writing methods and automatic expantion.
+ */
+class OutputBuffer
+{
+public:
+ OutputBuffer() { init(4*1024,4*1024); }
+ OutputBuffer(int initialSize,int sizeIncrement) { init(initialSize,sizeIncrement); }
+ ~OutputBuffer() { free(buf); }
+
+ /** Resets the buffer so that it can be reused */
+ void clear() {size = 0;}
+ /** Returns a pointer to the data written to this buffer */
+ const char* getByteArray() const {return buf;}
+ /** Returs the size of the data written to this buffer */
+ int getSize() const {return size;}
+
+ /** Writes some data to the buffer, expanding it as necessary */
+ void write(const void* data, int numBytes) {
+ if(numBytes > capacity-size) {
+ while(numBytes > capacity-size) capacity+=increment;
+ buf = (char*)realloc(buf,capacity);
+ }
+ memcpy(buf+size,data,numBytes);
+ size+=numBytes;
+ }
+
+private:
+ char* buf;
+ int capacity;
+ int size;
+ int increment;
+
+ void init(int aSize,int aIncrement) {
+ buf = (char*)malloc(aSize);
+ increment = aIncrement;
+ size = 0;
+ capacity = aSize;
+ }
+
+};
+
+typedef boost::shared_ptr<OutputBuffer> OutputBufferPtr;
+}
+
+#endif
diff --git a/rpcdriver/protectedbuffer.h b/rpcdriver/protectedbuffer.h
new file mode 100644
index 0000000..e7f0dd0
--- /dev/null
+++ b/rpcdriver/protectedbuffer.h
@@ -0,0 +1,86 @@
+/*
+ * Buffer.h
+ * RpcDriver
+ *
+ * Created by Mikael Gransell on 1/31/06.
+ * Copyright 2006 __MyCompanyName__. All rights reserved.
+ *
+ */
+
+#ifndef _BUFFER_H_
+#define _BUFFER_H_
+
+#include <boost/thread/mutex.hpp>
+
+#include <queue>
+#include <stdexcept>
+
+namespace rpc {
+
+/**
+ * Class that synchronizes access to a queue of items.
+ */
+template<class T>
+class ProtectedBuffer
+{
+public:
+
+ /**
+ * Adds an item to the end of the buffer.
+ * @param item Item to be added.
+ */
+ void enqueue( const T& item ) throw()
+ {
+ // Lock the mutex
+ boost::mutex::scoped_lock lock( m_mutex );
+ // Add item
+ m_queue.push( item );
+ }
+
+ /**
+ * Remove the first item in the buffer and return it to the caller.
+ * @return The first item in the buffer.
+ */
+ T dequeue() throw()
+ {
+ // Lock mutex
+ boost::mutex::scoped_lock lock( m_mutex );
+ // Check that the list is not empty
+ if( m_queue.empty() ) {
+
+ // Throw an exception
+ throw std::out_of_range( "Queue is empty" );
+ }
+
+ // Get the first item
+ T item = m_queue.front();
+ // Remove the first item
+ m_queue.pop();
+
+ return item;
+ }
+
+ /**
+ * Check if the buffer is empty.
+ * @return true if the buffer is empty.
+ */
+ bool empty() throw()
+ {
+ // Lock mutex
+ boost::mutex::scoped_lock lock( m_mutex );
+ // Fetch size of inner queue
+ bool empty = m_queue.empty();
+ return empty;
+ }
+
+private:
+ /// Mutex to synchronize access
+ boost::mutex m_mutex;
+
+ /// The queue of items
+ std::queue<T> m_queue;
+};
+
+}
+
+#endif
diff --git a/rpcdriver/rpccommandhandler.h b/rpcdriver/rpccommandhandler.h
new file mode 100644
index 0000000..8009e73
--- /dev/null
+++ b/rpcdriver/rpccommandhandler.h
@@ -0,0 +1,81 @@
+/*
+ * RpcCommandHandler.h
+ * RpcDriver
+ *
+ * Created by Mikael Gransell on 1/31/06.
+ * Copyright 2006 __MyCompanyName__. All rights reserved.
+ *
+ */
+
+#ifndef _RPC_COMMAND_H_
+#define _RPC_COMMAND_H_
+
+#include <boost/shared_ptr.hpp>
+#include <boost/any.hpp>
+#include <boost/function.hpp>
+
+#include <string>
+#include <list>
+
+namespace rpc {
+
+/// Smart pointer class for queueing commands
+typedef boost::shared_ptr< std::list<boost::any> > CmdPtr;
+
+/**
+ * Base class for all commands that want to register for notifications
+ * when commands come in to the RpcDriver.
+ */
+class RpcCommandHandler {
+
+public:
+ /**
+ * Create a command that corresponds to the given name.
+ */
+ explicit RpcCommandHandler( const std::string& name,
+ boost::function<void (int, const CmdPtr&)> sender,
+ short np )
+ : cmdSenderMethod( sender ), cmdName( name ), numParams(np)
+ { }
+
+ virtual ~RpcCommandHandler() {}
+
+ /**
+ * Retreive the name that this command shoul be registered for.
+ * @return Command name.
+ */
+ const std::string& getCmdName() const { return cmdName; }
+
+ /**
+ * Method that actually handles the command.
+ * @param clientId Id of the client that sent this command. Use this
+ * id when sending replies to a specific client.
+ * @param params Variant containing the parameters that were sent.
+ */
+ virtual void handleCommand( int clientId,
+ const std::list<boost::any>& params ) = 0;
+
+ /**
+ * Get the amount of params that this command wants.
+ * @return Number of params we want
+ */
+ short getNumParams() const { return numParams; }
+
+protected:
+ /// Function pointer to a method that allows us to send
+ /// commands using the pc driver.
+ boost::function<void (int, const CmdPtr&)> cmdSenderMethod;
+
+private:
+ /// The name that this command is registered for
+ const std::string cmdName;
+ /// The amount of paramams that this command should receive
+ const short numParams;
+};
+
+/// Smart pointer class for simplicity
+typedef boost::shared_ptr<RpcCommandHandler> RpcCommandHandlerPtr;
+
+}
+
+#endif
diff --git a/rpcdriver/rpcdriver.cpp b/rpcdriver/rpcdriver.cpp
new file mode 100644
index 0000000..39c0edc
--- /dev/null
+++ b/rpcdriver/rpcdriver.cpp
@@ -0,0 +1,510 @@
+/*
+ * RpcDriver.cpp
+ * RpcDriver
+ *
+ * Created by Mikael Gransell on 1/31/06.
+ * Copyright 2006 __MyCompanyName__. All rights reserved.
+ *
+ */
+
+#include <boost/bind.hpp>
+
+#include <typeinfo>
+#include <iostream>
+
+#include <netinet/in.h>
+
+#include "rpcdriver.h"
+
+#include "socketmanager.h"
+#include "inputbuffer.h"
+#include "datainputstream.h"
+#include "dataoutputstream.h"
+#include "rpcexception.h"
+
+using std::cout;
+using std::endl;
+using std::map;
+using std::list;
+using std::string;
+
+namespace rpc {
+
+RpcClientDriver::RpcClientDriver( const char* addr, int port ) : serverPort(port), serverAddr(addr)
+{
+ try {
+
+ Socket* client = new Socket( this );
+ //client->connect(addr, port);
+ clientSocket = client;
+ boost::shared_ptr< InputBuffer<int> > sizeBuf( new InputBuffer<int>( 4 ) );
+ SocketData sd = { sizeBuf, CmdInputBufferPtr() };
+ socketDataMap[clientSocket] = sd;
+ //SocketManager::instance()->addSocket(clientSocket);
+ enableAuthenticationChecking(false);
+ }
+ catch( const SocketException& e ) {
+ cout << "Could not create RpcClientDriver. " << e.getReason() << endl;
+ throw;
+ }
+}
+
+void RpcClientDriver::connect()
+{
+ clientSocket->connect(serverAddr, serverPort);
+ SocketManager::instance()->addSocket(clientSocket);
+ // Start the threads
+ startServer();
+}
+
+RpcClientDriver::~RpcClientDriver()
+{
+ clientSocket->disconnect();
+ SocketManager::instance()->removeSocket(clientSocket);
+}
+
+
+RpcServerDriver::RpcServerDriver( int port ) : serverPort(port)
+{
+ try {
+ enableAuthenticationChecking(true);
+ // Init the server listening socket
+ server = new Socket( this );
+
+ }
+ catch( const SocketException& e ) {
+ cout << "Could not init server socket. " << e.getReason() << endl;
+ throw;
+ }
+}
+
+void RpcServerDriver::startListening()
+{
+ try {
+ server->listen( serverPort );
+ SocketManager::instance()->addSocket( server );
+ }
+ catch( const SocketException& e ) {
+ cout << "Could not init server socket. " << e.getReason() << endl;
+ throw;
+ }
+ // Start the threads
+ startServer();
+}
+
+RpcServerDriver::~RpcServerDriver()
+{
+}
+
+
+RpcDriver::RpcDriver( ) : abort( false )
+{
+ // Create command dispatcher
+ CommandDispatcherPtr tempDisp( new CommandDispatcher() );
+ cmdDispatcher = tempDisp;
+
+ threads.create_thread( boost::bind( &CommandDispatcher::waitForCommand,
+ cmdDispatcher.get() ) );
+}
+
+RpcDriver::~RpcDriver()
+{
+ // Disconnect all the sockets at this point
+ std::set<Socket*> sockets = SocketManager::instance()->getSockets();
+ std::set<Socket*>::iterator it = sockets.begin();
+ for(;it!=sockets.end();it++) {
+ SocketManager::instance()->removeSocket(*it);
+ (*it)->disconnect();
+ delete *it;
+ }
+ // Signal command handler thread to exit
+
+ // Join with the command handler thread before destructing
+
+ // Removed by rikard, this is really not a good place to stop
+ // threads when you dont event know if they are running!!
+
+ // threads.join_all();
+}
+
+void RpcDriver::receiver()
+{
+ cout << "enter receiver" << endl;
+ try {
+
+ // How should we signal the receiver thread that we should quit.
+ while( !abort ) {
+
+ // Perform a select on all the sockets
+ SocketManager::instance()->select();
+ }
+ }
+ catch( const SocketException& e ) {
+
+ cout << "Exception caught while selecting. " << e.getReason() << endl;
+ }
+ cout << "exit receiver" << endl;
+}
+
+void RpcDriver::sender()
+{
+ try {
+
+ // Maybe we need another lock here so that no other threads execute thi method by misstake
+
+ // Lock for use with the condition below
+ boost::mutex::scoped_lock commandNotifyLock( commandMonitor );
+
+ // Keep waiting for command until someone tells us to stop
+ while( true ) {
+
+ //cout << "Waiting for a command" << endl;
+
+ // Wait for commands to be available in the
+ commandAvailable.wait( commandNotifyLock );
+
+ // Lock abort flag
+ {
+ boost::mutex::scoped_lock lk( abortMutex );
+
+ // Check abort flag
+ if( abort ) {
+
+ // Stop execution
+ break;
+ }
+ }
+
+ // Keep sending all the commands in the buffer until it is empty
+ while( !commandBuffer.empty() ) {
+
+ // Get next command
+ CmdEntry cmdEntry = commandBuffer.dequeue();
+
+
+ try {
+
+ OutputBufferPtr buf = convertCmdToBinaryBuffer( cmdEntry.cmd );
+
+ // Write to clients
+ sendCommandToClients( buf, cmdEntry.socket );
+ }
+ catch( const RpcException& rpcExcept ) {
+ cout << "--- RpcException in sender() ---" << endl;
+ cout << "Reason: " << rpcExcept.getReason() << endl;
+ string funk = boost::any_cast<string>(cmdEntry.cmd->front());
+ cout << "ProcedureName: " << funk << endl;
+
+ }
+ catch( const SocketException& socketExcept ) {
+ cout << "Could not write socket. " << socketExcept.getReason() << endl;
+ }
+ }
+ }
+ }
+ catch ( const boost::lock_error& e ) {
+
+ cout << "Could not lock mutex!" << endl;
+ }
+ catch( const std::bad_alloc& allocExcept ) {
+
+ cout << "Could not allocate buffer. " << allocExcept.what() << endl;
+ }
+
+ cout << "Stoping sender thread" << endl;
+}
+
+void RpcDriver::queueCommand( int socketId, const CmdPtr& cmd )
+{
+ // Add item to buffer. It should handle the synch by itself
+ CmdEntry entry = { socketId, cmd };
+ commandBuffer.enqueue( entry );
+
+ // Signal waiting thread that there is a command available
+ commandAvailable.notify_one();
+}
+
+void RpcDriver::registerCommand( RpcCommandHandlerPtr& pCmd )
+{
+ // Just forward the command handler to the command dispatcher
+ cmdDispatcher->registerCommand( pCmd );
+}
+
+void RpcDriver::startServer()
+{
+ threads.create_thread( boost::bind( &RpcDriver::receiver, this ) );
+ senderThread = threads.create_thread( boost::bind( &RpcDriver::sender, this ) );
+}
+
+void RpcDriver::stopSender()
+{
+
+ try {
+
+ {
+ boost::mutex::scoped_lock lk( abortMutex );
+ abort = true;
+ }
+
+ // Notify thread
+ commandAvailable.notify_one();
+ }
+ catch( const boost::lock_error& e ) {
+
+ cout << "Could not lock mutex" << endl;
+ }
+}
+
+void RpcDriver::waitForCompletion()
+{
+ cout << "Waiting for threads" << endl;
+ //threads.join_all();
+ senderThread->join();
+ cout << "Done Waiting" << endl;
+}
+
+
+void RpcDriver::onRead( Socket* socket )
+{
+ // We can read some data from the socket now
+ int bytesRead = 0;
+ try {
+
+ // Find the data for this socket and try to read into it
+ map< Socket*, SocketData >::iterator socketData = socketDataMap.find( socket );
+ if( socketData != socketDataMap.end() ) {
+
+ // Read an int to get the size of the payload
+ if( !socketData->second.sizeBuf->filled() ) {
+
+ bytesRead = socket->read( socketData->second.sizeBuf->curPtr(),
+ socketData->second.sizeBuf->remaining() );
+ socketData->second.sizeBuf->advance( bytesRead );
+
+ // If we have read the size now
+ if( socketData->second.sizeBuf->filled() ) {
+
+ // Get available size
+ uint32_t size = ntohl(*(socketData->second.sizeBuf->bufPtr()));
+ // Reset old data
+ CmdInputBufferPtr tempBuffer( new InputBuffer<char>( size ) );
+ socketData->second.dataBuf = tempBuffer;
+ }
+ }
+ else {
+
+ // We have read the size and there should be an input buffer available
+ bytesRead = socket->read( socketData->second.dataBuf->curPtr(),
+ socketData->second.dataBuf->remaining() );
+ socketData->second.dataBuf->advance( bytesRead );
+
+ // Check if we have read all the data that we are supposed to
+ if( socketData->second.dataBuf->filled() ) {
+ // Reset the buffer pointer so that it points to the beginning of the buffer
+ socketData->second.dataBuf->reset();
+
+ // Send the command to the command dispatcher
+ cmdDispatcher->handleCommand( socket->getId(), socketData->second.dataBuf );
+ // Reset the sizeBuf so that we can start to receive a new size
+ socketData->second.sizeBuf->reset();
+ }
+ }
+ }
+ else {
+ cout << "Could not find data corresponding to socket" << endl;
+ }
+ }
+ catch( const RpcException& rpcExcept ) {
+
+ cout << "Execption " << rpcExcept.getReason() << endl;
+ }
+ catch( const SocketException& socketExcept ) {
+
+ cout << "Could not read socket. " << socketExcept.getReason() << endl;
+ }
+ catch( const std::bad_alloc& allocExcept ) {
+
+ cout << "Could not allocate buffer. " << allocExcept.what() << endl;
+ }
+
+}
+
+void RpcDriver::onDisconnect( Socket* socket )
+{
+ try {
+ cout << "onDisconnect" << endl;
+ // Remove the socket from the socket manager
+ SocketManager::instance()->removeSocket( socket );
+
+ map< Socket*, SocketData>::iterator socketData = socketDataMap.find( socket );
+ socketDataMap.erase( socketData );
+ setClientAuthenticated( socket->getId(), false );
+ }
+ catch( const SocketException& e ) {
+
+ cout << "Exception caught when removing socket from socket manager" <<
+ e.getReason() <<
+ endl;
+ }
+}
+
+
+void RpcDriver::onIncoming( Socket* socket )
+{
+ try {
+ cout << "onIncoming" << endl;
+ // Accept the conenction
+ Socket* s = socket->accept( this );
+ boost::shared_ptr< InputBuffer<int> > sizeBuf( new InputBuffer<int>( 4 ) );
+ SocketData sd = { sizeBuf, CmdInputBufferPtr() };
+ socketDataMap[s] = sd;
+ cout << "Accepted new connection " << s->getFd() << endl;
+ SocketManager::instance()->addSocket( s );
+ }
+ catch( const SocketException& e ) {
+
+ cout << "Exception caught while accepting connection. " <<
+ e.getReason() <<
+ endl;
+ }
+}
+
+
+void RpcDriver::convertListToBinary(list< boost::any >::const_iterator cmdIt,
+list< boost::any >::const_iterator itEnd,
+DataOutputStream& stream) const
+{
+// Iterate through all the entries in the command and write the to the stream
+ while( cmdIt != itEnd ) {
+
+ if( cmdIt->type() == typeid(int) ) {
+ stream.writeByte(eRpcParamTypeInt);
+ stream.writeInt( boost::any_cast<int>(*cmdIt) );
+ }
+ else if( cmdIt->type() == typeid(char) ) {
+ stream.writeByte(eRpcParamTypeByte);
+ stream.writeByte( boost::any_cast<char>(*cmdIt) );
+ }
+ else if( cmdIt->type() == typeid(short) ) {
+ stream.writeByte(eRpcParamTypeShort);
+ stream.writeShort( boost::any_cast<short>(*cmdIt) );
+ }
+ else if( cmdIt->type() == typeid(bool) ) {
+ stream.writeByte(eRpcParamTypeBoolean);
+ stream.writeBool( boost::any_cast<bool>(*cmdIt) );
+ }
+ else if( cmdIt->type() == typeid(int64_t) ) {
+ stream.writeByte(eRpcParamTypeLong);
+ stream.writeLong( boost::any_cast<int64_t>(*cmdIt) );
+ }
+ else if( cmdIt->type() == typeid(string) ) {
+ stream.writeByte(eRpcParamTypeString);
+ stream.writeUTF( boost::any_cast<string>(*cmdIt) );
+ }
+ else if( cmdIt->type() == typeid(std::list<boost::any>) ) {
+ stream.writeByte(eRpcParamTypeList);
+ stream.writeInt((boost::any_cast<std::list<boost::any> >(*cmdIt)).size());
+ const std::list<boost::any>& lst = boost::any_cast<const std::list<boost::any> >(*cmdIt);
+ convertListToBinary(lst.begin(),lst.end(),stream);
+//(boost::any_cast<std::list<boost::any> >(*cmdIt)).begin(),
+//(boost::any_cast<std::list<boost::any> >(*cmdIt)).end(),
+//stream);
+ }
+ else {
+ cout << "unknown" << endl;
+ throw RpcException( "Invalid type in command" );
+ }
+
+ ++cmdIt;
+ }
+}
+
+OutputBufferPtr RpcDriver::convertCmdToBinaryBuffer( CmdPtr cmd ) const
+{
+ OutputBufferPtr buf( new OutputBuffer() );
+ DataOutputStream stream( buf.get() );
+
+ // Write a dummy int that will later hold the size
+ stream.writeInt(0);
+
+ try {
+
+ // To iterate through the list of params
+ list< boost::any >::const_iterator cmdIt = cmd->begin();
+
+ // First parameter should be the name
+ //cout << "Sending command: " << boost::any_cast<string>(*cmdIt) << endl;
+ stream.writeUTF(boost::any_cast<string>(*cmdIt));
+ ++cmdIt;
+
+ convertListToBinary(cmdIt,cmd->end(),stream);
+ }
+ catch( const boost::bad_any_cast& e ) {
+ cout << e.what() << endl;
+ throw RpcException( "Could not parse command" );
+ }
+
+ // Finally we write the size of the buffer to the first pos
+ // We have to do something about this crapy line of code
+ int* sizePtr = reinterpret_cast<int*>(const_cast<char*>(buf->getByteArray()));
+ // Subtract the size parameter from the size since this is not part of the command
+ *sizePtr = htonl(buf->getSize()-4);
+
+ return buf;
+}
+
+void RpcDriver::sendCommandToClients( OutputBufferPtr buf, int socketId ) const
+{
+ //cout << "sendCommandToClients" << endl;
+ // Send to all connected clients
+ if(socketId==-1)
+ {
+ const std::set<Socket*>& sockets = SocketManager::instance()->getSockets();
+ std::set<Socket*>::iterator it = sockets.begin();
+ while(it!=sockets.end()) {
+ if( !(*it)->getIsServer() && cmdDispatcher->isClientAuthenticated((*it)->getId())) {
+
+ try {
+ int bytesWritten = 0;
+ while ( bytesWritten < buf->getSize() ) {
+ bytesWritten += (*it)->write( buf->getByteArray() + bytesWritten,buf->getSize() - bytesWritten);
+ }
+ }
+ catch( const SocketException& e ) {
+ cout << "Could not write to socket. Error: " << e.getReason() << endl;
+ }
+ }
+ it++;
+ }
+ } else {
+ Socket* clientSocket = SocketManager::instance()->findSocket( socketId );
+
+ if( clientSocket ) {
+
+ if(cmdDispatcher->isClientAuthenticated(clientSocket->getId())) {
+ int bytesWritten = 0;
+ while ( bytesWritten < buf->getSize() ) {
+
+ bytesWritten += clientSocket->write( buf->getByteArray() + bytesWritten,
+ buf->getSize() - bytesWritten);
+ }
+ }
+ }
+ else {
+ throw RpcException("Could not find a socket to match id");
+ }
+ }
+}
+
+void RpcDriver::setClientAuthenticated( int clientId, bool yes )
+{
+ cmdDispatcher->setClientAuthenticated(clientId, yes);
+}
+
+void RpcDriver::enableAuthenticationChecking( bool yes )
+{
+ cmdDispatcher->enableAuthenticationChecking(yes);
+
+}
+
+}
diff --git a/rpcdriver/rpcdriver.h b/rpcdriver/rpcdriver.h
new file mode 100644
index 0000000..289d09a
--- /dev/null
+++ b/rpcdriver/rpcdriver.h
@@ -0,0 +1,228 @@
+
+#ifndef _RPC_DRIVER_H_
+#define _RPC_DRIVER_H_
+
+#include <boost/utility.hpp>
+#include <boost/shared_ptr.hpp>
+#include <boost/thread/condition.hpp>
+#include <boost/thread/mutex.hpp>
+#include <boost/thread/thread.hpp>
+
+#include <queue>
+#include <string>
+#include <list>
+
+#include "protectedbuffer.h"
+#include "rpccommandhandler.h"
+#include "commanddispatcher.h"
+
+#include "socketlistener.h"
+#include "inputbuffer.h"
+#include "outputbuffer.h"
+#include "dataoutputstream.h"
+
+
+namespace rpc {
+
+/**
+ *
+ */
+class RpcDriver :
+ public SocketListener,
+ private boost::noncopyable
+{
+public:
+
+ /**
+ * Create a server socket that listens for incoming connections
+ * on the specified port. We also create a command dispatcher.
+ * @param listeningPort Port that on wich we shold listen for incoming connections.
+ */
+ RpcDriver( );
+ virtual ~RpcDriver();
+
+ /**
+ * This is the entry point for the thread that listens for
+ * incomming data or connections. It will just keep on doing
+ * select on the sockets that we have created and when there
+ * is something available it will handle that request.
+ */
+ void receiver();
+
+ /**
+ * This is the entry point for the thread that does all the
+ * sending of commands. It will wait for an event to get signaled,
+ * and when it is it will check the queue of commands and send them
+ * all unti there is nothing more to send.
+ */
+ void sender();
+
+ /**
+ * Queues a command for sending and then signals the sender
+ * thread that there are things available in the queue.
+ * @param cmd The command that should be sent
+ */
+ void queueCommand( int socketId, const CmdPtr& cmd );
+
+ /**
+ * Register an class that implements the RpcCommandHandler interface
+ * with the driver. This will result in the class instance receiving
+ * notifications when a command that corresponds to the name it is
+ * registered for arrives from a client.
+ * @param pCmd Pointer to the command handler that is to be registered.
+ */
+ void registerCommand( RpcCommandHandlerPtr& pCmd );
+
+ /**
+ * Stop the sender thread. Do this by setting the abort member and
+ * signaling the thread that there is something available in the queue.
+ */
+ void stopSender();
+
+ /**
+ * This method will wait for all the threads to stop.
+ */
+ void waitForCompletion();
+
+ //////////////////
+ // Implement the SocketListener interface
+ //////////////////
+
+ /**
+ * Called when there is data available to read one of the sockets
+ * we are listening to.
+ * @param socket The socket that provides the data.
+ */
+ virtual void onRead( Socket* socket );
+
+
+ virtual void onWrite( Socket* ) {}
+ virtual void onConnect( Socket* ) {}
+
+ /**
+ * Called when a socket gets disconnected. When this happens we will
+ * remove it from the socket manager so that we dont select on it any more.
+ * @param socket The socket that has been disconnected.
+ */
+ virtual void onDisconnect( Socket* socket );
+
+ /**
+ * When there is a connection incoming. What we do is to accept the
+ * connection.
+ * @param socket The socket that has an incomming connection.
+ */
+ virtual void onIncoming( Socket* socket );
+
+ void enableAuthenticationChecking( bool yes );
+ void setClientAuthenticated( int clientId, bool yes );
+
+protected:
+
+ /**
+ * This method creates the neccesary threads for the server. it is
+ * needed since we can not create the threads in the ctor. This is because
+ * sockets and such might not be initialized at this point and we dont
+ * want to start listening on them if thats the case.
+ */
+ void startServer();
+
+ /// One of these entries sould be available for each socket we are listening to.
+ typedef struct SocketData {
+ boost::shared_ptr<InputBuffer<int> > sizeBuf;
+ CmdInputBufferPtr dataBuf;
+ } SocketData;
+
+ /// Map a socket to its data
+ typedef std::map< Socket*, SocketData> SocketDataMap;
+
+ SocketDataMap socketDataMap;
+
+private:
+
+ /// Used to queue commands in the rpc driver
+ typedef struct CmdEntry {
+ int socket;
+ CmdPtr cmd;
+ } CmdEntry;
+
+ /**
+ * Convert a list of boost::any to a DataOutputStream
+ */
+ OutputBufferPtr convertCmdToBinaryBuffer( CmdPtr cmd ) const;
+ void convertListToBinary(std::list< boost::any >::const_iterator cmdIt,
+ std::list< boost::any >::const_iterator itEnd,
+ DataOutputStream& stream) const;
+ /**
+ * Send the command to the client represented by the socket id.
+ * @param buf Binary buffer that contains the command.
+ * @param socketId Id of the socket/client this command should be sent to.
+ If this value is -1 it will be sent to all sockets/clients
+ */
+ void sendCommandToClients( OutputBufferPtr buf, int socketId ) const;
+
+ /// To handle the parsing if commands and dispatching them to the
+ /// commands that have registered.
+ CommandDispatcherPtr cmdDispatcher;
+
+ /// Mutex used in a condition to make the receiver thread wait
+ /// for data to arrive in the outgoing queue
+ boost::mutex commandMonitor;
+
+ /// Protect the abort flag
+ boost::mutex abortMutex;
+
+ /// Condition used to wait for data to be present in the command queue.
+ /// Used together with m_queueMonitor
+ boost::condition commandAvailable;
+
+ /// Queue of commands
+ ProtectedBuffer<CmdEntry> commandBuffer;
+
+ /// Thread that handles commands
+ boost::thread_group threads;
+
+ /// The sender thread
+ boost::thread* senderThread;
+
+ /// Set this to make the threads stop.
+ bool abort;
+
+
+};
+
+typedef boost::shared_ptr<RpcDriver> RpcDriverPtr;
+
+
+class RpcClientDriver : public RpcDriver
+{
+public:
+ /**
+ * Init parent class and try to connect to a server.
+ * @param addr Address of the server we should connect to.
+ * @param port Port that the server listens on.
+ */
+ RpcClientDriver( const char* addr, int port );
+ void connect();
+ virtual ~RpcClientDriver();
+
+private:
+ /// Client socket
+ Socket* clientSocket;
+ int serverPort;
+ const char* serverAddr;
+};
+
+class RpcServerDriver : public RpcDriver
+{
+public:
+ RpcServerDriver( int port );
+ void startListening();
+ virtual ~RpcServerDriver();
+private:
+ Socket* server;
+ int serverPort;
+};
+
+}
+
+#endif
diff --git a/rpcdriver/rpcexception.h b/rpcdriver/rpcexception.h
new file mode 100644
index 0000000..c4146e9
--- /dev/null
+++ b/rpcdriver/rpcexception.h
@@ -0,0 +1,37 @@
+/*
+ * exception.h
+ * RpcDriver
+ *
+ * Created by Mikael Gransell on 2/7/06.
+ * Copyright 2006 __MyCompanyName__. All rights reserved.
+ *
+ */
+
+#ifndef RPC_EXCEPTION_H_
+#define RPC_EXCEPTION_H_
+
+#include <string>
+
+namespace rpc {
+
+class RpcException
+{
+public:
+ RpcException( const char* r ) : reason( r ) { }
+
+ const char* getReason() const { return reason.c_str(); }
+
+private:
+ // The reason for the exception
+ const std::string reason;
+};
+
+class ClientAuthenticationException
+{
+public:
+ ClientAuthenticationException() {}
+};
+}
+
+#endif
+
diff --git a/rpcdriver/socket.cpp b/rpcdriver/socket.cpp
new file mode 100644
index 0000000..06d19cf
--- /dev/null
+++ b/rpcdriver/socket.cpp
@@ -0,0 +1,138 @@
+#include "socket.h"
+#include "socketlistener.h"
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/ip.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+# include <netinet/in.h>
+# include <netdb.h>
+
+#include <iostream>
+
+namespace rpc
+{
+
+ Socket::Socket(SocketListener* aListener) : listener(aListener), isServer(false), fd(-1), isConnected(false)
+{}
+
+
+ Socket::~Socket()
+ {}
+
+ void Socket::listen(int port) throw(SocketException)
+ {
+ fd = socket( PF_INET, SOCK_STREAM, 0 );
+ if(fd==-1)
+ throw SocketException("Could not create socket");
+ sockaddr_in saddr;
+ memset(&saddr, 0, sizeof(saddr));
+ saddr.sin_family = AF_INET;
+ saddr.sin_addr.s_addr = htonl(INADDR_ANY);
+ saddr.sin_port = htons((u_short) port);
+ int result = ::bind(fd, (struct sockaddr *)&saddr, sizeof(saddr));
+ if(result==-1)
+ throw SocketException("Could not bind to port");
+ result = ::listen(fd,5);
+ if(result==-1)
+ throw SocketException("Could not listen");
+ isServer = true;
+ // Turn on re-use
+ int i = 1;
+ setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,&i,sizeof(int))==0;
+ }
+
+ Socket* Socket::accept(SocketListener* sl) throw(SocketException)
+ {
+ int newfd = ::accept(fd,NULL,NULL);
+ if(newfd==-1)
+ throw SocketException("Could not accept");
+ Socket* s = new Socket(sl);
+ s->setFd(newfd);
+ s->isConnected = true;
+ return s;
+ }
+
+int Socket::read(void *buf,int len) throw(SocketException)
+{
+ int result = ::recv(fd,buf,len,0);
+ if(result==0)
+ {
+ disconnect();
+ listener->onDisconnect( this );
+ }
+ else
+ if(result==-1)
+ {
+ if(errno==EAGAIN)
+ return 0;
+
+ perror("socket::read: ");
+ throw SocketException(strerror(errno));
+ }
+ return result;
+}
+
+
+int Socket::write(const void *buf,int len) throw(SocketException)
+{
+ //std::cout << "Writing to fd: " << fd << std::endl;
+ int result = ::send(fd,buf,len,0);
+ if(result == -1) throw SocketException(strerror(errno));
+ return result;
+}
+
+void Socket::connect(const char* addr,int port)
+{
+ fd = socket(PF_INET, SOCK_STREAM, 0 );
+ struct sockaddr_in saddr;
+ memset(&saddr, 0, sizeof(saddr));
+ saddr.sin_family = AF_INET;
+
+ struct hostent *hp = gethostbyname(addr);
+ if (hp == 0)
+ throw SocketException("DNS lookup failed");
+
+ saddr.sin_family = hp->h_addrtype;
+ memcpy(&saddr.sin_addr, hp->h_addr, hp->h_length);
+ saddr.sin_port = htons((u_short) port);
+ int result = ::connect(fd, (struct sockaddr *)&saddr, sizeof(saddr));
+ if( result==-1 )
+ {
+ if( errno!=EAGAIN && errno!=EWOULDBLOCK)
+ throw SocketException( strerror(errno) );
+
+ }
+ isConnected = true;
+ listener->onConnect( this );
+
+}
+
+void Socket::disconnect()
+{
+ ::shutdown(fd,SHUT_RDWR);
+ close(fd);
+ fd=-1;
+ isConnected = false;
+}
+
+
+/**
+ * SocketManager calls the following methods.
+ *
+ */
+
+void Socket::onRead( )
+{
+ if(isServer)
+ listener->onIncoming(this);
+ else
+ listener->onRead(this);
+}
+
+}
+
+
diff --git a/rpcdriver/socket.h b/rpcdriver/socket.h
new file mode 100644
index 0000000..5369b0a
--- /dev/null
+++ b/rpcdriver/socket.h
@@ -0,0 +1,53 @@
+#ifndef RPCSOCKET_H
+#define RPCSOCKET_H
+
+namespace rpc {
+
+ class SocketException
+{
+public:
+ SocketException(char* aReason) : reason(aReason) {}
+ char* getReason() const {return reason;}
+private:
+ char* reason;
+};
+
+class SocketListener;
+
+/**
+@author Rikard Bjorklind <olof@linux.nu>
+ */
+class Socket{
+public:
+
+ Socket(SocketListener*);
+
+ // Interface for mortals
+
+ void listen(int port) throw(SocketException);
+ Socket* accept(SocketListener*) throw(SocketException);
+ int read(void* buf,int len) throw(SocketException);
+ int write(const void *buf,int len) throw(SocketException);
+ void connect(const char* addr,int port);
+ void disconnect();
+ int getFd() {return fd;}
+ void setFd(int f) {fd=f;}
+ int getId() {return fd;}
+ bool getIsServer() const { return isServer; }
+
+ // SocketManager interface - never call these
+
+ //! Called by the socketmanager when select() indicates readable
+ void onRead();
+
+ ~Socket();
+private:
+ SocketListener* listener;
+ bool isServer;
+ bool isConnected;
+ int fd;
+};
+
+}
+
+#endif
diff --git a/rpcdriver/socketlistener.h b/rpcdriver/socketlistener.h
new file mode 100644
index 0000000..948f02e
--- /dev/null
+++ b/rpcdriver/socketlistener.h
@@ -0,0 +1,26 @@
+#ifndef RPCSOCKETLISTENER_H
+#define RPCSOCKETLISTENER_H
+
+
+namespace rpc {
+
+class Socket;
+
+/**
+ @author Rikard Bjorklind <olof@linux.nu>
+*/
+class SocketListener{
+public:
+ virtual ~SocketListener() {}
+ virtual void onRead(Socket*) = 0;
+// virtual void onWrite(Socket*) = 0;
+ //! Called as soon as a connection is established. Do not do anything important in this method, especially dont do anything with the socket emitting it!
+ virtual void onConnect(Socket*) = 0;
+ virtual void onDisconnect(Socket*) = 0;
+ virtual void onIncoming(Socket*) {} // not pure since we only use it for servers.
+
+};
+
+}
+
+#endif
diff --git a/rpcdriver/socketmanager.cpp b/rpcdriver/socketmanager.cpp
new file mode 100644
index 0000000..f7ed000
--- /dev/null
+++ b/rpcdriver/socketmanager.cpp
@@ -0,0 +1,72 @@
+#include "socketmanager.h"
+
+
+using namespace std;
+
+namespace rpc {
+
+SocketManager* SocketManager::inst = 0;
+
+SocketManager::SocketManager()
+{
+}
+
+SocketManager::~SocketManager()
+{
+}
+
+void SocketManager::addSocket( Socket *s )
+{
+ sockets.insert(s);
+}
+
+void SocketManager::removeSocket( Socket *s )
+{
+ sockets.erase(s);
+}
+
+void SocketManager::select( )
+{
+ set<Socket*> sl;
+ fd_set fds;
+ FD_ZERO(&fds);
+ int maxfd = -1;
+ set<Socket*>::iterator it = sockets.begin();
+ while(it!=sockets.end()) {
+ int fd = (*it)->getFd();
+ FD_SET(fd,&fds);
+ if(maxfd < fd) maxfd = fd;
+ sl.insert(*it);
+ it++;
+ }
+ int nEvents = ::select( maxfd+1, &fds,NULL,NULL,NULL );
+ if(nEvents>0) {
+
+ for(it = sl.begin();it!=sl.end();it++) {
+ int fd = (*it)->getFd();
+ if(FD_ISSET(fd,&fds)) (*it)->onRead();
+ }
+
+ } else if(nEvents==-1) {
+ perror("select():");
+ }
+}
+
+Socket* SocketManager::findSocket( int socketId )
+{
+ Socket* ret = NULL;
+
+ std::set<Socket*>::const_iterator socketIt = sockets.begin();
+ while( socketIt != sockets.end() ) {
+
+ if( (*socketIt)->getId() == socketId ) {
+ ret = *socketIt;
+ break;
+ }
+ ++socketIt;
+ }
+ return ret;
+}
+
+} // namespace
+
diff --git a/rpcdriver/socketmanager.h b/rpcdriver/socketmanager.h
new file mode 100644
index 0000000..5a9f5d3
--- /dev/null
+++ b/rpcdriver/socketmanager.h
@@ -0,0 +1,57 @@
+#ifndef RPCSOCKETMANAGER_H
+#define RPCSOCKETMANAGER_H
+
+#include <set>
+
+#include "socket.h"
+
+namespace rpc {
+
+/**
+ @author Rikard Bjorklind <olof@linux.nu>
+*/
+class SocketManager{
+public:
+ ~SocketManager();
+
+ /**
+ * Add the socket to our internal set.
+ */
+ void addSocket(Socket*);
+
+ /**
+ * Remove the specified socket from our internal set.
+ */
+ void removeSocket(Socket*);
+
+ /**
+ * Perform a select on all the sockets that we are managing. This call
+ * blocks until something is available to read.
+ */
+ void select();
+
+ static SocketManager* instance() {if(!inst) inst=new SocketManager;return inst;}
+
+ const std::set<Socket*>& getSockets() const { return sockets; }
+
+ Socket* findSocket( int socketId );
+
+private:
+ /**
+ * Singleton has private ctor so that it can only be accessed by
+ * the instance method.
+ */
+ SocketManager();
+
+ /// One and only instance of the socketmanager
+ static SocketManager* inst;
+
+ /// The sockets that we are managing
+ std::set<Socket*> sockets;
+};
+
+}
+
+#define g_SocketManager SocketManager::instance()
+
+#endif
diff --git a/rpcdriver/types.h b/rpcdriver/types.h
new file mode 100644
index 0000000..f030264
--- /dev/null
+++ b/rpcdriver/types.h
@@ -0,0 +1,22 @@
+#ifndef types_h__
+#define types_h__
+#include <boost/cstdint.hpp>
+typedef boost::int64_t int64;
+typedef int int32;
+typedef short int16;
+typedef char int8;
+
+/// The different types of parameter types
+enum eRpcParamType {
+
+ eRpcParamTypeInt = 1,
+ eRpcParamTypeByte,
+ eRpcParamTypeShort,
+ eRpcParamTypeBoolean,
+ eRpcParamTypeLong,
+ eRpcParamTypeString,
+ eRpcParamTypeList
+};
+
+#endif
+
diff --git a/ui/backendconnection.cpp b/ui/backendconnection.cpp
new file mode 100644
index 0000000..51a17f9
--- /dev/null
+++ b/ui/backendconnection.cpp
@@ -0,0 +1,285 @@
+#include <string>
+#include <iostream>
+#include <boost/any.hpp>
+#include <QPair>
+#include <QVariant>
+
+#include "backendconnection.h"
+#include "log.h"
+#include "searchentry.h"
+#include "util.h"
+#include <rpcdriver/types.h>
+
+using namespace std;
+using namespace rpc;
+
+void BackendConnection::authenticate( const QString & password )
+{
+ boost::any p = password.toStdString();
+ CmdPtr cmd(new anylist);
+ cmd->push_back( string("authenticate") );
+ cmd->push_back(p);
+ driver->queueCommand(-1,cmd);
+}
+
+void BackendConnection::createSession( const QString& u )
+{
+ boost::any url = u.toStdString();
+ CmdPtr cmd(new anylist);
+ cmd->push_back( string("createSession") );
+ cmd->push_back(url);
+ driver->queueCommand(-1,cmd);
+}
+
+void BackendConnection::sendChat(int session,const QString& msg)
+{
+ CmdPtr cmd(new anylist);
+ cmd->push_back( string("sendChat") );
+ cmd->push_back( session );
+ cmd->push_back( msg.toStdString() );
+ driver->queueCommand(-1,cmd);
+}
+
+void BackendConnection::sendSearch(int session, int64 size, int sizeMode, int typeMode, const QString& search)
+{
+ CmdPtr cmd(new anylist);
+ cmd->push_back( string("search") );
+ cmd->push_back( session );
+ cmd->push_back( search.toStdString() );
+ cmd->push_back( size );
+ cmd->push_back( sizeMode );
+ cmd->push_back( typeMode );
+ driver->queueCommand(-1,cmd);
+}
+
+void BackendConnection::getSharedDirs()
+{
+ CmdPtr cmd(new anylist);
+ cmd->push_back( string("getSharedDirectories") );
+ driver->queueCommand(-1,cmd);
+}
+
+void BackendConnection::addShare(const QString& name,const QString& dir)
+{
+ CmdPtr cmd(new anylist);
+ cmd->push_back( string("addShare") );
+ cmd->push_back(name.toStdString());
+ cmd->push_back(dir.toStdString());
+ driver->queueCommand(-1,cmd);
+}
+
+void BackendConnection::delShare(const QString& name)
+{
+ CmdPtr cmd(new anylist);
+ cmd->push_back( string("removeShare") );
+ cmd->push_back(name.toStdString());
+ driver->queueCommand(-1,cmd);
+}
+
+void BackendConnection::requestRunningSessions()
+{
+ CmdPtr cmd(new anylist);
+ cmd->push_back( string("requestRunningSessions") );
+ driver->queueCommand(-1,cmd);
+}
+
+void BackendConnection::downloadFile(const SearchEntry& file)
+{
+ logger->debug("Downloading " + file.getPath() + file.getFileName());
+ CmdPtr cmd(new anylist);
+ cmd->push_back( string("downloadFile") );
+ cmd->push_back( file.getFile() );
+ cmd->push_back( file.getType() == SearchEntry::DIRECTORY ? (int64)-1 : file.getFileSize() );
+ cmd->push_back( file.getUserId() );
+ cmd->push_back( string("") );
+ cmd->push_back( file.getTTH().toStdString() );
+ driver->queueCommand(-1,cmd);
+}
+
+void BackendConnection::downloadFile( int uId, const QString& file, int64 size, const QString& tth )
+{
+ logger->debug( "Downloading " + file );
+ CmdPtr cmd(new anylist);
+ cmd->push_back( string("downloadFile") );
+ cmd->push_back( file.toStdString() );
+ cmd->push_back( size );
+ cmd->push_back( uId );
+ cmd->push_back( string("") );
+ cmd->push_back( tth.toStdString() );
+ driver->queueCommand(-1,cmd);
+}
+
+
+void BackendConnection::downloadFileTo(const SearchEntry& file, const QString& dir )
+{
+ logger->debug("Downloading " + file.getPath() + file.getFileName());
+ CmdPtr cmd(new anylist);
+ cmd->push_back( string("downloadFile") );
+ cmd->push_back( file.getFile() );
+ cmd->push_back( file.getType() == SearchEntry::DIRECTORY ? -1 : file.getFileSize() );
+ cmd->push_back( file.getUserId() );
+ cmd->push_back(dir.toStdString());
+ cmd->push_back( file.getTTH().toStdString() );
+ driver->queueCommand(-1,cmd);
+}
+
+void BackendConnection::downloadFileTo( int uId, const QString& file, int64 size, const QString& tth, const QString& dir )
+{
+ logger->debug( "Downloading " + file + " to " + dir );
+ CmdPtr cmd(new anylist);
+ cmd->push_back( string("downloadFile") );
+ cmd->push_back( file.toStdString() );
+ cmd->push_back( size );
+ cmd->push_back( uId );
+ cmd->push_back( dir.toStdString() );
+ cmd->push_back( tth.toStdString() );
+ driver->queueCommand(-1,cmd);
+}
+
+
+void BackendConnection::getUserFileList( int userId )
+{
+ logger->debug( "Requesting file list for user with id:" + QString::number(userId) );
+ CmdPtr cmd(new anylist);
+ cmd->push_back( string("getUserFileList") );
+ cmd->push_back( userId );
+ driver->queueCommand(-1,cmd);
+}
+
+void BackendConnection::tellBackendToDie()
+{
+ CmdPtr cmd(new anylist);
+ cmd->push_back( string("die") );
+ driver->queueCommand(-1,cmd);
+}
+
+void BackendConnection::getHubList( bool refresh )
+{
+ CmdPtr cmd(new anylist);
+ cmd->push_back( string("getHubList") );
+ cmd->push_back( refresh );
+ driver->queueCommand(-1,cmd);
+}
+
+
+void BackendConnection::getFavouriteHubs( )
+{
+ CmdPtr cmd(new anylist);
+ cmd->push_back( string("getFavouriteHubs") );
+ driver->queueCommand(-1,cmd);
+}
+
+void BackendConnection::addFavouriteHub( const rpc_types::FavouriteHub &e )
+{
+ CmdPtr cmd(new anylist);
+ cmd->push_back( string("addFavouriteHub") );
+ cmd->push_back( e.nick );
+ cmd->push_back( e.userDescription );
+ cmd->push_back( e.name );
+ cmd->push_back( e.server );
+ cmd->push_back( e.description );
+ cmd->push_back( e.password );
+ cmd->push_back( e.autoConnect );
+ driver->queueCommand(-1,cmd);
+}
+
+void BackendConnection::removeFavouriteHub( const QString &server )
+{
+ CmdPtr cmd(new anylist);
+ cmd->push_back( string("removeFavouriteHub") );
+ cmd->push_back( server.toStdString() );
+ driver->queueCommand(-1,cmd);
+}
+
+void BackendConnection::closeSession( int session )
+{
+ CmdPtr cmd(new anylist);
+ cmd->push_back( string("closeSession") );
+ cmd->push_back( session );
+ driver->queueCommand(-1,cmd);
+}
+
+void BackendConnection::getSettings( QList<QString> keys )
+{
+ CmdPtr cmd(new anylist);
+ cmd->push_back( string("getSettings") );
+ // convert the qlist to an anylist
+ anylist k;
+ for(int i=0;i<keys.size();i++)
+ k.push_back(keys[i].toStdString());
+ cmd->push_back(k);
+ driver->queueCommand(-1,cmd);
+}
+
+void BackendConnection::setSettings(QList<QPair<QString,QVariant> > settings)
+{
+ CmdPtr cmd(new anylist);
+ cmd->push_back( string("setSettings") );
+ anylist keys,values;
+
+ for(int i=0;i<settings.size();i++)
+ {
+
+ keys.push_back(settings[i].first.toStdString());
+ if(settings[i].second.type()==QVariant::String)
+ values.push_back( settings[i].second.toString().toStdString() );
+ else if(settings[i].second.type()==QVariant::Int)
+ values.push_back( settings[i].second.toInt() );
+ else if(settings[i].second.type()==QVariant::LongLong)
+ values.push_back( settings[i].second.toLongLong() );
+ }
+
+ cmd->push_back(keys);
+ cmd->push_back(values);
+ driver->queueCommand(-1,cmd);
+}
+
+void BackendConnection::sendPassword( int session, const QString& pass )
+{
+ CmdPtr cmd(new anylist);
+ cmd->push_back( string("sendPassword") );
+ cmd->push_back( session );
+ cmd->push_back( pass.toStdString() );
+ driver->queueCommand(-1,cmd);
+}
+
+void BackendConnection::removeQueueItem( int id )
+{
+ logger->info(QString("Removing QueueItem with id %1").arg(id));
+ CmdPtr cmd(new anylist);
+ cmd->push_back( string("removeQueueItem") );
+ cmd->push_back( id );
+ driver->queueCommand(-1,cmd);
+}
+
+void BackendConnection::forceConnectionAttempt( int userId )
+{
+ logger->info(QString("Forcing attempt on user with id %1").arg(userId));
+ CmdPtr cmd(new anylist);
+ cmd->push_back( string("connectToUser") );
+ cmd->push_back( userId );
+ driver->queueCommand(-1,cmd);
+}
+
+void BackendConnection::die( )
+{
+ logger->info("BackendConnection::die()");
+ CmdPtr cmd(new anylist);
+ cmd->push_back( string("die") );
+ driver->queueCommand(-1,cmd);
+}
+
+BackendConnection::~ BackendConnection( )
+{
+ logger->info("Destructing backendconnection");
+}
+
+void BackendConnection::removeSource( int qid, int uid )
+{
+ logger->debug("BackendConnection::removeSource: " + QString::number( qid ) + ", " + QString::number( uid ));
+ CmdPtr cmd(new anylist);
+ cmd->push_back( string("removeSource") );
+ cmd->push_back( qid );
+ cmd->push_back( uid );
+ driver->queueCommand(-1,cmd);
+}
diff --git a/ui/backendconnection.h b/ui/backendconnection.h
new file mode 100644
index 0000000..37fafd2
--- /dev/null
+++ b/ui/backendconnection.h
@@ -0,0 +1,73 @@
+//
+// C++ Interface: backendconnection
+//
+//
+// Author: rilleb and mickyg
+//
+// Copyright: See COPYING file that comes with this distribution
+//
+//
+#ifndef BACKENDCONNECTION_H
+#define BACKENDCONNECTION_H
+#include <string>
+#include <QObject>
+#include <QVariant>
+#include <QPair>
+#include "global.h"
+#include <boost/shared_ptr.hpp>
+#include <rpcdriver.h>
+#include "rpctypes.h"
+
+class SearchEntry;
+
+/**
+ Simply the RPC interface.
+ Converts ordinary method calls to RPC calls which are sent to the other side.
+ BIG NOTE: The RPC Driver does not accept values of type int64. Therefore, use int64
+ from rpcdriver/types.h (this applies to x86_64 only).
+*/
+class BackendConnection : public QObject
+{
+Q_OBJECT
+
+public:
+ BackendConnection(boost::shared_ptr< rpc::RpcClientDriver > aDriver) : driver(aDriver) {}
+ ~BackendConnection();
+
+public slots:
+
+ void authenticate( const QString& password );
+ void createSession( const QString& url );
+ void getSharedDirs();
+ void addShare(const QString& name,const QString& dir);
+ void delShare(const QString& name);
+ void getHubList(bool refresh);
+ void getFavouriteHubs();
+ void getSettings(QList<QString> keys);
+ void setSettings(QList<QPair<QString,QVariant> > );
+ void sendChat(int session,const QString& msg);
+ void sendSearch(int session, int64 size, int sizeMode, int typeMode, const QString& search);
+ void requestRunningSessions();
+ void downloadFile( const SearchEntry& file );
+ void downloadFileTo( const SearchEntry& file, const QString& dir );
+ void downloadFile( int uId, const QString& file, int64 size, const QString& tth );
+ void downloadFileTo( int uId, const QString& file, int64 size, const QString& tth, const QString& dir );
+ void tellBackendToDie();
+ void getUserFileList( int userId );
+ void addFavouriteHub(const rpc_types::FavouriteHub&);
+ void removeFavouriteHub(const QString& server);
+ void closeSession( int session );
+ void sendPassword( int session, const QString& );
+ void removeQueueItem(int id);
+ void forceConnectionAttempt(int userId);
+ void removeSource(int qid, int uid);
+ void die();
+
+private:
+ boost::shared_ptr< rpc::RpcClientDriver > driver;
+};
+
+typedef boost::shared_ptr<BackendConnection> BackendConnectionPtr;
+
+#endif
+
diff --git a/ui/blockallocator.h b/ui/blockallocator.h
new file mode 100644
index 0000000..6b3d048
--- /dev/null
+++ b/ui/blockallocator.h
@@ -0,0 +1,102 @@
+#ifndef __blockallocator_h__
+#define __blockallocator_h__
+
+#include <vector>
+#include <iostream>
+
+using namespace std;
+
+typedef unsigned char byte;
+
+//! Custom memory allocator for single items
+template <class T, size_t blocksPerBatch=100, size_t blockAlignment=4>
+class BlockAllocator
+{
+public:
+ void* operator new(size_t)
+ {
+ //cerr << "blocknew\n";
+ return s_Store.AllocateBlock();
+ }
+
+ void operator delete(void* pBlock)
+ {
+ //cerr << "blockdelete" << endl;
+ s_Store.ReleaseBlock((T*)pBlock);
+ }
+
+ static T* AllocateBlock()
+ {
+ return s_Store.AllocateBlock();
+ }
+ static void ReleaseBlock(T* pBlock)
+ {
+ s_Store.ReleaseBlock(pBlock);
+ }
+
+private:
+
+ struct BlockStore
+ {
+ BlockStore() : ppNextBlock(0)
+ {}
+ ;
+ ~BlockStore()
+ {
+ size_t iNum = batches.size();
+ for (size_t i=0; i<iNum; ++i)
+ {
+ byte* p = (byte*)batches[i];
+ delete [] p;
+ }
+ }
+
+ T* AllocateBlock()
+ {
+ if (!ppNextBlock || !*ppNextBlock)
+ {
+ // determine the allligned size of the blocks
+ static const size_t blockSize = (sizeof(T)+blockAlignment-1)&(~(blockAlignment-1));
+
+ // make a new batch
+ byte *pNewBatch = new byte[blocksPerBatch*blockSize+15];
+ batches.push_back(pNewBatch);
+
+ //* Align the block on a 16-byte boundary
+ byte* pAlignedPtr =(byte*)((uintptr_t)(pNewBatch+15)&(~15));
+
+ // fill the pointers with the new blocks
+ ppNextBlock = (byte**)pAlignedPtr;
+ for (int i=0; i<blocksPerBatch-1; ++i)
+ {
+ *((uintptr_t*)(pAlignedPtr + i*blockSize)) = (uintptr_t)(pAlignedPtr + (i+1)*blockSize);
+ }
+ *((uintptr_t*)(pAlignedPtr + (blocksPerBatch-1)*blockSize)) = (uintptr_t)0;
+ }
+
+ byte* pBlock = (byte*)ppNextBlock;
+ ppNextBlock = (byte**)*ppNextBlock;
+ return (T*)pBlock;
+ }
+
+ void ReleaseBlock(T* pBlock)
+ {
+ if(pBlock)
+ {
+ *((uintptr_t*)pBlock) = (uintptr_t)ppNextBlock;
+ ppNextBlock = (byte**)((byte*)pBlock);
+ }
+ }
+
+ typedef vector<byte*> BatchPtrVector;
+
+ byte** ppNextBlock; // Pointer to the next available block pointer
+ BatchPtrVector batches; // Array of pointers to batches
+ };
+
+
+ static BlockStore s_Store;
+};
+
+#endif
+
diff --git a/ui/commandhandlers.cpp b/ui/commandhandlers.cpp
new file mode 100644
index 0000000..c453cb9
--- /dev/null
+++ b/ui/commandhandlers.cpp
@@ -0,0 +1,807 @@
+/*! \file commandhandlers.cpp
+ \brief Implements the exposed RPC methods.
+
+ This file contains implementations of the handleCommand() methods of the RPC handler classes.
+ */
+
+#include "commandhandlers.h"
+#include "global.h"
+#include "log.h"
+#include "user.h"
+#include "shareitemmodel.h"
+#include "finisheditem.h"
+
+#include <QThread>
+#include <iostream>
+#include <rpcdriver/types.h>
+using namespace std;
+using namespace boost;
+
+namespace ui_cmd_handlers
+{
+
+using std::list;
+
+/**
+ * Parses an anylist of user structs and returns a QList which contains user pointers.
+ *
+ */
+QList<User*> parseUserList(const anylist& users)
+{
+ anylist::const_iterator uit = users.begin();
+ QList<User*> parsedUsers;
+
+ for(;uit!=users.end();uit++)
+ {
+ User* user = new User; // Fast, not slow! :)
+ anylist userprops = boost::any_cast<anylist>(*uit);
+ anylist::const_iterator pit = userprops.begin();
+ user->id = boost::any_cast<int>(*pit);
+ pit++;
+ user->nick = boost::any_cast<string>(*pit).c_str();
+ pit++;
+ user->flags = any_cast<int>(*pit);
+ pit++;
+ user->email = any_cast<string>(*pit).c_str();
+ pit++;
+ user->description = any_cast<string>(*pit).c_str();
+ pit++;
+ user->connection = any_cast<string>(*pit).c_str();
+ pit++;
+ user->tag = any_cast<string>(*pit).c_str();
+ pit++;
+ user->shared = any_cast<int64>(*pit);
+ pit++;
+ user->numSlots = any_cast<int>(*pit);
+ parsedUsers.append(user);
+ }
+
+ return parsedUsers;
+}
+
+void HubConnectedHandler::handleCommand(const anylist& params)
+{
+ try
+ {
+ logger->info("hubConnected!");
+ int sessionId = boost::any_cast<int>(params.front());
+ emit hubConnected(sessionId);
+ }
+ catch( const boost::bad_any_cast& e )
+ {}
+}
+
+void HubUpdatedHandler::handleCommand(const anylist& params)
+{
+ try
+ {
+ logger->info("hubUpdated!");
+ anylist::const_iterator it = params.begin();
+ int sessionId = boost::any_cast<int>(*it);
+ it++;
+ QString hubName = (boost::any_cast<string>(*it)).c_str();
+
+ emit hubUpdated(sessionId,hubName);
+ }
+ catch( const boost::bad_any_cast& e )
+ {}
+}
+
+void HubStatsHandler::handleCommand(const anylist& params)
+{
+ try
+ {
+ anylist::const_iterator it = params.begin();
+ int sessionId = boost::any_cast<int>(*it);
+ it++;
+ int64 shared = boost::any_cast<int64>(*it);
+
+ emit hubStats(sessionId,shared);
+ }
+ catch( const boost::bad_any_cast& e )
+ {
+ throw RpcHandlerException("HubStatsHandler","Bad AnyCast");
+ }
+}
+
+
+void ConnectionFailedHandler::handleCommand(const anylist& params)
+{
+ try
+ {
+ logger->info("connectionFailed!");
+ anylist::const_iterator it = params.begin();
+ int sessionId = boost::any_cast<int>(*it);
+ it++;
+ QString reason = (boost::any_cast<string>(*it)).c_str();
+
+ emit connectionFailed(sessionId,reason);
+ }
+ catch( const boost::bad_any_cast& e )
+ {}
+}
+
+void PrivateChatHandler::handleCommand( const anylist& params)
+{
+ try
+ {
+ logger->debug("privateChat!");
+ anylist::const_iterator it = params.begin();
+ int sessionId = boost::any_cast<int>(*it);
+ it++;
+ QString from = (boost::any_cast<string>(*it)).c_str();
+ it++;
+ QString msg((boost::any_cast<string>(*it)).c_str());
+ emit privateChat(sessionId,from,msg);
+ }
+ catch( const boost::bad_any_cast& e )
+ {}
+
+}
+
+void ChatMessageHandler::handleCommand( const anylist& params)
+{
+ try
+ {
+ logger->debug("publicChat!");
+ anylist::const_iterator it = params.begin();
+ int sessionId = boost::any_cast<int>(*it);
+ it++;
+ QString msg = (boost::any_cast<string>(*it)).c_str();
+ emit chatMessage(sessionId,msg);
+ }
+ catch( const boost::bad_any_cast& e )
+ {
+ logger->error("bad anycast in chatmessage");
+ }
+
+}
+
+void UsersUpdatedHandler::handleCommand( const anylist& params)
+{
+ try
+ {
+ QList<User*> parsedUsers;
+
+ //logger->debug("usersUpdated!");
+ anylist::const_iterator it = params.begin();
+ int sessionId = boost::any_cast<int>(*it);
+ it++;
+
+ // ................................................................
+ // TODO: This will result in a deep copy of the entire list??!!
+ // We need to solve it!
+ anylist users = boost::any_cast<anylist>(*it);
+
+ /*anylist::const_iterator uit = users.begin();
+
+ for(;uit!=users.end();uit++)
+ {
+ User* user = new User; // Fast, not slow! :)
+ anylist userprops = boost::any_cast<anylist>(*uit);
+ anylist::const_iterator pit = userprops.begin();
+ user->id = boost::any_cast<int>(*pit); pit++;
+ user->nick = boost::any_cast<string>(*pit).c_str(); pit++;
+ user->flags = any_cast<int>(*pit);pit++;
+ user->email = any_cast<string>(*pit).c_str();pit++;
+ user->description = any_cast<string>(*pit).c_str();pit++;
+ user->connection = any_cast<string>(*pit).c_str();pit++;
+ user->tag = any_cast<string>(*pit).c_str(); pit++;
+ user->shared = any_cast<int64>(*pit); pit++;
+ user->numSlots = any_cast<int>(*pit);
+ parsedUsers.append(user);
+ }*/
+ parsedUsers = parseUserList(users);
+
+ emit usersUpdated( sessionId, parsedUsers );
+
+ }
+ catch( const boost::bad_any_cast& e )
+ {
+ logger->error("badanycast in usersupdated.");
+ }
+
+}
+
+
+
+void settingsInfoHandler::handleCommand(const anylist& params)
+{
+ try
+ {
+ QList<QString> keys;
+ QList<QVariant> values;
+ anylist::const_iterator pi = params.begin();
+ const anylist& k = boost::any_cast<anylist>(*pi);
+ pi++;
+ const anylist& v = boost::any_cast<anylist>(*pi);
+
+ anylist::const_iterator ki = k.begin();
+ while(ki!=k.end())
+ {
+ keys.append(QString(boost::any_cast<string>(*ki).c_str()));
+ ki++;
+ }
+ anylist::const_iterator vi = v.begin();
+ while(vi!=v.end())
+ {
+ if((*vi).type()==typeid(string))
+ values.append(QString(boost::any_cast<string>(*vi).c_str()));
+ else if((*vi).type()==typeid(int))
+ values.append(boost::any_cast<int>(*vi));
+ else if((*vi).type()==typeid(bool))
+ values.append(boost::any_cast<bool>(*vi));
+ else if((*vi).type()==typeid(int64))
+ values.append((qlonglong)boost::any_cast<int64>(*vi));
+ vi++;
+ }
+ emit settingsInfo( keys, values ) ;
+ }
+ catch (const boost::bad_any_cast& e)
+ {
+
+ cout << "bad any cast" << endl;
+ }
+ catch (...)
+ {
+ cout << "exception" << endl;
+ }
+}
+
+void PublicHubListHandler::handleCommand( const anylist& params )
+{
+ try {
+ // The parameters should consist of a list of lists.
+ // Iterate through the list and cast each item to a list which
+ // is treated as a hub entry and converted.
+
+ QList<rpc_types::HubEntry> hubEntries;
+ const anylist& hubs = boost::any_cast<const anylist&>(params.front());
+ anylist::const_iterator hubsIt = hubs.begin();
+ while(hubsIt != hubs.end()) {
+
+ const anylist& hub = boost::any_cast<const anylist&>(*hubsIt);
+
+ if( hub.size() == rpc_types::HubEntry::HUB_ENTRY_SIZE ) {
+
+ anylist::const_iterator hubIt = hub.begin();
+ rpc_types::HubEntry hubEntry;
+ hubEntry.name = boost::any_cast<string>(*hubIt);
+ ++hubIt;
+ hubEntry.server = boost::any_cast<string>(*hubIt);
+ ++hubIt;
+ hubEntry.description = boost::any_cast<string>(*hubIt);
+ ++hubIt;
+ hubEntry.country = boost::any_cast<string>(*hubIt);
+ ++hubIt;
+ hubEntry.rating = boost::any_cast<string>(*hubIt);
+ ++hubIt;
+ hubEntry.reliability = boost::any_cast<int>(*hubIt);
+ ++hubIt;
+ hubEntry.shared = boost::any_cast<int64>(*hubIt);
+ ++hubIt;
+ hubEntry.minShare = boost::any_cast<int64>(*hubIt);
+ ++hubIt;
+ hubEntry.users = boost::any_cast<int>(*hubIt);
+ ++hubIt;
+ hubEntry.minSlots = boost::any_cast<int>(*hubIt);
+ ++hubIt;
+ hubEntry.maxHubs = boost::any_cast<int>(*hubIt);
+ ++hubIt;
+ hubEntry.maxUsers = boost::any_cast<int>(*hubIt);
+
+ hubEntries.append(hubEntry);
+ }
+
+ ++hubsIt;
+ }
+
+ emit hubList(hubEntries);
+ }
+ catch( const boost::bad_any_cast& e ){
+ logger->error(e.what());
+ throw RpcHandlerException("HubList","Bad AnyCast");
+ }
+}
+
+void SessionCreatedHandler::handleCommand(const anylist& params)
+{
+ try
+ {
+ logger->info("SessionCreated");
+ int sessionId = boost::any_cast<int>(params.front());
+ emit sessionCreated(sessionId);
+ }
+ catch( const boost::bad_any_cast& e )
+ {
+ logger->error("sessioncreated bad anycast");
+ }
+}
+
+void RunningSessionsHandler::handleCommand( const anylist& params)
+{
+ try
+ {
+ logger->debug("runningSessions!");
+ anylist::const_iterator it = params.begin();
+
+ anylist sessions = any_cast<anylist>(*it);
+ it++;
+ anylist queue = any_cast<anylist>(*it);
+
+ // Parse the sessions list
+ anylist::const_iterator sessionIt = sessions.begin();
+ for(;sessionIt!=sessions.end();sessionIt++)
+ {
+ // Each element in the sessions list is a SessionInfo struct, which is sent as a list of course.
+ anylist si = any_cast<anylist>(*sessionIt);
+ anylist::const_iterator infoIt = si.begin();
+ int sessionId = boost::any_cast<int>(*infoIt);
+ infoIt++;
+ QString hubName = (boost::any_cast<string>(*infoIt)).c_str();
+ infoIt++;
+ QString url = (boost::any_cast<string>(*infoIt)).c_str();
+ infoIt++;
+ // Now comes the list of users
+ anylist userlist = any_cast<anylist>(*infoIt);
+ QList<User*> parsedUsers = parseUserList(userlist);
+ emit sessionInfo( sessionId,hubName,url,parsedUsers );
+ }
+
+ QList<QueueItem> queueItemList;
+
+ for(anylist::const_iterator qlit=queue.begin();qlit!=queue.end();qlit++)
+ {
+ anylist qitem = any_cast<anylist>(*qlit);
+ anylist::const_iterator qit = qitem.begin();
+ QueueItem qi;
+ qi.id = any_cast<int>(*qit);
+ qit++;
+ qi.target = any_cast<string>(*qit).c_str();
+ qit++;
+ qi.size = any_cast<int64>(*qit);
+ qit++;
+ qi.downloadedBytes = any_cast<int64>(*qit);
+ qit++;
+ qi.status = any_cast<int>(*qit);
+ qit++;
+ qi.priority = any_cast<int>(*qit);
+ qit++;
+ qi.currentSource = any_cast<int>(*qit);
+ qit++;
+ if(qit!=qitem.end()) {
+ anylist sources = any_cast<anylist>(*qit);
+ anylist::const_iterator slit = sources.begin();
+ while(slit!=sources.end())
+ {
+ qi.sources += any_cast<int>(*slit);
+ slit++;
+ }
+
+ }
+ queueItemList+=qi;
+ }
+ emit queueList(queueItemList);
+
+ }
+ catch( const boost::bad_any_cast& e )
+ {
+ logger->error("bad anycast in runningSessions");
+ throw RpcHandlerException("runningSessions","Bad anycast");
+ }
+
+}
+
+void SharedDirsHandler::handleCommand( const anylist & params )
+{
+ try
+ {
+ logger->debug("sharedDirs");
+ anylist dirs = any_cast<anylist>(params.front());
+ anylist::const_iterator it = dirs.begin();
+ QList<ShareItem> shares;
+
+ for(;it!=dirs.end();it++)
+ {
+ anylist item = any_cast<anylist>(*it);
+ anylist::iterator sit = item.begin();
+ QString virtualName((any_cast<string>(*sit)).c_str());
+ sit++;
+ QString realName((any_cast<string>(*sit)).c_str());
+ sit++;
+ int64 bytesShared(any_cast<int64>(*sit));
+ ShareItem shareItem(virtualName,realName,bytesShared);
+ shares+=shareItem;
+ }
+ emit sharedDirs( shares );
+ }
+ catch( const boost::bad_any_cast& e )
+ {
+ logger->error("shareddirs bad anycast");
+ }
+}
+
+void SearchResultsHandler::handleCommand( const anylist & params )
+{
+ try
+ {
+ QList<SearchEntry> results;
+
+ logger->debug("searchResults");
+ anylist::const_iterator it = params.begin();
+ int session = any_cast<int>(*it);
+ it++;
+ anylist srs = any_cast<anylist>(*it);
+
+ for(it=srs.begin();it!=srs.end();it++)
+ {
+ anylist sr = any_cast<anylist>(*it);
+ //logger->debug(QString("a struct with %1 elements").arg(sr.size()));
+ anylist::const_iterator srit = sr.begin();
+
+ string file = any_cast<string>(*srit);
+ srit++;
+ int fileisutf8 = any_cast<int>(*srit);
+ srit++;
+ QString fileName(any_cast<string>(*srit).c_str());
+ srit++;
+ QString userName(any_cast<string>(*srit).c_str());
+ srit++;
+ QString hubName(any_cast<string>(*srit).c_str());
+ srit++;
+ int userId(any_cast<int>(*srit));
+ srit++;
+ int64 fileSize = any_cast<int64>(*srit);
+ srit++;
+ int sluts = any_cast<int>(*srit);
+ srit++;
+ int freeSlots = any_cast<int>(*srit);
+ srit++;
+ int type = any_cast<int>(*srit);
+ srit++;
+ QString tth(any_cast<string>(*srit).c_str());
+ SearchEntry se(file,userName,hubName,userId,fileSize,sluts,freeSlots,type,tth,fileisutf8!=0);
+ results += se;
+ }
+
+ emit searchResults( session, results );
+
+ }
+ catch( const boost::bad_any_cast& e )
+ {
+ logger->error("searchresults bad anycast");
+ }
+}
+
+
+void QueueEventHandler::handleCommand( const anylist & params )
+{
+ try
+ {
+ logger->debug("queueEvent");
+ anylist::const_iterator it = params.begin();
+ int eventType = any_cast<int>(*it);
+ it++;
+
+ switch(eventType)
+ {
+ case 1: // add
+ {
+ // Next param is a queueItem
+ anylist qitem = any_cast<anylist>(*it);
+ anylist::const_iterator qit = qitem.begin();
+ QueueItem qi;
+ qi.id = any_cast<int>(*qit);
+ qit++;
+ qi.target = any_cast<string>(*qit).c_str();
+ qit++;
+ qi.size = any_cast<int64>(*qit);
+ qit++;
+ qi.downloadedBytes = any_cast<int64>(*qit);
+ qit++;
+ qi.status = any_cast<int>(*qit);
+ qit++;
+ qi.priority = any_cast<int>(*qit);
+ qit++;
+ qi.currentSource = any_cast<int>(*qit);
+ //qit++;
+ //if(qit!=params.end()) {
+ // anylist sourcearr = any_cast<anylist>(*qit);
+ // anylist::iterator sourceit = sourcearr.begin();
+ // for(;sourceit!=sourcearr.end();sourceit++) qi.sources+=any_cast<int>(*sourceit);
+ //}
+ logger->debug("emitting queueItemAdded");
+ emit queueItemAdded( qi );
+
+ break;
+ }
+ case 2: // remove
+ {
+ // Second param: id
+ int id = any_cast<int>(*it);
+
+ logger->debug("emitting queueItemRemoved");
+
+ emit queueItemRemoved( id );
+ break;
+ }
+ case 3: // finished
+ {
+ int id = any_cast<int>(*it);
+
+ logger->debug("emitting queueItemFinshed");
+
+ emit queueItemFinshed( id );
+ break;
+ }
+ case 4: // sourcesupdated
+ {
+ int id = any_cast<int>(*it);
+ it++;
+ anylist sources = any_cast<anylist>(*it);
+ QList<int> sourceList;
+ for(anylist::iterator sit=sources.begin();sit!=sources.end();sit++)
+ sourceList+=any_cast<int>(*sit);
+
+ logger->debug("emitting sourcesUpdated");
+
+ emit sourcesUpdated( id, sourceList );
+ break;
+ }
+ case 5: { // statusupdated
+ int id = any_cast<int>(*it);
+ it++;
+ int status = any_cast<int>(*it);
+ it++;
+ int currSrc = any_cast<int>(*it);
+ it++;
+ int priority = any_cast<int>(*it);
+ it++;
+
+ logger->debug("emitting statusUpdated");
+
+ emit statusUpdated( id, status, currSrc, priority );
+ break;
+ }
+ default:
+ logger->error("Wrong eventType in queueEvent!");
+ }
+
+ }
+ catch( const boost::bad_any_cast& e )
+ {
+ logger->error("queueevent bad anycast");
+ }
+}
+
+void FinishedEventHandler::handleCommand( const anylist & params )
+{
+ try {
+ logger->debug("finishedEvent");
+ anylist::const_iterator it = params.begin();
+ int eventType = any_cast<int>(*it);
+ it++;
+ int transferType = any_cast<int>(*it);
+ it++;
+
+ switch(eventType)
+ {
+ case 1: // add
+ {
+ // A FinishedItem
+ anylist fi = any_cast<anylist>(*it);
+ anylist::iterator fit = fi.begin();
+ FinishedItem fitem;
+ fitem.id = any_cast<int>(*fit);
+ fit++;
+ fitem.target = any_cast<string>(*fit).c_str();
+ fit++;
+ fitem.user = any_cast<string>(*fit).c_str();
+ fit++;
+ fitem.hub = any_cast<string>(*fit).c_str();
+ fitem.type = transferType;
+ emit itemAdded(fitem);
+
+ }
+ break;
+ case 2: // remove
+ {
+ int id = any_cast<int>(*it);
+ emit itemRemoved( id );
+ }
+ break;
+ case 3: // removeAll
+ emit allRemoved(transferType);
+ break;
+ }
+ }
+ catch( const boost::bad_any_cast& e ) {
+ logger->error("finishedevent bad anycast");
+ }
+}
+
+void TransferEventHandler::handleCommand( const anylist & params )
+{
+ //logger->debug("transferEvent");
+ anylist::const_iterator it = params.begin();
+ int eventType = any_cast<int>(*it);
+ //logger->debug(QString("eventType: %1").arg(eventType));
+ it++;
+ FileTransfer::Type transferType = static_cast<FileTransfer::Type>(any_cast<int>(*it));
+ //logger->debug(QString("transfertype: %1").arg(transferType));
+ it++;
+
+
+ if(eventType==0 ) emit transferStarting( createTransfer(transferType,any_cast<anylist>(*it)) );
+ else if(eventType==1) {
+ QList<FileTransfer> tl;
+ anylist ftlst = any_cast<anylist>(*it);
+ for(anylist::iterator fit = ftlst.begin();fit!=ftlst.end();fit++)
+ tl+=createTransfer( transferType, any_cast<anylist>(*fit));
+ emit transferTick( tl );
+ }
+ else if(eventType==2) emit transferComplete( createTransfer(transferType,any_cast<anylist>(*it)) );
+ else if(eventType==3) {
+ anylist ft = any_cast<anylist>(*it);
+ it++;
+ QString errorMsg = any_cast<string>(*it).c_str();
+ emit transferFailed( createTransfer(transferType,ft), errorMsg );
+ }
+
+}
+
+FileTransfer TransferEventHandler::createTransfer( FileTransfer::Type type, const anylist & ft )
+{
+ anylist::const_iterator it = ft.begin();
+ FileTransfer f;
+ f.type = type;
+ f.pos = any_cast<int64>(*it);
+ it++;
+ f.startpos = any_cast<int64>(*it);
+ it++;
+ f.actual = any_cast<int64>(*it);it++;
+ f.size = any_cast<int64>(*it);it++;
+ f.averageSpeed = any_cast<int64>(*it);it++;
+ f.secondsLeft = any_cast<int64>(*it);it++;
+ f.bytesLeft = any_cast<int64>(*it);it++;
+ f.filename = any_cast<string>(*it).c_str();it++;
+ if(type==FileTransfer::UPLOAD) {f.localfilename = any_cast<string>(*it).c_str();it++;}
+ f.tth = any_cast<string>(*it).c_str();it++;
+ f.userid = any_cast<int>(*it);it++;
+ return f;
+}
+
+void UserFileListHandler::handleCommand( const anylist& params )
+{
+ try {
+ logger->debug("File lising");
+ anylist::const_iterator it = params.begin();
+ int userId = any_cast<int>(*it);
+ ++it;
+ string fileTree = any_cast<string>(*it);
+ QString temp = fileTree.c_str();
+ // Create a new model and emit it
+ UserFileModelPtr model( new UserFileModel( userId, temp ) );
+
+ emit fileListing( model );
+ }
+ catch( const boost::bad_any_cast& e ) {
+ logger->error(e.what());
+ }
+}
+
+
+void FavouriteHubListHandler::handleCommand( const anylist& params )
+{
+ try {
+ logger->debug("Favourite hub list");
+
+ anylist::const_iterator paramIt = params.begin();
+ const anylist& favHubList = any_cast<const anylist&>(*paramIt);
+ anylist::const_iterator favHubListIt = favHubList.begin();
+
+ QList<rpc_types::FavouriteHub> hubs;
+
+ while( favHubListIt != favHubList.end() ) {
+
+ const anylist& favHub = any_cast<const anylist&>(*favHubListIt);
+ anylist::const_iterator favHubIt = favHub.begin();
+ rpc_types::FavouriteHub hubEntry;
+ hubEntry.nick = any_cast<string>(*favHubIt);
+ ++favHubIt;
+ hubEntry.userDescription = any_cast<string>(*favHubIt);
+ ++favHubIt;
+ hubEntry.name = any_cast<string>(*favHubIt);
+ ++favHubIt;
+ hubEntry.server = any_cast<string>(*favHubIt);
+ ++favHubIt;
+ hubEntry.description = any_cast<string>(*favHubIt);
+ ++favHubIt;
+ hubEntry.password = any_cast<string>(*favHubIt);
+ ++favHubIt;
+ hubEntry.autoConnect = any_cast<bool>(*favHubIt);
+
+ hubs.push_back(hubEntry);
+
+ ++favHubListIt;
+ }
+
+ emit hubList( hubs );
+ }
+ catch( const boost::bad_any_cast& e ) {
+ logger->error(e.what());
+ }
+}
+
+void UserRemovedHandler::handleCommand( const anylist& params )
+{
+ try {
+ anylist::const_iterator it = params.begin();
+ int sessionId = any_cast<int>(*it);
+ ++it;
+ int userId = any_cast<int>(*it);
+ emit userRemoved( sessionId, userId );
+ }
+ catch( const boost::bad_any_cast& e )
+ {
+ logger->error(e.what());
+ throw RpcHandlerException("userRemoved","Bad anycast");
+ }
+}
+
+void FavouriteHubAddedHandler::handleCommand( const anylist& params )
+{
+ try {
+ logger->debug("Favourite hub added");
+
+ anylist::const_iterator paramIt = params.begin();
+
+ const anylist& favHub = any_cast<const anylist&>(*paramIt);
+ anylist::const_iterator favHubIt = favHub.begin();
+ rpc_types::FavouriteHub hubEntry;
+ hubEntry.nick = any_cast<string>(*favHubIt);
+ ++favHubIt;
+ hubEntry.userDescription = any_cast<string>(*favHubIt);
+ ++favHubIt;
+ hubEntry.name = any_cast<string>(*favHubIt);
+ ++favHubIt;
+ hubEntry.server = any_cast<string>(*favHubIt);
+ ++favHubIt;
+ hubEntry.description = any_cast<string>(*favHubIt);
+ ++favHubIt;
+ hubEntry.password = any_cast<string>(*favHubIt);
+ ++favHubIt;
+ hubEntry.autoConnect = any_cast<bool>(*favHubIt);
+
+ emit favHubAdded( hubEntry );
+
+ }
+ catch( const boost::bad_any_cast& e ) {
+ logger->error(e.what());
+ }
+}
+
+
+void FavouriteHubRemovedHandler::handleCommand( const anylist& params )
+{
+ try {
+ logger->debug("Favourite hub removed");
+
+ anylist::const_iterator paramIt = params.begin();
+
+ const string server = any_cast<string>(*paramIt);
+
+ emit favHubRemoved( server );
+
+ }
+ catch( const boost::bad_any_cast& e ) {
+ logger->error(e.what());
+ }
+}
+
+void HashInfoHandler::handleCommand( const anylist& params )
+{
+ // TODO
+}
+
+}
diff --git a/ui/commandhandlers.h b/ui/commandhandlers.h
new file mode 100644
index 0000000..6c538ee
--- /dev/null
+++ b/ui/commandhandlers.h
@@ -0,0 +1,420 @@
+#ifndef COMMAND_HANDLERS_H_
+#define COMMAND_HANDLERS_H_
+#include "commandhandlersinclude.h"
+#include "shareitemmodel.h"
+#include "searchentry.h"
+#include "queueitem.h"
+#include "filetransfer.h"
+#include "exceptions.h"
+#include "userfilemodel.h"
+
+#include <QCoreApplication>
+#include <QMessageBox>
+
+class User;
+class FinishedItem;
+
+namespace ui_cmd_handlers
+{
+
+class RpcCommandEvent : public QEvent
+{
+public:
+ RpcCommandEvent(const anylist& params) : QEvent(QEvent::User), parameters(params)
+ {}
+ ;
+ const anylist& getParams() const
+ {
+ return parameters;
+ }
+private:
+ const anylist parameters;
+};
+
+class GuiCommandHandlerBase : public QObject, public rpc::RpcCommandHandler
+{
+ Q_OBJECT
+public:
+ GuiCommandHandlerBase(const char* name,
+ boost::function<void( int, const rpc::CmdPtr&)> sender,
+ int numParams ) :
+ rpc::RpcCommandHandler(name, sender,numParams ),dialogFence(false) {}
+
+ //! Called by the RpcDriver.
+ virtual void handleCommand(int /*clientId*/, const anylist& params)
+ {
+ RpcCommandEvent* event = new RpcCommandEvent(params); // deleted by qt
+ QCoreApplication::postEvent(this,event);
+ }
+
+protected:
+ virtual void customEvent(QEvent* e)
+ {
+ RpcCommandEvent* ce = static_cast<RpcCommandEvent *>(e);
+ try {
+ handleCommand(ce->getParams());
+ } catch(const boost::bad_any_cast& e) {
+ if(!dialogFence) {
+ dialogFence = true;
+ QMessageBox::critical(0,tr("Backend Communication Error"), tr("We encountered an error while communicating with the backend! Perhaps versions mismatch?"));
+ dialogFence = false;
+ }
+ } catch(const RpcHandlerException& re) {
+ if(!dialogFence) {
+ dialogFence = true;
+ QMessageBox::critical(0,tr("Backend Communication Error"), QString("Exception: ") + re.getMethodName() + " " + re.getMessage());
+ dialogFence = false;
+ }
+ }
+ }
+ virtual void handleCommand(const anylist& params) = 0;
+ private:
+ bool dialogFence;
+};
+
+
+
+class HubConnectedHandler : public GuiCommandHandlerBase
+{
+ Q_OBJECT
+public:
+ HubConnectedHandler(boost::function<void( int, const rpc::CmdPtr&)> sender ) :
+ GuiCommandHandlerBase("hubConnected", sender,NUM_PARAMS )
+ {}
+protected:
+ //! Implements the actual command parsing and signal emission.
+ virtual void handleCommand(const anylist& params);
+private:
+ static const short NUM_PARAMS = 1;
+signals:
+ void hubConnected(int);
+};
+
+class HubUpdatedHandler : public GuiCommandHandlerBase
+{
+ Q_OBJECT
+public:
+ HubUpdatedHandler(boost::function<void( int, const rpc::CmdPtr&)> sender ) :
+ GuiCommandHandlerBase("hubUpdated", sender,NUM_PARAMS )
+ {}
+protected:
+ //! Implements the actual command parsing and signal emission.
+ virtual void handleCommand(const anylist& params);
+private:
+ static const short NUM_PARAMS = 2;
+signals:
+ void hubUpdated(int,const QString&);
+};
+
+class HubStatsHandler : public GuiCommandHandlerBase
+{
+ Q_OBJECT
+ public:
+ HubStatsHandler(boost::function<void( int, const rpc::CmdPtr&)> sender ) :
+ GuiCommandHandlerBase("hubStats", sender,NUM_PARAMS )
+ {}
+ protected:
+ //! Implements the actual command parsing and signal emission.
+ virtual void handleCommand(const anylist& params);
+ private:
+ static const short NUM_PARAMS = 2;
+ signals:
+ void hubStats(int,qint64);
+};
+
+class ConnectionFailedHandler : public GuiCommandHandlerBase
+{
+ Q_OBJECT
+public:
+ ConnectionFailedHandler(boost::function<void( int, const rpc::CmdPtr&)> sender ) :
+ GuiCommandHandlerBase("connectionFailed", sender,NUM_PARAMS )
+ {}
+protected:
+ //! Implements the actual command parsing and signal emission.
+ virtual void handleCommand(const anylist& params);
+private:
+ static const short NUM_PARAMS = 2;
+signals:
+ void connectionFailed(int,const QString&);
+};
+
+class PrivateChatHandler : public GuiCommandHandlerBase
+{
+ Q_OBJECT
+public:
+ PrivateChatHandler(boost::function<void( int, const rpc::CmdPtr&)> sender ) :
+ GuiCommandHandlerBase("privateChatMessage", sender,NUM_PARAMS) {}
+protected:
+ virtual void handleCommand(const anylist& params);
+private:
+ static const short NUM_PARAMS = 3;
+signals:
+ void privateChat(int session,const QString& user,const QString& msg);
+
+};
+
+class ChatMessageHandler : public GuiCommandHandlerBase
+{
+ Q_OBJECT
+public:
+ ChatMessageHandler( boost::function<void( int, const rpc::CmdPtr&)> sender ) :
+ GuiCommandHandlerBase("chatMessage",sender,NUM_PARAMS ) {}
+protected:
+ virtual void handleCommand(const anylist& params);
+private:
+ static const short NUM_PARAMS = 2;
+signals:
+ void chatMessage(int session,const QString&);
+};
+
+class UsersUpdatedHandler : public GuiCommandHandlerBase
+{
+ Q_OBJECT
+public:
+ UsersUpdatedHandler( boost::function<void( int, const rpc::CmdPtr&)> sender ) :
+ GuiCommandHandlerBase("usersUpdated",sender,NUM_PARAMS ) {}
+protected:
+ virtual void handleCommand(const anylist& params);
+private:
+ static const short NUM_PARAMS = 2;
+signals:
+ void usersUpdated(int session,QList<User*>);
+};
+
+
+class settingsInfoHandler : public GuiCommandHandlerBase
+{
+ Q_OBJECT
+public:
+ settingsInfoHandler(boost::function<void( int, const rpc::CmdPtr&)> sender ) :
+ GuiCommandHandlerBase("settingsInfo", sender,NUM_PARAMS )
+ {}
+protected:
+ virtual void handleCommand(const anylist& params);
+private:
+ static const short NUM_PARAMS = 2;
+signals:
+ void settingsInfo(const QList<QString>&,const QList<QVariant>&);
+};
+
+
+class PublicHubListHandler : public GuiCommandHandlerBase
+{
+ Q_OBJECT
+public:
+ PublicHubListHandler( boost::function<void( int, const rpc::CmdPtr& )> sender ) :
+ GuiCommandHandlerBase( "newHubList", sender, NUM_PARAMS )
+ {}
+protected:
+ virtual void handleCommand( const anylist& params );
+private:
+ static const short NUM_PARAMS = 1;
+signals:
+ void hubList( const QList<rpc_types::HubEntry>& );
+};
+
+
+class FavouriteHubAddedHandler : public GuiCommandHandlerBase
+{
+ Q_OBJECT
+public:
+ FavouriteHubAddedHandler( boost::function<void( int, const rpc::CmdPtr& )> sender ) :
+ GuiCommandHandlerBase( "favouriteHubAdded", sender, NUM_PARAMS )
+ {}
+protected:
+ virtual void handleCommand( const anylist& params );
+private:
+ static const short NUM_PARAMS = 1;
+signals:
+ void favHubAdded( const rpc_types::FavouriteHub& );
+};
+
+
+class FavouriteHubRemovedHandler : public GuiCommandHandlerBase
+{
+ Q_OBJECT
+public:
+ FavouriteHubRemovedHandler( boost::function<void( int, const rpc::CmdPtr& )> sender ) :
+ GuiCommandHandlerBase( "favouriteHubRemoved", sender, NUM_PARAMS )
+ {}
+protected:
+ virtual void handleCommand( const anylist& params );
+private:
+ static const short NUM_PARAMS = 1;
+signals:
+ void favHubRemoved( const string& );
+};
+
+
+class FavouriteHubListHandler : public GuiCommandHandlerBase
+{
+ Q_OBJECT
+public:
+ FavouriteHubListHandler( boost::function<void( int, const rpc::CmdPtr& )> sender ) :
+ GuiCommandHandlerBase( "favouriteHubList", sender, NUM_PARAMS )
+ {}
+protected:
+ virtual void handleCommand( const anylist& params );
+private:
+ static const short NUM_PARAMS = 1;
+signals:
+ void hubList( const QList<rpc_types::FavouriteHub>& );
+};
+
+
+class RunningSessionsHandler : public GuiCommandHandlerBase
+{
+ Q_OBJECT
+ public:
+ RunningSessionsHandler(boost::function<void( int, const rpc::CmdPtr&)> sender ) :
+ GuiCommandHandlerBase("runningSessions", sender,NUM_PARAMS )
+ {}
+ protected:
+ virtual void handleCommand(const anylist& params);
+ private:
+ static const short NUM_PARAMS = 2;
+ signals:
+ void sessionInfo(int id,const QString& hubname,const QString& url,const QList<User*>);
+ void queueList(const QList<QueueItem>&);
+};
+
+
+class SessionCreatedHandler : public GuiCommandHandlerBase
+{
+Q_OBJECT
+public:
+ SessionCreatedHandler(boost::function<void( int, const rpc::CmdPtr&)> sender ) : GuiCommandHandlerBase("sessionCreated", sender,NUM_PARAMS ) {}
+protected:
+ virtual void handleCommand(const anylist& params);
+private:
+ static const short NUM_PARAMS = 1;
+signals:
+ void sessionCreated(int);
+
+
+};
+
+
+//! Handles the sharedDirs remote procedure call.
+class SharedDirsHandler : public GuiCommandHandlerBase
+{
+ Q_OBJECT
+ public:
+ SharedDirsHandler(boost::function<void( int, const rpc::CmdPtr&)> sender ) : GuiCommandHandlerBase("sharedDirs", sender,NUM_PARAMS ) {}
+ protected:
+ virtual void handleCommand(const anylist& params);
+ private:
+ static const short NUM_PARAMS = 1;
+ signals:
+ void sharedDirs(const QList<ShareItem>);
+};
+
+//! Handles the searchResults remote procedure call.
+class SearchResultsHandler : public GuiCommandHandlerBase
+{
+ Q_OBJECT
+ public:
+ SearchResultsHandler(boost::function<void( int, const rpc::CmdPtr&)> sender ) : GuiCommandHandlerBase("searchResults", sender,NUM_PARAMS ) {}
+ protected:
+ virtual void handleCommand(const anylist& params);
+ private:
+ static const short NUM_PARAMS = 2;
+ signals:
+ void searchResults(int,QList<SearchEntry>&);
+};
+
+//! Handles queueEvent
+class QueueEventHandler : public GuiCommandHandlerBase
+{
+ Q_OBJECT
+ public:
+ QueueEventHandler(boost::function<void( int, const rpc::CmdPtr&)> sender ) : GuiCommandHandlerBase("queueEvent", sender,NUM_PARAMS ) {}
+ protected:
+ virtual void handleCommand(const anylist& params);
+ private:
+ static const short NUM_PARAMS = 1;
+ signals:
+ void queueItemAdded(const QueueItem&);
+ void queueItemRemoved(int id);
+ void queueItemFinshed(int id);
+ void sourcesUpdated(int id, QList<int> sources);
+ void statusUpdated(int id, int status, int currentSource, int priority);
+};
+
+//! Handles finishedEvent
+class FinishedEventHandler : public GuiCommandHandlerBase
+{
+ Q_OBJECT
+ public:
+ FinishedEventHandler(boost::function<void( int, const rpc::CmdPtr&)> sender ) : GuiCommandHandlerBase("finishedEvent", sender,NUM_PARAMS ) {}
+ protected:
+ virtual void handleCommand(const anylist& params);
+ private:
+ static const short NUM_PARAMS = 2;
+ signals:
+ void itemAdded(const FinishedItem&);
+ void itemRemoved(int);
+ void allRemoved(int);
+};
+
+//! Handles transferEvent
+class TransferEventHandler : public GuiCommandHandlerBase
+{
+ Q_OBJECT
+ public:
+ TransferEventHandler(boost::function<void( int, const rpc::CmdPtr&)> sender ) : GuiCommandHandlerBase("transferEvent", sender,NUM_PARAMS ) {}
+ protected:
+ virtual void handleCommand(const anylist& params);
+ private:
+ static const short NUM_PARAMS = 2;
+ FileTransfer createTransfer(FileTransfer::Type type,const anylist& ft);
+ signals:
+ void transferStarting(const FileTransfer&);
+ void transferTick( const QList<FileTransfer>& );
+ void transferComplete( const FileTransfer& );
+ void transferFailed( const FileTransfer&,const QString& );
+};
+
+class UserFileListHandler : public GuiCommandHandlerBase
+{
+ Q_OBJECT
+public:
+ UserFileListHandler(boost::function<void( int, const rpc::CmdPtr&)> sender ) : GuiCommandHandlerBase("userFileListing", sender,NUM_PARAMS ) {}
+protected:
+ virtual void handleCommand(const anylist& params);
+private:
+ static const short NUM_PARAMS = 2;
+signals:
+ void fileListing( const UserFileModelPtr& model );
+};
+
+//! Handles the userRemoved command.
+class UserRemovedHandler : public GuiCommandHandlerBase
+{
+ Q_OBJECT
+ public:
+ UserRemovedHandler(boost::function<void( int, const rpc::CmdPtr&)> sender ) : GuiCommandHandlerBase("userRemoved", sender,NUM_PARAMS ) {}
+ protected:
+ virtual void handleCommand(const anylist& params);
+ private:
+ static const short NUM_PARAMS = 2;
+ signals:
+ void userRemoved(int session, int id);
+};
+
+class HashInfoHandler : public GuiCommandHandlerBase
+{
+ Q_OBJECT
+ public:
+ HashInfoHandler(boost::function<void( int, const rpc::CmdPtr&)> sender ) : GuiCommandHandlerBase("hashInfo", sender,NUM_PARAMS ) {}
+ protected:
+ virtual void handleCommand(const anylist& params);
+ private:
+ static const short NUM_PARAMS = 4;
+ signals:
+ void userRemoved(int session, int id);
+};
+
+
+}
+#endif
diff --git a/ui/commandhandlersinclude.h b/ui/commandhandlersinclude.h
new file mode 100644
index 0000000..307107a
--- /dev/null
+++ b/ui/commandhandlersinclude.h
@@ -0,0 +1,16 @@
+#ifndef COMMANDHANDLER_INCLUDES_H__
+#define COMMANDHANDLER_INCLUDES_H__
+
+#include <list>
+#include <algorithm>
+#include <boost/any.hpp>
+#include <boost/function.hpp>
+#include "rpcdriver/rpccommandhandler.h"
+#include "global.h"
+#include "rpctypes.h"
+#include <QObject>
+#include <QList>
+#include <QVariant>
+#include <QEvent>
+
+#endif
diff --git a/ui/connectdialog.cpp b/ui/connectdialog.cpp
new file mode 100644
index 0000000..99d3713
--- /dev/null
+++ b/ui/connectdialog.cpp
@@ -0,0 +1,67 @@
+/*
+ * connctdialog.cpp
+ * ui
+ *
+ * Created by Mikael Gransell on 4/13/06.
+ * Copyright 2006 __MyCompanyName__. All rights reserved.
+ *
+ */
+
+#include "connectdialog.h"
+#include "publichubswidget.h"
+#include "favouritehubswidget.h"
+
+ConnectDialog::ConnectDialog( const BackendConnectionPtr& backendConn,
+ QWidget* parent )
+: QDialog( parent ),
+ backendConnection( backendConn )
+{
+ // Create hub lists and add them to the tab widget that we contain
+ ui.setupUi( this );
+ // Remove the first tab that is automatically created by designer
+ publicHubs = new PublicHubs( backendConn, ui.tabWidget );
+ ui.tabWidget->addTab( publicHubs, tr("Public Hubs") );
+ favouriteHubs = new FavouriteHubs( backendConn, ui.tabWidget );
+ ui.tabWidget->addTab( favouriteHubs, tr("Favourite Hubs") );
+ ui.tabWidget->removeTab(0);
+
+ connect(publicHubs, SIGNAL(close()), this, SLOT(accept()));
+ connect(favouriteHubs, SIGNAL(close()), this, SLOT(accept()));
+}
+
+
+void ConnectDialog::publicHubList( const QList<rpc_types::HubEntry>& hubs )
+{
+ publicHubs->hubList(hubs);
+}
+
+void ConnectDialog::favouriteHubList( const QList<rpc_types::FavouriteHub>& hubs )
+{
+ favouriteHubs->setHubs(hubs);
+}
+
+void ConnectDialog::favouriteHubAdded( const rpc_types::FavouriteHub& hub )
+{
+ favouriteHubs->addHub(hub);
+}
+
+void ConnectDialog::favouriteHubRemoved( const string& hub )
+{
+ favouriteHubs->removeHub(hub);
+}
+
+void ConnectDialog::getLists()
+{
+ publicHubs->on_refreshButton_pressed();
+ favouriteHubs->refresh();
+}
+
+void ConnectDialog::on_quickConnectButton_pressed()
+{
+ QString addr = ui.addressEdit->text();
+ if( !addr.isEmpty() ) {
+ backendConnection->createSession( addr );
+ }
+
+ done(QDialog::Accepted);
+} \ No newline at end of file
diff --git a/ui/connectdialog.h b/ui/connectdialog.h
new file mode 100644
index 0000000..6c1bfa5
--- /dev/null
+++ b/ui/connectdialog.h
@@ -0,0 +1,50 @@
+/*
+ * connectdialog.h
+ * ui
+ *
+ * Created by Mikael Gransell on 4/11/06.
+ * Copyright 2006 __MyCompanyName__. All rights reserved.
+ *
+ */
+
+
+#ifndef _CONNECT_DIALOG_H_
+#define _CONNECT_DIALOG_H_
+
+#include <QDialog>
+
+#include "rpctypes.h"
+#include "backendconnection.h"
+#include "ui_connectdialog.h"
+
+class PublicHubs;
+class FavouriteHubs;
+
+class ConnectDialog : public QDialog
+{
+ Q_OBJECT
+public:
+ ConnectDialog( const BackendConnectionPtr& backendConn,
+ QWidget* parent = NULL );
+ ~ConnectDialog() { }
+
+public slots:
+ void publicHubList( const QList<rpc_types::HubEntry>& hubs );
+
+ void favouriteHubList( const QList<rpc_types::FavouriteHub>& hubs );
+ void favouriteHubAdded( const rpc_types::FavouriteHub& hubs );
+ void favouriteHubRemoved( const string& hubs );
+
+ void getLists();
+
+private slots:
+ void on_quickConnectButton_pressed();
+
+private:
+ BackendConnectionPtr backendConnection;
+ Ui::connectDialog ui;
+ PublicHubs* publicHubs;
+ FavouriteHubs* favouriteHubs;
+};
+
+#endif
diff --git a/ui/connectdialog.ui b/ui/connectdialog.ui
new file mode 100644
index 0000000..80fa63d
--- /dev/null
+++ b/ui/connectdialog.ui
@@ -0,0 +1,64 @@
+<ui version="4.0" >
+ <author></author>
+ <comment></comment>
+ <exportmacro></exportmacro>
+ <class>connectDialog</class>
+ <widget class="QDialog" name="connectDialog" >
+ <property name="geometry" >
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>923</width>
+ <height>558</height>
+ </rect>
+ </property>
+ <property name="windowTitle" >
+ <string>Connect</string>
+ </property>
+ <layout class="QVBoxLayout" >
+ <property name="margin" >
+ <number>9</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item>
+ <widget class="QTabWidget" name="tabWidget" >
+ <widget class="QWidget" name="tab" >
+ <attribute name="title" >
+ <string>Tab 1</string>
+ </attribute>
+ </widget>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="quickConnectGroupBox" >
+ <property name="title" >
+ <string>Quick Connect</string>
+ </property>
+ <layout class="QHBoxLayout" >
+ <property name="margin" >
+ <number>9</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item>
+ <widget class="QLineEdit" name="addressEdit" />
+ </item>
+ <item>
+ <widget class="QPushButton" name="quickConnectButton" >
+ <property name="text" >
+ <string>Connect</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <pixmapfunction></pixmapfunction>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/ui/createfavourite.ui b/ui/createfavourite.ui
new file mode 100644
index 0000000..f4cba75
--- /dev/null
+++ b/ui/createfavourite.ui
@@ -0,0 +1,144 @@
+<ui version="4.0" >
+ <author></author>
+ <comment></comment>
+ <exportmacro></exportmacro>
+ <class>CreateFavouriteDialog</class>
+ <widget class="QDialog" name="CreateFavouriteDialog" >
+ <property name="geometry" >
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>256</width>
+ <height>205</height>
+ </rect>
+ </property>
+ <property name="windowTitle" >
+ <string>Create Favourite</string>
+ </property>
+ <layout class="QVBoxLayout" >
+ <property name="margin" >
+ <number>9</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item>
+ <layout class="QGridLayout" >
+ <property name="margin" >
+ <number>0</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item row="3" column="0" >
+ <widget class="QLabel" name="pwdLabel" >
+ <property name="text" >
+ <string>Password:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1" >
+ <widget class="QLineEdit" name="nickEdit" />
+ </item>
+ <item row="3" column="1" >
+ <widget class="QLineEdit" name="pwdEdit" />
+ </item>
+ <item row="0" column="1" >
+ <widget class="QLineEdit" name="nameEdit" />
+ </item>
+ <item row="0" column="0" >
+ <widget class="QLabel" name="nameLabel" >
+ <property name="text" >
+ <string>Name:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0" >
+ <widget class="QLabel" name="serverLabel" >
+ <property name="text" >
+ <string>Server:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1" >
+ <widget class="QLineEdit" name="serverEdit" />
+ </item>
+ <item row="2" column="0" >
+ <widget class="QLabel" name="nickLabel" >
+ <property name="text" >
+ <string>Nick:</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="autoConnectCheckBox" >
+ <property name="text" >
+ <string>Connect automatically on startup</string>
+ </property>
+ <property name="checked" >
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" >
+ <property name="margin" >
+ <number>0</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item>
+ <spacer>
+ <property name="orientation" >
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" >
+ <size>
+ <width>131</width>
+ <height>31</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QPushButton" name="createButton" >
+ <property name="text" >
+ <string>Create</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="cancelButton" >
+ <property name="text" >
+ <string>Cancel</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <pixmapfunction></pixmapfunction>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>cancelButton</sender>
+ <signal>clicked()</signal>
+ <receiver>CreateFavouriteDialog</receiver>
+ <slot>reject()</slot>
+ <hints>
+ <hint type="sourcelabel" >
+ <x>205</x>
+ <y>178</y>
+ </hint>
+ <hint type="destinationlabel" >
+ <x>127</x>
+ <y>102</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>
diff --git a/ui/createfavouritedialog.cpp b/ui/createfavouritedialog.cpp
new file mode 100644
index 0000000..d52e921
--- /dev/null
+++ b/ui/createfavouritedialog.cpp
@@ -0,0 +1,34 @@
+/*
+ * createfavouritedialog.cpp
+ * ui
+ *
+ * Created by Mikael Gransell on 4/18/06.
+ * Copyright 2006 __MyCompanyName__. All rights reserved.
+ *
+ */
+
+#include "createfavouritedialog.h"
+
+#include "rpctypes.h"
+
+CreateFavouriteDialog::CreateFavouriteDialog( const BackendConnectionPtr& backendConn,
+ QWidget* parent )
+: QDialog(parent),
+ backendConnection(backendConn)
+{
+ ui.setupUi(this);
+}
+
+void CreateFavouriteDialog::on_createButton_clicked()
+{
+ rpc_types::FavouriteHub hubEntry;
+ hubEntry.name = ui.nameEdit->text().toStdString();
+ hubEntry.server = ui.serverEdit->text().toStdString();
+ hubEntry.nick = ui.nickEdit->text().toStdString();
+ hubEntry.password = ui.pwdEdit->text().toStdString();
+ hubEntry.autoConnect = ui.autoConnectCheckBox->isChecked();
+
+ // Add the favourite and close the dialog
+ backendConnection->addFavouriteHub(hubEntry);
+ done(QDialog::Accepted);
+} \ No newline at end of file
diff --git a/ui/createfavouritedialog.h b/ui/createfavouritedialog.h
new file mode 100644
index 0000000..b423fe7
--- /dev/null
+++ b/ui/createfavouritedialog.h
@@ -0,0 +1,37 @@
+/*
+ * createfavouritedialog.h
+ * ui
+ *
+ * Created by Mikael Gransell on 4/18/06.
+ * Copyright 2006 __MyCompanyName__. All rights reserved.
+ *
+ */
+
+#ifndef CREATE_FAVOURITE_DIALOG_H_
+#define CREATE_FAVOURITE_DIALOG_H_
+
+#include "backendconnection.h"
+
+#include <QDialog>
+
+#include "ui_createfavourite.h"
+
+
+class CreateFavouriteDialog : public QDialog
+{
+ Q_OBJECT
+public:
+ CreateFavouriteDialog( const BackendConnectionPtr& backendConn,
+ QWidget* parent = NULL );
+ ~CreateFavouriteDialog() {}
+
+public slots:
+ void on_createButton_clicked();
+
+private:
+ BackendConnectionPtr backendConnection;
+
+ Ui::CreateFavouriteDialog ui;
+};
+
+#endif
diff --git a/ui/exceptions.h b/ui/exceptions.h
new file mode 100644
index 0000000..7627801
--- /dev/null
+++ b/ui/exceptions.h
@@ -0,0 +1,43 @@
+//
+// C++ Interface: Exceptions
+//
+// Description:
+//
+//
+// Author: Rikard Björklind <olof@linux.nu>, (C) 2005
+//
+// Copyright: See COPYING file that comes with this distribution
+//
+//
+
+#ifndef EXCEPTIONS_H__
+#define EXCEPTIONS_H__
+
+class Exception
+{
+public:
+ Exception() : message("") {}
+ Exception(const char* msg) : message(msg) {}
+ const char* getMessage() const {return message;}
+protected:
+ const char* message;
+};
+
+class IndexOutOfBoundsException : public Exception
+{
+ public:
+ IndexOutOfBoundsException() : Exception() {}
+};
+
+class RpcHandlerException : public Exception
+{
+ public:
+ RpcHandlerException(const char* aMethodName,const char* aMessage) : Exception(aMessage),methodName(aMethodName) {}
+ const char* getMethodName() const {return methodName;}
+ private:
+ const char* methodName;
+
+};
+
+#endif
+
diff --git a/ui/favouritehublist.ui b/ui/favouritehublist.ui
new file mode 100644
index 0000000..1124575
--- /dev/null
+++ b/ui/favouritehublist.ui
@@ -0,0 +1,80 @@
+<ui version="4.0" >
+ <author></author>
+ <comment></comment>
+ <exportmacro></exportmacro>
+ <class>FavouriteHubsForm</class>
+ <widget class="QWidget" name="FavouriteHubsForm" >
+ <property name="geometry" >
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>588</width>
+ <height>414</height>
+ </rect>
+ </property>
+ <property name="windowTitle" >
+ <string>Form</string>
+ </property>
+ <layout class="QVBoxLayout" >
+ <property name="margin" >
+ <number>9</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item>
+ <widget class="QTreeView" name="favouriteHubList" />
+ </item>
+ <item>
+ <layout class="QHBoxLayout" >
+ <property name="margin" >
+ <number>0</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="addFavouriteButton" >
+ <property name="text" >
+ <string>Add Favourite</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="removeFavouriteButton" >
+ <property name="text" >
+ <string>Remove Favourite</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer>
+ <property name="orientation" >
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" >
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QPushButton" name="connectFavouriteButton" >
+ <property name="text" >
+ <string>Connect</string>
+ </property>
+ <property name="default" >
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <pixmapfunction></pixmapfunction>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/ui/favouritehubswidget.cpp b/ui/favouritehubswidget.cpp
new file mode 100644
index 0000000..7798145
--- /dev/null
+++ b/ui/favouritehubswidget.cpp
@@ -0,0 +1,192 @@
+//
+// C++ Implementation: FavouriteHubs
+//
+// Description:
+//
+//
+// Author: Rikard Björklind <olof@linux.nu>, (C) 2005
+//
+// Copyright: See COPYING file that comes with this distribution
+//
+//
+
+#include <QtGui>
+#include "favouritehubswidget.h"
+#include "backendconnection.h"
+#include "createfavouritedialog.h"
+
+FavouriteHubs::CheckBoxDelegate::CheckBoxDelegate( QObject *parent ) : QItemDelegate(parent)
+{
+}
+
+QWidget * FavouriteHubs::CheckBoxDelegate::createEditor( QWidget * parent, const QStyleOptionViewItem & /*option*/, const QModelIndex & /*index*/ ) const
+{
+ QCheckBox* editor = new QCheckBox( parent );
+ editor->installEventFilter(editor);
+ return editor;
+}
+
+void FavouriteHubs::CheckBoxDelegate::setEditorData( QWidget * editor, const QModelIndex & index ) const
+{
+ QCheckBox *checkBox = dynamic_cast<QCheckBox*>(editor);
+ try {
+ checkBox->setChecked(dynamic_cast<const FavouriteHubsModel*>(index.model())->getHubData(index).autoConnect);
+ }
+ catch(...) {
+
+ }
+}
+
+void FavouriteHubs::CheckBoxDelegate::setModelData( QWidget * editor, QAbstractItemModel * model, const QModelIndex & index ) const
+{
+ QCheckBox *checkBox = dynamic_cast<QCheckBox*>(editor);
+ bool value = checkBox->isChecked();
+ model->setData(index, value);
+}
+
+void FavouriteHubs::CheckBoxDelegate::updateEditorGeometry( QWidget * editor, const QStyleOptionViewItem & option, const QModelIndex & /*index*/ ) const
+{
+ editor->setGeometry(option.rect);
+}
+
+
+FavouriteHubs::FavouriteHubs( const BackendConnectionPtr& backendConn,
+ QWidget * parent )
+: QWidget(parent),
+ model(new FavouriteHubsModel(this)),
+ delegate( new CheckBoxDelegate ),
+ backendConnection( backendConn )
+{
+ // Create the UI
+ ui.setupUi(this);
+ ui.favouriteHubList->setModel(model.get());
+ ui.favouriteHubList->setItemDelegate(delegate.get());
+ connect(model.get(),SIGNAL(layoutChanged()),SLOT(createPersistentEditors()));
+}
+
+void FavouriteHubs::on_connectFavouriteButton_clicked( )
+{
+ backendConnection->createSession( model->getServer( ui.favouriteHubList->selectionModel()->currentIndex().row() ));
+ emit close();
+}
+
+void FavouriteHubs::on_removeFavouriteButton_clicked( )
+{
+ QModelIndex i = ui.favouriteHubList->selectionModel()->currentIndex();
+ if(i.isValid())
+ backendConnection->removeFavouriteHub( model->getServer( i.row()) );
+}
+
+void FavouriteHubs::on_addFavouriteButton_clicked( )
+{
+ CreateFavouriteDialog dlg( backendConnection, this );
+ dlg.setModal(true);
+ dlg.exec();
+}
+
+void FavouriteHubs::on_favouriteHubList_doubleClicked()
+{
+ on_connectFavouriteButton_clicked();
+}
+
+void FavouriteHubs::createPersistentEditors( )
+{
+ // todo iterate, call openpersistenteditor
+ for(int i = 0; i < model->rowCount(QModelIndex());i++) {
+ QModelIndex mi = model->index(i,0);
+ ui.favouriteHubList->openPersistentEditor(mi);
+ }
+}
+
+void FavouriteHubs::refresh()
+{
+ backendConnection->getFavouriteHubs();
+}
+
+
+QVariant FavouriteHubs::FavouriteHubsModel::data( const QModelIndex & index, int role ) const
+{
+ if(role==Qt::DisplayRole) {
+ switch(index.column()) {
+// case 0: return entries[index.row()].autoConnect;
+ case 1: return entries[index.row()].name.c_str();
+ case 2: return entries[index.row()].server.c_str();
+ case 3: return entries[index.row()].description.c_str();
+ case 4: return entries[index.row()].password.c_str();
+ case 5: return entries[index.row()].nick.c_str();
+ case 6: return entries[index.row()].userDescription.c_str();
+ }
+
+ }
+ return QVariant();
+}
+
+rpc_types::FavouriteHub FavouriteHubs::FavouriteHubsModel::getHubData( const QModelIndex& index ) const
+{
+ if( index.isValid() ) {
+ return entries[index.row()];
+ }
+ return rpc_types::FavouriteHub();
+}
+
+QVariant FavouriteHubs::FavouriteHubsModel::headerData( int section, Qt::Orientation orientation, int role ) const
+{
+ if( orientation==Qt::Horizontal && role==Qt::DisplayRole )
+ {
+ switch(section) {
+ case 0: return tr("AutoConnect");
+ case 1: return tr("Name");
+ case 2: return tr("Server");
+ case 3: return tr("Description");
+ case 4: return tr("Password");
+ case 5: return tr("Nick");
+ case 6: return tr("User Description");
+
+ }
+ }
+ return QVariant();
+}
+
+void FavouriteHubs::FavouriteHubsModel::setHubs( const QList< rpc_types::FavouriteHub >& hubs)
+{
+ entries = hubs;
+ emit layoutChanged();
+}
+
+void FavouriteHubs::FavouriteHubsModel::addHub( const rpc_types::FavouriteHub &e )
+{
+ entries.append( e );
+ emit layoutChanged();
+}
+
+void FavouriteHubs::FavouriteHubsModel::removeHub( const string &s )
+{
+ rpc_types::FavouriteHub tempFav;
+ tempFav.server = s;
+ entries.removeAll( tempFav );
+ emit layoutChanged();
+}
+
+
+bool FavouriteHubs::FavouriteHubsModel::setData ( const QModelIndex& index, const QVariant& value, int role /*= Qt::EditRole*/ )
+{
+ if(index.column()==0 && role==Qt::EditRole) // editing autoconnect
+ {
+ entries[index.row()].autoConnect = value.toBool();
+ return true;
+ }
+ return false;
+}
+
+Qt::ItemFlags FavouriteHubs::FavouriteHubsModel::flags ( const QModelIndex& index ) const
+{
+ switch(index.column()) {
+ case 0: return Qt::ItemIsSelectable |
+ Qt::ItemIsEnabled |
+ Qt::ItemIsUserCheckable |
+ Qt::ItemIsEditable;
+ }
+ return Qt::ItemIsSelectable | Qt::ItemIsEnabled;
+}
+
+
diff --git a/ui/favouritehubswidget.h b/ui/favouritehubswidget.h
new file mode 100644
index 0000000..b3a7c0f
--- /dev/null
+++ b/ui/favouritehubswidget.h
@@ -0,0 +1,148 @@
+//
+// C++ Interface: FavouriteHubs
+//
+//
+//
+// Author: Rikard Björklind <olof@linux.nu>, (C) 2005
+//
+// Copyright: See COPYING file that comes with this distribution
+//
+//
+#ifndef FAVOURITE_HUBS_H__
+#define FAVOURITE_HUBS_H__
+
+#include <boost/shared_ptr.hpp>
+
+#include <QWidget>
+#include <QAbstractTableModel>
+#include <QItemDelegate>
+
+#include "ui_favouritehublist.h"
+
+#include "rpctypes.h"
+#include "backendconnection.h"
+
+
+//! Implements the favourite hub list user interface
+class FavouriteHubs : public QWidget
+{
+ Q_OBJECT
+public:
+ FavouriteHubs( const BackendConnectionPtr& backendConn,
+ QWidget* parent=0);
+ ~FavouriteHubs()
+ {}
+
+public slots:
+ //! Called to set the list of favourite hubs.
+ void setHubs(const QList<rpc_types::FavouriteHub>& hubs)
+ {
+ model->setHubs(hubs);
+ }
+ //! Called when a hub has been added.
+ void addHub(const rpc_types::FavouriteHub& e)
+ {
+ model->addHub(e);
+ }
+ //! Called when a hub has been removed.
+ void removeHub(const string& e)
+ {
+ model->removeHub(e);
+ }
+
+ void refresh();
+
+private slots:
+ void on_connectFavouriteButton_clicked();
+
+ //! Called to execute a remove request to the backend.
+ void on_removeFavouriteButton_clicked();
+
+ void on_addFavouriteButton_clicked();
+
+ void on_favouriteHubList_doubleClicked();
+
+ void createPersistentEditors();
+
+signals:
+ void close();
+
+private:
+ class FavouriteHubsModel;
+ class CheckBoxDelegate;
+ boost::shared_ptr<FavouriteHubsModel> model;
+ boost::shared_ptr<CheckBoxDelegate> delegate;
+
+ Ui::FavouriteHubsForm ui;
+
+ BackendConnectionPtr backendConnection;
+
+ class CheckBoxDelegate : public QItemDelegate
+ {
+ public:
+ CheckBoxDelegate(QObject *parent = 0);
+ //! Returns the editor widget used for editing the given index
+ QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option,const QModelIndex &index) const;
+ //! Set the data to be edited in the given editor
+ void setEditorData(QWidget *editor, const QModelIndex &index) const;
+
+ void setModelData(QWidget *editor, QAbstractItemModel *model,const QModelIndex &index) const;
+ void updateEditorGeometry(QWidget *editor,const QStyleOptionViewItem &option, const QModelIndex &index) const;
+ };
+
+ class FavouriteHubsModel : public QAbstractTableModel
+ {
+ friend class CheckBoxDelegate;
+ public:
+ FavouriteHubsModel(QWidget* parent=0) : QAbstractTableModel( parent )
+ {}
+ ~FavouriteHubsModel()
+ {}
+
+ // QAbstractTableModel methods...
+ int rowCount ( const QModelIndex& ) const
+ {
+ return entries.size();
+ }
+ int columnCount ( const QModelIndex& ) const
+ {
+ return 7;
+ }
+ QVariant data ( const QModelIndex & index, int role = Qt::DisplayRole ) const;
+ QVariant headerData ( int section, Qt::Orientation orientation, int role = Qt::DisplayRole ) const;
+
+ QString getServer(int row) const
+ {
+ return entries.at(row).server.c_str();
+ }
+
+ //! AbstractItemView overload. Called by the view when editing.
+ virtual bool setData ( const QModelIndex & index, const QVariant & value, int role = Qt::EditRole );
+ virtual Qt::ItemFlags flags ( const QModelIndex & index ) const;
+
+ void setHubs(const QList<rpc_types::FavouriteHub>&);
+ void addHub(const rpc_types::FavouriteHub&);
+ void removeHub(const string&);
+ protected:
+ rpc_types::FavouriteHub getHubData( const QModelIndex& index ) const;
+
+ private:
+ QList<rpc_types::FavouriteHub> entries;
+ };
+};
+
+
+#endif
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ui/filelog.cpp b/ui/filelog.cpp
new file mode 100644
index 0000000..b5d1fd3
--- /dev/null
+++ b/ui/filelog.cpp
@@ -0,0 +1,136 @@
+//
+// C++ Implementation: filelog
+//
+// Description:
+//
+//
+// Author: Arsenij Vodjanov <arsenij@gmail.com>, (C) 2005
+//
+// Copyright: See COPYING file that comes with this distribution
+//
+//
+
+#include "filelog.h"
+#include <QDateTime>
+#include <QFile>
+#include <QTextStream>
+
+#define FLOG_ENABLE
+
+// This should be retrieved from gui settings
+#define FLOG_FILE_NAME "/tmp/qtclient.log"
+
+
+FileLog::FileLog() {
+ time.start();
+ QString msg("--- New log started on %1");
+ printMessage(msg.arg(QDateTime::currentDateTime().toString("ddd MMMM d yyyy")));
+}
+
+void FileLog::info(const char* format, ...)
+{
+#ifdef FLOG_ENABLE
+ va_list va;
+ va_start(va,format);
+ printMessage(format,va);
+ va_end(va);
+#endif
+}
+
+void FileLog::info(const QString& msg) {
+#ifdef FLOG_ENABLE
+ printMessage(msg);
+#endif
+}
+
+void FileLog::error(const char* format, ...)
+{
+#ifdef FLOG_ENABLE
+ va_list va;
+ va_start(va,format);
+ printMessage(format,va);
+ va_end(va);
+#endif
+}
+
+void FileLog::error(const QString& msg)
+{
+#ifdef FLOG_ENABLE
+ printMessage(msg);
+#endif
+}
+
+void FileLog::warn(const char* format, ...)
+{
+#ifdef FLOG_ENABLE
+ va_list va;
+ va_start(va,format);
+ printMessage(format,va);
+ va_end(va);
+#endif
+}
+
+void FileLog::warn(const QString& msg)
+{
+#ifdef FLOG_ENABLE
+ printMessage(msg);
+#endif
+}
+
+void FileLog::debug(const char* format, ...)
+{
+#ifdef FLOG_ENABLE
+ va_list va;
+ va_start(va,format);
+ printMessage(format,va);
+ va_end(va);
+#endif
+}
+
+void FileLog::debug(const QString& msg)
+{
+#ifdef FLOG_ENABLE
+ printMessage(msg);
+#endif
+}
+
+void FileLog::msg(const QString& msg,const QColor&)
+{
+#ifdef FLOG_ENABLE
+ printMessage(msg);
+#endif
+}
+
+FileLog::~FileLog()
+{
+#ifdef FLOG_ENABLE
+ printMessage("--- End of log");
+#endif
+}
+
+void FileLog::printMessage(const QString msg)
+{
+#ifdef FLOG_ENABLE
+ time.restart();
+
+ QFile file(FLOG_FILE_NAME);
+ if (!file.open(QIODevice::Append | QIODevice::Text))
+ return;
+
+ QTextStream out(&file);
+ out << time.toString("hh:mm:ss.zzz") << ": " << msg;
+ if (!msg.endsWith("\n"))
+ out << endl;
+
+ file.close();
+#endif
+}
+
+void FileLog::printMessage(const char* format, va_list va)
+{
+#ifdef FLOG_ENABLE
+ QString msg;
+ msg.vsprintf(format,va);
+ printMessage(msg);
+#endif
+}
diff --git a/ui/filelog.h b/ui/filelog.h
new file mode 100644
index 0000000..b264673
--- /dev/null
+++ b/ui/filelog.h
@@ -0,0 +1,43 @@
+//
+// C++ Interface: filelog
+//
+// Description:
+//
+//
+// Author: Arsenij Vodjanov <arsenij@gmail.com>, (C) 2005
+//
+// Copyright: See COPYING file that comes with this distribution
+//
+//
+
+#ifndef FILELOG_H_
+#define FILELOG_H_
+
+#include "log.h"
+
+#include <QTime>
+
+
+//! Log implementation that logs to a file.
+class FileLog : public Log
+{
+public:
+ FileLog();
+ virtual void info(const char* format, ...);
+ virtual void info(const QString&);
+ virtual void error(const char* format, ...);
+ virtual void error(const QString&);
+ virtual void warn(const char* format, ...);
+ virtual void warn(const QString&);
+ virtual void debug(const char* format, ...);
+ virtual void debug(const QString&);
+ virtual void msg(const QString&,const QColor& color);
+ virtual ~FileLog();
+
+private:
+ void printMessage(const QString msg);
+ void printMessage(const char* format, va_list va);
+ QTime time;
+};
+
+#endif
diff --git a/ui/filetransfer.h b/ui/filetransfer.h
new file mode 100644
index 0000000..6fa7f75
--- /dev/null
+++ b/ui/filetransfer.h
@@ -0,0 +1,59 @@
+//
+// C++ Interface: filetransfer
+//
+// Description:
+//
+//
+// Author: Rikard Björklind <olof@linux.nu>, (C) 2005
+//
+// Copyright: See COPYING file that comes with this distribution
+//
+//
+#ifndef FILETRANSFER_H__
+#define FILETRANSFER_H__
+
+#include "global.h"
+#include "log.h"
+#include <QString>
+
+struct FileTransfer
+{
+ enum Type {UPLOAD=0,DOWNLOAD=1};
+ Type type;
+ qint64 pos;
+ qint64 startpos;
+ qint64 actual;
+ qint64 size;
+ qint64 averageSpeed;
+ qint64 secondsLeft;
+ qint64 bytesLeft;
+ QString filename;
+ QString localfilename;
+ QString tth;
+ QString status;
+
+ int userid;
+
+ //! This method might be quite incorrect, we have to check dc++ behaviour a little better.
+ bool operator==(const FileTransfer& ft) const {
+ // quick rejection test
+ logger->debug("comparing files");
+ if(type==ft.type) logger->debug("type samma");
+ if(type!=ft.type) return false;
+ // Use TTH if available for both
+ //if(!tth.isEmpty() && !ft.tth.isEmpty()) {
+ // logger->debug("tthcomp:" + QString::number(tth==ft.tth?1:0 ));;
+ // return tth==ft.tth;
+ //}
+ logger->debug("userid: " + QString::number(userid) + ", " + QString::number(ft.userid) + ", " + filename + ", " + ft.filename);
+ // No tth, use filename, user
+ return userid==ft.userid && filename==ft.filename;
+ }
+ QString toString() const
+ {
+ return QString("Type: ") + QString::number(type) + "\nFilename: " + filename + "\nsize: " + QString::number(size) + " userid:"+QString::number(userid);
+ }
+};
+
+
+#endif
diff --git a/ui/finisheditem.h b/ui/finisheditem.h
new file mode 100644
index 0000000..40486c8
--- /dev/null
+++ b/ui/finisheditem.h
@@ -0,0 +1,30 @@
+//
+// C++ Interface: FinishedItem
+//
+// Description:
+//
+//
+// Author: Rikard Björklind <olof@linux.nu>, (C) 2005
+//
+// Copyright: See COPYING file that comes with this distribution
+//
+//
+#ifndef FINISHED_ITEM_H__
+#define FINISHED_ITEM_H__
+
+struct FinishedItem
+{
+ int id;
+ int type;
+ QString target;
+ QString user;
+ QString hub;
+};
+
+
+
+#endif
+
+
+
+
diff --git a/ui/finishedmodel.cpp b/ui/finishedmodel.cpp
new file mode 100644
index 0000000..3c081d5
--- /dev/null
+++ b/ui/finishedmodel.cpp
@@ -0,0 +1,92 @@
+//
+// C++ Implementation: FinishedModel
+//
+// Description:
+//
+//
+// Author: Rikard Björklind <olof@linux.nu>, (C) 2005
+//
+// Copyright: See COPYING file that comes with this distribution
+//
+//
+#include "finishedmodel.h"
+#include <QtCore>
+#include "log.h"
+
+
+QVariant FinishedModel::data ( const QModelIndex & index, int role ) const
+{
+ if(!index.isValid()) return QVariant();
+ if(index.row() < 0 || index.row() >= itemList.size()) return QVariant();
+ if(index.column() < 0 || index.column()>4) return QVariant();
+ if(role == Qt::DisplayRole) {
+ int i = index.row();
+ switch(index.column()) {
+ case 0:
+ return itemList[i]->type==0?tr("Upload"):tr("Download");
+ case 1:
+ return itemList[i]->target;
+ case 2:
+ return itemList[i]->user;
+ case 3:
+ return itemList[i]->hub;
+ }
+ }
+ return QVariant();
+}
+
+QVariant FinishedModel::headerData ( int section, Qt::Orientation orientation, int role ) const
+{
+ if (role != Qt::DisplayRole)
+ return QVariant();
+
+ if(orientation==Qt::Horizontal) {
+
+ if(section==0) return tr("Type");
+ if(section==1) return tr("Target");
+ if(section==2) return tr("User");
+ if(section==3) return tr("Hub");
+ }
+ return QVariant();
+}
+
+void FinishedModel::onItemAdded( const FinishedItem& item)
+{
+ logger->debug("FinishedModel::onItemAdded: %d",item.id);
+ items[item.id] = item;
+ itemList.append( &items[item.id] );
+ emit layoutChanged();
+}
+
+void FinishedModel::onItemRemoved( int id )
+{
+ logger->debug("FinishedModel::onItemRemoved: %d",id);
+ itemList.removeAll(&items[id]);
+ items.remove(id);
+ emit layoutChanged();
+}
+
+void FinishedModel::onAllRemoved(int type)
+{
+ logger->debug("FinishedModel::onAllRemoved: %d",type);
+ QList<FinishedItem*>::iterator it = itemList.begin();
+ while(it != itemList.end()) {
+ FinishedItem *fi = *it;
+ if(fi->type == type)
+ {
+ it = itemList.erase(it);
+ items.remove(fi->id);
+ }
+ else
+ it++;
+ }
+
+
+}
+
+
+
+
+
+
+ \ No newline at end of file
diff --git a/ui/finishedmodel.h b/ui/finishedmodel.h
new file mode 100644
index 0000000..7f5874f
--- /dev/null
+++ b/ui/finishedmodel.h
@@ -0,0 +1,36 @@
+#ifndef FINISHED_MODEL_H__
+#define FINISHED_MODEL_H__
+
+#include "global.h"
+
+#include <QAbstractTableModel>
+#include <QList>
+#include "finisheditem.h"
+
+class FinishedModel : public QAbstractTableModel
+{
+ Q_OBJECT
+ public:
+ FinishedModel() {}
+ virtual ~FinishedModel() {}
+
+ int rowCount ( const QModelIndex& ) const {return itemList.size();}
+ int columnCount ( const QModelIndex& ) const {return 4;}
+ QVariant data ( const QModelIndex & index, int role = Qt::DisplayRole ) const;
+ QVariant headerData ( int section, Qt::Orientation orientation, int role = Qt::DisplayRole ) const;
+
+ public slots:
+ void onItemAdded( const FinishedItem& );
+ void onItemRemoved( int );
+ void onAllRemoved(int);
+
+ private:
+ QMap<int,FinishedItem> items;
+ QList<FinishedItem*> itemList;
+};
+
+
+
+
+#endif
+
diff --git a/ui/global.h b/ui/global.h
new file mode 100644
index 0000000..2dd769c
--- /dev/null
+++ b/ui/global.h
@@ -0,0 +1,12 @@
+#ifndef GLOBAL_H__
+#define GLOBAL_H__
+
+#include <QtGlobal>
+#include <boost/any.hpp>
+#include <list>
+
+//typedef qint64 int64;
+//#define int64 qint64
+typedef std::list<boost::any> anylist;
+
+#endif
diff --git a/ui/globalusermodel.cpp b/ui/globalusermodel.cpp
new file mode 100644
index 0000000..af1dd80
--- /dev/null
+++ b/ui/globalusermodel.cpp
@@ -0,0 +1,51 @@
+//
+// C++ Implementation: globalusermodel
+//
+// Description:
+//
+//
+// Author: Rikard Bjorklind <olof@users.sourceforge.net>, (C) 2006
+//
+// Copyright: See COPYING file that comes with this distribution
+//
+//
+#include "globalusermodel.h"
+
+GlobalUserModel::GlobalUserModel(QObject *parent)
+ : QObject(parent)
+{
+}
+
+
+GlobalUserModel::~GlobalUserModel()
+{
+}
+
+User* GlobalUserModel::getUser( int id )
+{
+ if( userMap.contains(id) ) return userMap[id];
+ return NULL;
+}
+
+void GlobalUserModel::usersUpdated( int , QList<User *> ulst)
+{
+ for(int i=0;i < ulst.size();i++)
+ {
+ User* u = ulst[i];
+ userMap[u->id] = u;
+ }
+}
+
+void GlobalUserModel::userRemoved( int , int userid)
+{
+ userMap.remove( userid);
+}
+
+void GlobalUserModel::sessionInfo( int , const QString &, const QString &, const QList< User * > ulst )
+{
+ for(int i=0;i < ulst.size();i++)
+ {
+ User* u = ulst[i];
+ userMap[u->id] = u;
+ }
+}
diff --git a/ui/globalusermodel.h b/ui/globalusermodel.h
new file mode 100644
index 0000000..68098c3
--- /dev/null
+++ b/ui/globalusermodel.h
@@ -0,0 +1,42 @@
+//
+// C++ Interface: globalusermodel
+//
+// Description:
+//
+//
+// Author: Rikard Bjorklind <olof@users.sourceforge.net>, (C) 2006
+//
+// Copyright: See COPYING file that comes with this distribution
+//
+//
+#ifndef GLOBALUSERMODEL_H
+#define GLOBALUSERMODEL_H
+
+#include <QObject>
+#include <QHash>
+#include "user.h"
+
+/**
+ Keeps a record of all users.
+ Needed for easy access by all components who deals with users at some point.
+ TODO: how are we going to clean this list when user objects are released in the backend?
+*/
+class GlobalUserModel : public QObject
+{
+ Q_OBJECT
+public:
+ GlobalUserModel(QObject *parent = 0);
+ ~GlobalUserModel();
+
+ User* getUser(int id);
+
+public slots:
+ void usersUpdated(int,QList<User*>);
+ void userRemoved(int,int);
+ void sessionInfo(int,const QString&,const QString&,const QList<User*>);
+
+private:
+ QHash<int,User*> userMap;
+};
+
+#endif
diff --git a/ui/hub.ui b/ui/hub.ui
new file mode 100644
index 0000000..e7f2db8
--- /dev/null
+++ b/ui/hub.ui
@@ -0,0 +1,72 @@
+<ui version="4.0" >
+ <author></author>
+ <comment></comment>
+ <exportmacro></exportmacro>
+ <class>HubForm</class>
+ <widget class="QWidget" name="HubForm" >
+ <property name="geometry" >
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>603</width>
+ <height>455</height>
+ </rect>
+ </property>
+ <property name="windowTitle" >
+ <string>Form</string>
+ </property>
+ <layout class="QHBoxLayout" >
+ <property name="margin" >
+ <number>9</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item>
+ <widget class="QSplitter" name="splitter" >
+ <property name="orientation" >
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <widget class="QSplitter" name="splitter" >
+ <property name="orientation" >
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <widget class="QWidget" name="layoutWidget" >
+ <layout class="QVBoxLayout" >
+ <property name="margin" >
+ <number>0</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item>
+ <widget class="QTextEdit" name="mainChat" />
+ </item>
+ <item>
+ <widget class="QLineEdit" name="chatEdit" />
+ </item>
+ </layout>
+ </widget>
+ </widget>
+ <widget class="QTreeView" name="userView" >
+ <property name="contextMenuPolicy" >
+ <enum>Qt::CustomContextMenu</enum>
+ </property>
+ <property name="indentation" >
+ <number>10</number>
+ </property>
+ <property name="uniformRowHeights" >
+ <bool>true</bool>
+ </property>
+ <property name="itemsExpandable" >
+ <bool>false</bool>
+ </property>
+ </widget>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <pixmapfunction></pixmapfunction>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/ui/images/arrows.bmp b/ui/images/arrows.bmp
new file mode 100644
index 0000000..510ad23
--- /dev/null
+++ b/ui/images/arrows.bmp
Binary files differ
diff --git a/ui/images/browse.png b/ui/images/browse.png
new file mode 100644
index 0000000..b95e331
--- /dev/null
+++ b/ui/images/browse.png
Binary files differ
diff --git a/ui/images/dldir.png b/ui/images/dldir.png
new file mode 100644
index 0000000..037c2da
--- /dev/null
+++ b/ui/images/dldir.png
Binary files differ
diff --git a/ui/images/dldirto.png b/ui/images/dldirto.png
new file mode 100644
index 0000000..32baf9c
--- /dev/null
+++ b/ui/images/dldirto.png
Binary files differ
diff --git a/ui/images/download.png b/ui/images/download.png
new file mode 100644
index 0000000..5829fe4
--- /dev/null
+++ b/ui/images/download.png
Binary files differ
diff --git a/ui/images/downloadto.png b/ui/images/downloadto.png
new file mode 100644
index 0000000..8f21884
--- /dev/null
+++ b/ui/images/downloadto.png
Binary files differ
diff --git a/ui/images/exit.png b/ui/images/exit.png
new file mode 100644
index 0000000..f1ca32a
--- /dev/null
+++ b/ui/images/exit.png
Binary files differ
diff --git a/ui/images/filelist.png b/ui/images/filelist.png
new file mode 100644
index 0000000..f1b7236
--- /dev/null
+++ b/ui/images/filelist.png
Binary files differ
diff --git a/ui/images/folders.bmp b/ui/images/folders.bmp
new file mode 100644
index 0000000..cdebf43
--- /dev/null
+++ b/ui/images/folders.bmp
Binary files differ
diff --git a/ui/images/publichubs.png b/ui/images/publichubs.png
new file mode 100644
index 0000000..350ec81
--- /dev/null
+++ b/ui/images/publichubs.png
Binary files differ
diff --git a/ui/images/remove.png b/ui/images/remove.png
new file mode 100644
index 0000000..b2cc9be
--- /dev/null
+++ b/ui/images/remove.png
Binary files differ
diff --git a/ui/images/search.png b/ui/images/search.png
new file mode 100644
index 0000000..b95e331
--- /dev/null
+++ b/ui/images/search.png
Binary files differ
diff --git a/ui/images/settings.png b/ui/images/settings.png
new file mode 100644
index 0000000..d81e2fb
--- /dev/null
+++ b/ui/images/settings.png
Binary files differ
diff --git a/ui/images/toolbar.bmp b/ui/images/toolbar.bmp
new file mode 100644
index 0000000..62ffbee
--- /dev/null
+++ b/ui/images/toolbar.bmp
Binary files differ
diff --git a/ui/images/toolbar20-highlight.bmp b/ui/images/toolbar20-highlight.bmp
new file mode 100644
index 0000000..6691419
--- /dev/null
+++ b/ui/images/toolbar20-highlight.bmp
Binary files differ
diff --git a/ui/images/toolbar20.bmp b/ui/images/toolbar20.bmp
new file mode 100644
index 0000000..a3dcc51
--- /dev/null
+++ b/ui/images/toolbar20.bmp
Binary files differ
diff --git a/ui/images/tthsearch.png b/ui/images/tthsearch.png
new file mode 100644
index 0000000..f9451cc
--- /dev/null
+++ b/ui/images/tthsearch.png
Binary files differ
diff --git a/ui/images/user-dcpp-op.png b/ui/images/user-dcpp-op.png
new file mode 100644
index 0000000..d580e5c
--- /dev/null
+++ b/ui/images/user-dcpp-op.png
Binary files differ
diff --git a/ui/images/user-dcpp-passive-op.png b/ui/images/user-dcpp-passive-op.png
new file mode 100644
index 0000000..36d1815
--- /dev/null
+++ b/ui/images/user-dcpp-passive-op.png
Binary files differ
diff --git a/ui/images/user-dcpp-passive.png b/ui/images/user-dcpp-passive.png
new file mode 100644
index 0000000..520e36b
--- /dev/null
+++ b/ui/images/user-dcpp-passive.png
Binary files differ
diff --git a/ui/images/user-dcpp.png b/ui/images/user-dcpp.png
new file mode 100644
index 0000000..bfc819c
--- /dev/null
+++ b/ui/images/user-dcpp.png
Binary files differ
diff --git a/ui/images/user-normal-op.png b/ui/images/user-normal-op.png
new file mode 100644
index 0000000..81d70a0
--- /dev/null
+++ b/ui/images/user-normal-op.png
Binary files differ
diff --git a/ui/images/user-normal.png b/ui/images/user-normal.png
new file mode 100644
index 0000000..415942d
--- /dev/null
+++ b/ui/images/user-normal.png
Binary files differ
diff --git a/ui/images/user-passive-op.png b/ui/images/user-passive-op.png
new file mode 100644
index 0000000..a7bf465
--- /dev/null
+++ b/ui/images/user-passive-op.png
Binary files differ
diff --git a/ui/images/user-passive.png b/ui/images/user-passive.png
new file mode 100644
index 0000000..f1f6519
--- /dev/null
+++ b/ui/images/user-passive.png
Binary files differ
diff --git a/ui/images/users.bmp b/ui/images/users.bmp
new file mode 100644
index 0000000..77a4c20
--- /dev/null
+++ b/ui/images/users.bmp
Binary files differ
diff --git a/ui/log.h b/ui/log.h
new file mode 100644
index 0000000..3115ded
--- /dev/null
+++ b/ui/log.h
@@ -0,0 +1,37 @@
+//
+// C++ Interface: log
+//
+// Description:
+//
+//
+// Author: Rikard Björklind <olof@linux.nu>, (C) 2005
+//
+// Copyright: See COPYING file that comes with this distribution
+//
+//
+#ifndef LOG_H__
+#define LOG_H__
+
+#include <QString>
+#include <QColor>
+
+//! Logging interface
+class Log
+{
+ public:
+ virtual void info(const char* format, ...) = 0;
+ virtual void info(const QString&) = 0;
+ virtual void error(const char* format, ...) = 0;
+ virtual void error(const QString&) = 0;
+ virtual void warn(const char* format, ...) = 0;
+ virtual void warn(const QString&) = 0;
+ virtual void debug(const char* format, ...) = 0;
+ virtual void debug(const QString&) = 0;
+ virtual void msg(const QString&,const QColor& color) = 0;
+ virtual ~Log() {}
+};
+
+extern Log* logger;
+
+
+#endif
diff --git a/ui/main.cpp b/ui/main.cpp
new file mode 100644
index 0000000..672bdce
--- /dev/null
+++ b/ui/main.cpp
@@ -0,0 +1,54 @@
+#include "mainwindow.h"
+#include "filelog.h"
+
+#include "rpcdriver/rpcdriver.h"
+
+#include <QApplication>
+#include <QMessageBox>
+
+#include <string>
+#include <iostream>
+#include <boost/program_options.hpp>
+namespace opt = boost::program_options;
+
+using std::string;
+
+Log* logger = new FileLog;
+
+int main(int argc, char *argv[])
+{
+ opt::options_description desc("Allowed options");
+ desc.add_options()
+ ("help",
+ "Display help message")
+ ("addr",
+ opt::value<string>()->default_value("localhost"),
+ "Address to connect to")
+ ("port",
+ opt::value<int>()->default_value(6161),
+ "Specify port");
+
+ opt::variables_map vm;
+
+ try {
+ opt::store(opt::parse_command_line(argc, argv, desc), vm);
+ }
+ catch( const opt::unknown_option& e ) {
+ std::cerr << "Invalid option " << e.what() << std::endl;
+ }
+
+ if(vm.count("help")) {
+ std::cout << desc << std::endl;
+ return 1;
+ }
+
+ QApplication app(argc, argv);
+ MainWindow *mw = new MainWindow;
+
+ mw->show();
+ //mw->connectToBackend(vm["addr"].as<string>().c_str(), port);
+ // TODO use the above function instead if parameters was actually given -> overrides settings.
+ mw->connectToBackend();
+ return app.exec();
+}
+
diff --git a/ui/mainwindow.cpp b/ui/mainwindow.cpp
new file mode 100644
index 0000000..a61302e
--- /dev/null
+++ b/ui/mainwindow.cpp
@@ -0,0 +1,453 @@
+
+#include <QtGui>
+
+#include <iostream>
+#include <boost/bind.hpp>
+
+#include "mainwindow.h"
+#include "connectdialog.h"
+#include "commandhandlers.h"
+#include "transferlistitemdelegate.h"
+#include "userfiledialog.h"
+
+using namespace std;
+using namespace ui_cmd_handlers;
+
+MainWindow::MainWindow()
+{
+ ui.setupUi(this);
+ QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8"));
+ QPushButton* b = new QPushButton(ui.hubTabs);
+ QIcon i(":/images/remove.png");
+ b->setIcon(i);
+ b->setFlat(true);
+ b->setToolTip(tr("Disconnect from hub"));
+ ui.hubTabs->removeTab(0);
+ ui.hubTabs->setCornerWidget(b);
+ connect(b,SIGNAL(pressed()),SLOT(onDisconnectPressed()));
+
+ backendRetriesLeft=-1;
+ statusBar()->showMessage(tr("Hello"),2000);
+ shareStatusLbl = new QLabel(tr("Total shared: 0b"));
+ shareStatusLbl->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken);
+ statusBar()->addPermanentWidget(shareStatusLbl);
+
+}
+
+MainWindow::~MainWindow()
+{}
+
+void MainWindow::closeEvent(QCloseEvent* e)
+{
+ int i = QMessageBox::question(this,tr("Quit"),tr("Keep backend running?"),tr("&Yes"),tr("&No"),tr("&Cancel"),1,2);
+ if(i==1)
+ {
+ if(backendConnection)
+ backendConnection->die();
+ QThread::currentThread()->wait(1000); // TODO dont sleep. make a better way of knowing that the command has completed
+ driver->stopSender();
+ driver->waitForCompletion();
+ e->accept();
+ }
+ else if(i==0) e->accept();
+ else e->ignore();
+}
+
+void MainWindow::on_actionConnect_triggered()
+{
+ if(connectDialog)
+ connectDialog->show();
+}
+
+void MainWindow::on_actionSettings_triggered( )
+{
+ if(settingsDialog)
+ settingsDialog->show();
+}
+
+void MainWindow::on_actionSearch_triggered( )
+{
+ if(searchManager)
+ searchManager->show(this);
+}
+
+void MainWindow::userFileListing( const UserFileModelPtr& model )
+{
+ UserFileDialog* fileDialog = new UserFileDialog( model, backendConnection, userModel );
+ fileDialog->show();
+}
+
+bool MainWindow::connectToBackend( const char* hostname, int port )
+{
+ try
+ {
+ driver = boost::shared_ptr< rpc::RpcClientDriver >(new rpc::RpcClientDriver(hostname, port));
+ backendConnection = boost::shared_ptr<BackendConnection>(new BackendConnection(driver));
+ userModel = boost::shared_ptr<GlobalUserModel>(new GlobalUserModel(this));
+
+ cout << "Creating dialogs and models" << endl;
+
+ // Create all dialogs and data models. This code must not make any calls to the backend!
+ if(settingsDialog)
+ delete settingsDialog;
+ settingsDialog = new SettingsDialog(backendConnection,this);
+ if(sessionManager)
+ delete sessionManager;
+ sessionManager = new SessionManager(ui.hubTabs,backendConnection,this);
+ connect(sessionManager,SIGNAL(currentHubTotalShare(qint64)),SLOT(updateTotalShareStatus(qint64)));
+ if(searchManager)
+ delete searchManager;
+ searchManager = new SearchManager(backendConnection,this);
+ if(queueModel)
+ delete queueModel;
+ queueModel = new QueueModel(userModel);
+ ui.queueView->setModel(queueModel);
+ ui.queueView->addAction(ui.actionCancelDownload);
+ ui.queueView->setContextMenuPolicy(Qt::CustomContextMenu);
+ connect(ui.queueView,SIGNAL(customContextMenuRequested ( const QPoint&)),SLOT(onQueueContextMenu(const QPoint&)));
+ if(finishedModel)
+ delete finishedModel;
+ finishedModel = new FinishedModel;
+ ui.finishedView->setModel(finishedModel);
+ if(transferModel)
+ delete transferModel;
+ transferModel = new TransferListModel;
+ ui.transfersView->setModel(transferModel);
+ ui.transfersView->setItemDelegate( new TransferListItemDelegate );
+ ui.transfersView->addAction(ui.actionForceAttempt);
+ ui.transfersView->setContextMenuPolicy(Qt::ActionsContextMenu);
+
+ if(connectDialog)
+ delete connectDialog;
+ connectDialog = new ConnectDialog(backendConnection, this);
+
+ cout << "Done creating" << endl;
+
+ // End create dialogs and models.
+
+ // Register all command handlers and connect their signals to our dialogs and models.
+ registerCommandHandlers();
+
+ cout << "connnect!" << endl;
+ // Finally, connect to the backend.
+ driver->connect();
+
+ // -------------------------------------------------------------------------------------------
+ // Everything is up and running! That means we can authenticate and request running sessions.
+ // -------------------------------------------------------------------------------------------
+ backendConnection->authenticate(password);
+ QThread::currentThread()->wait(500);
+ backendConnection->requestRunningSessions();
+ // And get the hub lists
+ connectDialog->getLists();
+
+ }
+ catch( ... )
+ {
+ logger->error("Exception in connectToBackend()");
+ return false;
+ }
+ return true;
+}
+
+void MainWindow::connectToBackend( )
+{
+ QSettings settings("dc-qt.sf.net","dcqt");
+ SettingsDialog::BackendConnectionType backendConnectionType =
+ (SettingsDialog::BackendConnectionType)settings.value("bctype",(int)SettingsDialog::USE_LOCAL_BACKEND).toInt();
+ QString backendConnectionURL = settings.value("bcurl","localhost").toString();
+ int localPort = settings.value("bclocalport",6161).toInt();
+ int remotePort = settings.value("bcremoteport",6161).toInt();
+ password = settings.value("bcpassword",QString::number(rand() % 100000 + 100000)).toString();
+
+ if( backendConnectionType == SettingsDialog::USE_LOCAL_BACKEND)
+ {
+ if(!connectToBackend("localhost",localPort))
+ {
+ // Ok, connection failed, try to start a backend
+ // Figure out the path of the executable
+ if( backendRetriesLeft > 0 )
+ {
+ backendRetriesLeft--;
+ QTimer::singleShot( 3000, this, SLOT(connectToBackend()));
+
+ }
+ else if(backendRetriesLeft==0)
+ {
+ QMessageBox::critical(this, "Error", "Backend started but could not connect.");
+ }
+ else
+ {
+ QString dir = QCoreApplication::applicationDirPath();
+ QString path;
+ if(QFile::exists(dir+"/backend"))
+ {
+ path = dir+"/backend";
+ }
+ else if(QFile::exists(dir+"/../backend/backend"))
+ {
+ path = dir+"/../backend/backend";
+ }
+ else if(QFile::exists(dir+"/../Resources/backend"))
+ {
+ path = dir+"/../Resources/backend";
+ }
+ else
+ QMessageBox::critical(this,"Error","Could not find backend executable (installation problem?).");
+
+ if(!path.isEmpty())
+ {
+ QStringList args;
+ args+="--port";
+ args+=QString::number(localPort);
+ args+="--password";
+ args+=password;
+ logger->info(QString("Trying to start backend at %1").arg(path));
+ if( !QProcess::startDetached(path,args) )
+ {
+ logger->error("Error starting backend");
+ QMessageBox::critical(this,"Error","Could not start the backend.");
+ }
+ else
+ {
+ // Ok, the backend is starting at this point. We need to give it some time to initialize.
+ // This will take different amount of time on different machines.
+ QTimer::singleShot( 2000, this, SLOT(connectToBackend()));
+ backendRetriesLeft=5;
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ if(!connectToBackend( backendConnectionURL.toAscii(), remotePort ))
+ {
+ QMessageBox::critical(this, "Error", QString("Error connecting to backend at %1:%2").arg(backendConnectionURL).arg(remotePort));
+ }
+ }
+
+}
+
+// temporary place
+void MainWindow::registerCommandHandlers()
+{
+ // SettingsInfo
+ ui_cmd_handlers::settingsInfoHandler* p =new ui_cmd_handlers::settingsInfoHandler( boost::bind(&rpc::RpcDriver::queueCommand, driver.get(),_1,_2) );
+ rpc::RpcCommandHandlerPtr settingsInfo( p );
+ driver->registerCommand( settingsInfo );
+
+
+ connect(p,SIGNAL(settingsInfo(const QList<QString>&,const QList<QVariant>&)),
+ settingsDialog,SLOT(settingsInfo(const QList<QString>&,const QList<QVariant>&)));
+
+ // SessionCreated
+ ui_cmd_handlers::GuiCommandHandlerBase* ptr = new ui_cmd_handlers::SessionCreatedHandler(boost::bind(&rpc::RpcDriver::queueCommand, driver.get(),_1,_2));
+ rpc::RpcCommandHandlerPtr rpccptr( ptr );
+ driver->registerCommand( rpccptr );
+ connect(ptr,SIGNAL(sessionCreated(int)),sessionManager,SLOT(createSession(int)));
+
+ // HubConnected
+ ptr = new ui_cmd_handlers::HubConnectedHandler(boost::bind(&rpc::RpcDriver::queueCommand, driver.get(),_1,_2));
+ rpccptr = rpc::RpcCommandHandlerPtr(ptr);
+ driver->registerCommand( rpccptr );
+ connect(ptr,SIGNAL(hubConnected(int)),sessionManager,SLOT(createSession(int)));
+
+ // HubUpdated
+ ptr = new ui_cmd_handlers::HubUpdatedHandler(boost::bind(&rpc::RpcDriver::queueCommand, driver.get(),_1,_2));
+ rpccptr = rpc::RpcCommandHandlerPtr(ptr);
+ driver->registerCommand( rpccptr );
+ connect(ptr,SIGNAL(hubUpdated(int,const QString&)),sessionManager,SLOT(hubUpdated(int,const QString&)));
+
+ // HubUpdated
+ ptr = new ui_cmd_handlers::HubStatsHandler(boost::bind(&rpc::RpcDriver::queueCommand, driver.get(),_1,_2));
+ rpccptr = rpc::RpcCommandHandlerPtr(ptr);
+ driver->registerCommand( rpccptr );
+ connect(ptr,SIGNAL(hubStats(int,qint64)),sessionManager,SLOT(onHubStats(int,qint64)));
+
+ // ConnectionFailed
+ ptr = new ui_cmd_handlers::ConnectionFailedHandler(boost::bind(&rpc::RpcDriver::queueCommand, driver.get(),_1,_2));
+ rpccptr = rpc::RpcCommandHandlerPtr(ptr);
+ driver->registerCommand( rpccptr );
+ connect(ptr,SIGNAL(connectionFailed(int,const QString&)),sessionManager,SLOT(connectionFailed(int,const QString&)));
+
+ // PrivateChat
+ ptr = new ui_cmd_handlers::PrivateChatHandler(boost::bind(&rpc::RpcDriver::queueCommand, driver.get(),_1,_2));
+ rpccptr = rpc::RpcCommandHandlerPtr(ptr);
+ driver->registerCommand( rpccptr );
+ connect(ptr,SIGNAL(privateChat(int,const QString&,const QString&)),sessionManager,SLOT(privateChat(int,const QString&,const QString&)));
+
+ // UsersUpdated
+ ptr = new ui_cmd_handlers::UsersUpdatedHandler(boost::bind(&rpc::RpcDriver::queueCommand, driver.get(),_1,_2));
+ rpccptr = rpc::RpcCommandHandlerPtr(ptr);
+ driver->registerCommand( rpccptr );
+ connect(ptr,SIGNAL(usersUpdated(int,QList<User*>)),sessionManager,SLOT(usersUpdated(int,QList<User*>)));
+ connect(ptr,SIGNAL(usersUpdated(int,QList<User*>)),userModel.get(),SLOT(usersUpdated(int,QList<User*>)));
+
+ // UserRemoved
+ ptr = new ui_cmd_handlers::UserRemovedHandler(boost::bind(&rpc::RpcDriver::queueCommand, driver.get(),_1,_2));
+ rpccptr = rpc::RpcCommandHandlerPtr(ptr);
+ driver->registerCommand( rpccptr );
+ connect(ptr,SIGNAL(userRemoved(int,int)),sessionManager,SLOT(userRemoved(int,int)));
+ connect(ptr,SIGNAL(userRemoved(int,int)),userModel.get(),SLOT(userRemoved(int,int)));
+
+ // ChatMessage
+ ptr = new ui_cmd_handlers::ChatMessageHandler(boost::bind(&rpc::RpcDriver::queueCommand, driver.get(),_1,_2));
+ rpccptr = rpc::RpcCommandHandlerPtr(ptr);
+ driver->registerCommand( rpccptr );
+ connect(ptr,SIGNAL(chatMessage(int,const QString&)),sessionManager,SLOT(chatMessage(int,const QString&)));
+
+ // RunningSessions
+ ptr = new ui_cmd_handlers::RunningSessionsHandler(boost::bind(&rpc::RpcDriver::queueCommand, driver.get(),_1,_2));
+ rpccptr = rpc::RpcCommandHandlerPtr(ptr);
+ driver->registerCommand( rpccptr );
+ connect(ptr,SIGNAL(sessionInfo(int,const QString&,const QString&,const QList<User*>)),
+ sessionManager,SLOT(sessionInfo(int,const QString&,const QString,const QList<User*>)));
+ connect(ptr,SIGNAL(sessionInfo(int,const QString&,const QString&,const QList<User*>)),
+ userModel.get(),SLOT(sessionInfo(int,const QString&,const QString,const QList<User*>)));
+ connect(ptr,SIGNAL(queueList(const QList<QueueItem>&)), queueModel, SLOT(onItemsAdded(const QList<QueueItem>&)));
+
+ // SharedDirs
+ ptr = new ui_cmd_handlers::SharedDirsHandler(boost::bind(&rpc::RpcDriver::queueCommand, driver.get(),_1,_2));
+ rpccptr = rpc::RpcCommandHandlerPtr(ptr);
+ driver->registerCommand( rpccptr );
+ connect(ptr,SIGNAL(sharedDirs(const QList<ShareItem>)),settingsDialog,SLOT(sharedDirs(const QList<ShareItem>)));
+
+ // SearchResults
+ ptr = new ui_cmd_handlers::SearchResultsHandler(boost::bind(&rpc::RpcDriver::queueCommand, driver.get(),_1,_2));
+ rpccptr = rpc::RpcCommandHandlerPtr(ptr);
+ driver->registerCommand( rpccptr );
+ connect(ptr,SIGNAL(searchResults(int,QList<SearchEntry>&)),searchManager,SLOT(searchResults(int,QList<SearchEntry>&)));
+
+ // NewHubList
+ ptr = new ui_cmd_handlers::PublicHubListHandler(boost::bind(&rpc::RpcDriver::queueCommand, driver.get(),_1,_2));
+ rpccptr = rpc::RpcCommandHandlerPtr(ptr);
+ driver->registerCommand( rpccptr );
+ connect(ptr,SIGNAL(hubList(const QList<rpc_types::HubEntry>&)),connectDialog,SLOT(publicHubList(const QList<rpc_types::HubEntry>&)));
+
+ // FavouriteHubList
+ ptr = new ui_cmd_handlers::FavouriteHubListHandler(boost::bind(&rpc::RpcDriver::queueCommand, driver.get(),_1,_2));
+ rpccptr = rpc::RpcCommandHandlerPtr(ptr);
+ driver->registerCommand( rpccptr );
+ connect(ptr,SIGNAL(hubList(const QList<rpc_types::FavouriteHub>&)),connectDialog,SLOT(favouriteHubList(const QList<rpc_types::FavouriteHub>&)));
+
+ // FavouriteHubList
+ ptr = new ui_cmd_handlers::FavouriteHubAddedHandler(boost::bind(&rpc::RpcDriver::queueCommand, driver.get(),_1,_2));
+ rpccptr = rpc::RpcCommandHandlerPtr(ptr);
+ driver->registerCommand( rpccptr );
+ connect(ptr,SIGNAL(favHubAdded(const rpc_types::FavouriteHub&)),connectDialog,SLOT(favouriteHubAdded(const rpc_types::FavouriteHub&)));
+
+ // FavouriteHubList
+ ptr = new ui_cmd_handlers::FavouriteHubRemovedHandler(boost::bind(&rpc::RpcDriver::queueCommand, driver.get(),_1,_2));
+ rpccptr = rpc::RpcCommandHandlerPtr(ptr);
+ driver->registerCommand( rpccptr );
+ connect(ptr,SIGNAL(favHubRemoved(const string&)),connectDialog,SLOT(favouriteHubRemoved(const string&)));
+
+ // QueueEvent
+ ptr = new ui_cmd_handlers::QueueEventHandler(boost::bind(&rpc::RpcDriver::queueCommand, driver.get(),_1,_2));
+ rpccptr = rpc::RpcCommandHandlerPtr(ptr);
+ driver->registerCommand( rpccptr );
+ connect(ptr,SIGNAL(queueItemAdded(const QueueItem&)),queueModel,SLOT(onItemAdded(const QueueItem&)));
+ connect(ptr,SIGNAL(queueItemRemoved(int)),queueModel,SLOT(onItemRemoved(int)));
+ connect(ptr,SIGNAL(queueItemFinshed(int)),queueModel,SLOT(onFinished(int)));
+ connect(ptr,SIGNAL(sourcesUpdated(int, QList<int>)),queueModel,SLOT(onSourcesUpdated(int,QList<int>)));
+ connect(ptr,SIGNAL(statusUpdated(int, int, int, int)),queueModel,SLOT(onStatusUpdated(int,int,int,int)));
+
+ // FinishedEvent
+ ptr = new ui_cmd_handlers::FinishedEventHandler(boost::bind(&rpc::RpcDriver::queueCommand, driver.get(),_1,_2));
+ rpccptr = rpc::RpcCommandHandlerPtr(ptr);
+ driver->registerCommand( rpccptr );
+ connect(ptr,SIGNAL(itemAdded(const FinishedItem&)),finishedModel,SLOT(onItemAdded(const FinishedItem&)));
+ connect(ptr,SIGNAL(itemRemoved(int)),finishedModel,SLOT(onItemRemoved(int)));
+ connect(ptr,SIGNAL(allRemoved(int)),finishedModel,SLOT(onAllRemoved(int)));
+
+ // TransferEvent
+ ptr = new ui_cmd_handlers::TransferEventHandler(boost::bind(&rpc::RpcDriver::queueCommand, driver.get(),_1,_2));
+ rpccptr = rpc::RpcCommandHandlerPtr(ptr);
+ driver->registerCommand( rpccptr );
+ connect(ptr,SIGNAL( transferStarting(const FileTransfer&)), transferModel, SLOT(onTransferStart(const FileTransfer&)));
+ connect(ptr,SIGNAL( transferTick(const QList<FileTransfer>&)), transferModel, SLOT(onTransferTick(const QList<FileTransfer>&)));
+ connect(ptr,SIGNAL( transferComplete(const FileTransfer&)), transferModel, SLOT(onTransferComplete(const FileTransfer&)));
+ connect(ptr,SIGNAL( transferFailed(const FileTransfer&,const QString&)), transferModel, SLOT(onTransferFailed(const FileTransfer&,const QString&)));
+
+ // UerFileList
+ ptr = new ui_cmd_handlers::UserFileListHandler(boost::bind(&rpc::RpcDriver::queueCommand, driver.get(),_1,_2));
+ rpccptr = rpc::RpcCommandHandlerPtr(ptr);
+ driver->registerCommand( rpccptr );
+ connect(ptr,SIGNAL(fileListing(const UserFileModelPtr& )),
+ this,SLOT(userFileListing(const UserFileModelPtr& )));
+
+ // Hashinfo
+ ptr = new ui_cmd_handlers::HashInfoHandler(boost::bind(&rpc::RpcDriver::queueCommand, driver.get(),_1,_2));
+ rpccptr = rpc::RpcCommandHandlerPtr(ptr);
+ driver->registerCommand( rpccptr );
+}
+
+void MainWindow::on_actionForceAttempt_triggered( )
+{
+ QModelIndex mi = ui.transfersView->selectionModel()->currentIndex();
+ if(mi.isValid())
+ {
+ FileTransfer ft = transferModel->data(mi.row());
+ backendConnection->forceConnectionAttempt(ft.userid);
+ }
+}
+
+void MainWindow::on_actionCancelDownload_triggered( )
+{
+ QModelIndex mi = ui.queueView->selectionModel()->currentIndex();
+ if(mi.isValid())
+ {
+ QueueItem* qi = queueModel->getItem(mi.row());
+ if(qi)
+ backendConnection->removeQueueItem(qi->id);
+ }
+}
+
+void MainWindow::onDisconnectPressed( )
+{
+ if(sessionManager) sessionManager->closeCurrentSession();
+}
+
+void MainWindow::updateTotalShareStatus( qint64 s)
+{
+ shareStatusLbl->setText( tr("Total shared: ") + Util::bytesToStr(s));
+}
+
+void MainWindow::onQueueContextMenu( const QPoint &p )
+{
+ if( ui.queueView->indexAt(p).isValid() ) {
+ // Build context menu
+ QMenu *contextMenu = new QMenu(tr("Queue Menu"),this);
+ QueueItem* item = queueModel->getItem( ui.queueView->indexAt(p).row() );
+ contextMenu->addAction(ui.actionCancelDownload);
+ QMenu *srcMenu = contextMenu->addMenu(tr("Remove source"));
+ QList< QPair<QAction*,int> > al;
+ for(int i = 0;i < item->sources.size();i++) {
+ User* u = userModel->getUser(item->sources[i]);
+ if(u!=NULL) {
+ QString username = u->nick;
+ QAction *a = new QAction(username,srcMenu);
+ al+= QPair<QAction*,int>(a,item->sources[i]);
+ srcMenu->addAction(a);
+ }
+ }
+
+ QAction *a = contextMenu->exec(QCursor::pos());
+
+ if( a == ui.actionCancelDownload ) {
+ logger->debug("cancel dl");
+ } else {
+ // Find the action in the array
+ logger->debug("array action");
+ for(int i=0;i < al.size();i++) {
+ if( al[i].first == a ) {
+ logger->debug("remove source " + QString::number(al[i].second));
+ backendConnection->removeSource( item->id, al[i].second );
+ }
+ }
+ }
+
+ delete contextMenu;
+ }
+}
diff --git a/ui/mainwindow.h b/ui/mainwindow.h
new file mode 100644
index 0000000..cabf380
--- /dev/null
+++ b/ui/mainwindow.h
@@ -0,0 +1,81 @@
+#ifndef MAINWINDOW_H
+#define MAINWINDOW_H
+
+#include "ui_mainwindow.h"
+#include "backendconnection.h"
+#include "settingsdialog.h"
+#include "commandhandlers.h"
+#include "sessionmanager.h"
+#include "searchmanager.h"
+#include "globalusermodel.h"
+#include "queuemodel.h"
+#include "finishedmodel.h"
+#include "transferlistmodel.h"
+#include "userfilemodel.h"
+
+#include <QMainWindow>
+#include <QPointer>
+#include <boost/shared_ptr.hpp>
+#include <rpcdriver.h>
+
+class ConnectDialog;
+class QLabel;
+
+using namespace ui_cmd_handlers;
+
+class MainWindow : public QMainWindow
+{
+ Q_OBJECT
+public:
+ MainWindow();
+ virtual ~MainWindow();
+
+ /**
+ * Connects to the backend. TODO add code for connecting to another instance when already connected
+ */
+ bool connectToBackend(const char* hostname,int port);
+
+ public slots:
+ /**
+ * Connects to the backend using local settings.
+ */
+ void connectToBackend();
+
+protected:
+ virtual void closeEvent(QCloseEvent*);
+
+private:
+ Ui::MainWindow ui;
+ QLabel *shareStatusLbl;
+ boost::shared_ptr<BackendConnection> backendConnection;
+ QPointer<SettingsDialog> settingsDialog;
+ QPointer<SessionManager> sessionManager;
+ QPointer<SearchManager> searchManager;
+ QPointer<ConnectDialog> connectDialog;
+ QPointer<QueueModel> queueModel;
+ QPointer<FinishedModel> finishedModel;
+ QPointer<TransferListModel> transferModel;
+
+ boost::shared_ptr< rpc::RpcClientDriver > driver;
+ boost::shared_ptr< GlobalUserModel > userModel;
+
+ int backendRetriesLeft;
+
+ //! Registers the command handlers
+ void registerCommandHandlers();
+
+ QString password;
+
+private slots:
+ void on_actionConnect_triggered();
+ void on_actionSettings_triggered();
+ void on_actionSearch_triggered();
+ void on_actionForceAttempt_triggered();
+ void on_actionCancelDownload_triggered();
+ void userFileListing( const UserFileModelPtr& model );
+ void onDisconnectPressed();
+ void updateTotalShareStatus(qint64);
+ void onQueueContextMenu(const QPoint&);
+};
+
+#endif
diff --git a/ui/mainwindow.ui b/ui/mainwindow.ui
new file mode 100644
index 0000000..c92e890
--- /dev/null
+++ b/ui/mainwindow.ui
@@ -0,0 +1,210 @@
+<ui version="4.0" >
+ <author></author>
+ <comment></comment>
+ <exportmacro></exportmacro>
+ <class>MainWindow</class>
+ <widget class="QMainWindow" name="MainWindow" >
+ <property name="geometry" >
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>699</width>
+ <height>568</height>
+ </rect>
+ </property>
+ <property name="windowTitle" >
+ <string>dcqt</string>
+ </property>
+ <widget class="QWidget" name="centralwidget" >
+ <layout class="QVBoxLayout" >
+ <property name="margin" >
+ <number>9</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item>
+ <widget class="QSplitter" name="splitter" >
+ <property name="orientation" >
+ <enum>Qt::Vertical</enum>
+ </property>
+ <widget class="QTabWidget" name="hubTabs" >
+ <widget class="QWidget" name="tab_4" >
+ <attribute name="title" >
+ <string>Tab 2</string>
+ </attribute>
+ </widget>
+ </widget>
+ <widget class="QTabWidget" name="transferTabs" >
+ <property name="tabShape" >
+ <enum>QTabWidget::Rounded</enum>
+ </property>
+ <widget class="QWidget" name="tab" >
+ <attribute name="title" >
+ <string>Transfers</string>
+ </attribute>
+ <layout class="QVBoxLayout" >
+ <property name="margin" >
+ <number>9</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item>
+ <widget class="QTreeView" name="transfersView" >
+ <property name="alternatingRowColors" >
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="tab_2" >
+ <attribute name="title" >
+ <string>Queue</string>
+ </attribute>
+ <layout class="QHBoxLayout" >
+ <property name="margin" >
+ <number>9</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item>
+ <widget class="QTreeView" name="queueView" >
+ <property name="alternatingRowColors" >
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="tab_3" >
+ <attribute name="title" >
+ <string>Finished</string>
+ </attribute>
+ <layout class="QVBoxLayout" >
+ <property name="margin" >
+ <number>9</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item>
+ <widget class="QTreeView" name="finishedView" >
+ <property name="alternatingRowColors" >
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </widget>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QStatusBar" name="statusbar" >
+ <property name="geometry" >
+ <rect>
+ <x>0</x>
+ <y>544</y>
+ <width>699</width>
+ <height>24</height>
+ </rect>
+ </property>
+ </widget>
+ <widget class="QToolBar" name="toolBar" >
+ <property name="layoutDirection" >
+ <enum>Qt::LeftToRight</enum>
+ </property>
+ <property name="autoFillBackground" >
+ <bool>false</bool>
+ </property>
+ <property name="orientation" >
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="iconSize" >
+ <size>
+ <width>32</width>
+ <height>32</height>
+ </size>
+ </property>
+ <property name="toolButtonStyle" >
+ <enum>Qt::ToolButtonTextUnderIcon</enum>
+ </property>
+ <attribute name="toolBarArea" >
+ <number>4</number>
+ </attribute>
+ <addaction name="actionConnect" />
+ <addaction name="actionSearch" />
+ <addaction name="actionSettings" />
+ <addaction name="separator" />
+ <addaction name="actionQuit" />
+ </widget>
+ <action name="actionQuit" >
+ <property name="icon" >
+ <iconset resource="res.qrc" >:/images/exit.png</iconset>
+ </property>
+ <property name="text" >
+ <string>Quit</string>
+ </property>
+ </action>
+ <action name="actionSettings" >
+ <property name="icon" >
+ <iconset resource="res.qrc" >:/images/settings.png</iconset>
+ </property>
+ <property name="text" >
+ <string>Settings</string>
+ </property>
+ </action>
+ <action name="actionConnect" >
+ <property name="icon" >
+ <iconset resource="res.qrc" >:/images/publichubs.png</iconset>
+ </property>
+ <property name="text" >
+ <string>Connect</string>
+ </property>
+ </action>
+ <action name="actionSearch" >
+ <property name="icon" >
+ <iconset resource="res.qrc" >:/images/search.png</iconset>
+ </property>
+ <property name="text" >
+ <string>Search</string>
+ </property>
+ </action>
+ <action name="actionForceAttempt" >
+ <property name="text" >
+ <string>Force Attempt</string>
+ </property>
+ </action>
+ <action name="actionCancelDownload" >
+ <property name="text" >
+ <string>Cancel download</string>
+ </property>
+ </action>
+ </widget>
+ <pixmapfunction></pixmapfunction>
+ <resources>
+ <include location="res.qrc" />
+ </resources>
+ <connections>
+ <connection>
+ <sender>actionQuit</sender>
+ <signal>triggered()</signal>
+ <receiver>MainWindow</receiver>
+ <slot>close()</slot>
+ <hints>
+ <hint type="sourcelabel" >
+ <x>-1</x>
+ <y>-1</y>
+ </hint>
+ <hint type="destinationlabel" >
+ <x>295</x>
+ <y>250</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>
diff --git a/ui/nicetreeview.cpp b/ui/nicetreeview.cpp
new file mode 100644
index 0000000..d540cb9
--- /dev/null
+++ b/ui/nicetreeview.cpp
@@ -0,0 +1,61 @@
+//
+// C++ Implementation: nicetreeview
+//
+// Description:
+//
+//
+// Author: Rikard Bjorklind <olof@users.sourceforge.net>, (C) 2006
+//
+// Copyright: See COPYING file that comes with this distribution
+//
+//
+#include "nicetreeview.h"
+#include <QPainter>
+#include <QtGui>
+#include "log.h"
+
+
+NiceTreeView::NiceTreeView(QWidget *parent)
+ : QTreeView(parent)
+{
+}
+
+NiceTreeView::~NiceTreeView()
+{
+}
+/*
+void NiceTreeView::paintEvent( QPaintEvent * event )
+{
+ int rowCount = model()->rowCount( );
+ if(rowCount) {
+ QTreeView::paintEvent(event);
+ }
+ else {
+ QPainter painter(this);
+ painter.setPen(Qt::blue);
+ painter.setFont(QFont("Arial", 30));
+ painter.drawText(rect(), Qt::AlignCenter, "Click or drag items here to add something");
+ }
+}
+
+void NiceTreeView::dragEnterEvent( QDragEnterEvent * event )
+{
+ QStringList sl = event->mimeData()->formats();
+ for(int i =0;i<sl.size();i++) {
+ QString s = sl[i];
+
+ logger->info(s);
+ }
+
+}
+*/
+
+
+void NiceTreeView::mousePressEvent( QMouseEvent * event )
+{
+ if(event->button()==Qt::LeftButton && !indexAt(event->pos()).isValid()) {
+ emit leftMousePressed();
+ }
+ else QTreeView::mousePressEvent(event);
+}
+
diff --git a/ui/nicetreeview.h b/ui/nicetreeview.h
new file mode 100644
index 0000000..5919822
--- /dev/null
+++ b/ui/nicetreeview.h
@@ -0,0 +1,38 @@
+//
+// C++ Interface: nicetreeview
+//
+// Description:
+//
+//
+// Author: Rikard Bjorklind <olof@users.sourceforge.net>, (C) 2006
+//
+// Copyright: See COPYING file that comes with this distribution
+//
+//
+#ifndef NICETREEVIEW_H
+#define NICETREEVIEW_H
+
+#include <QTreeView>
+
+/**
+A TreeView with some slight improvements
+
+ @author Rikard Bjorklind <olof@users.sourceforge.net>
+*/
+class NiceTreeView : public QTreeView
+{
+Q_OBJECT
+public:
+ NiceTreeView(QWidget *parent = 0);
+ virtual ~NiceTreeView();
+ signals:
+ void leftMousePressed();
+
+protected:
+ // Overridden from QWidget
+// virtual void paintEvent ( QPaintEvent * event );
+// virtual void dragEnterEvent(QDragEnterEvent *event);
+ virtual void mousePressEvent ( QMouseEvent * event );
+};
+
+#endif
diff --git a/ui/publichublist.ui b/ui/publichublist.ui
new file mode 100644
index 0000000..cfa517c
--- /dev/null
+++ b/ui/publichublist.ui
@@ -0,0 +1,67 @@
+<ui version="4.0" >
+ <author></author>
+ <comment></comment>
+ <exportmacro></exportmacro>
+ <class>PublicHubForm</class>
+ <widget class="QWidget" name="PublicHubForm" >
+ <property name="geometry" >
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>749</width>
+ <height>534</height>
+ </rect>
+ </property>
+ <property name="windowTitle" >
+ <string>Form</string>
+ </property>
+ <layout class="QVBoxLayout" >
+ <property name="margin" >
+ <number>9</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item>
+ <widget class="QTreeView" name="hubList" />
+ </item>
+ <item>
+ <layout class="QHBoxLayout" >
+ <property name="margin" >
+ <number>0</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item>
+ <widget class="QLabel" name="label" >
+ <property name="text" >
+ <string>Filter:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLineEdit" name="filerEdit" />
+ </item>
+ <item>
+ <widget class="QPushButton" name="connectButton" >
+ <property name="text" >
+ <string>Connect</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="refreshButton" >
+ <property name="text" >
+ <string>Refresh</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <pixmapfunction></pixmapfunction>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/ui/publichubswidget.cpp b/ui/publichubswidget.cpp
new file mode 100644
index 0000000..c30f9b0
--- /dev/null
+++ b/ui/publichubswidget.cpp
@@ -0,0 +1,142 @@
+/*
+ * publichubswidget.cpp
+ * ui
+ *
+ * Created by Mikael Gransell on 3/21/06.
+ * Copyright 2006 __MyCompanyName__. All rights reserved.
+ *
+ */
+
+#include <QHeaderView>
+
+#include "publichubswidget.h"
+#include "util.h"
+
+int PublicHubs::PublicHubsModel::Entry::sortCol = 0;
+Qt::SortOrder PublicHubs::PublicHubsModel::Entry::sortOrder = Qt::AscendingOrder;
+
+PublicHubs::PublicHubs( const BackendConnectionPtr& backendConn,
+ QWidget* parent )
+: QWidget( parent ),
+ backendConnection(backendConn),
+ model( new PublicHubsModel( this ) )
+{
+ ui.setupUi( this );
+ ui.hubList->setModel(model.get());
+ ui.hubList->header()->setClickable(true);
+ ui.hubList->header()->setSortIndicatorShown(true);
+}
+
+
+void PublicHubs::hubList( const QList<rpc_types::HubEntry>& hubs )
+{
+ model->setHubs( hubs );
+}
+
+PublicHubs::PublicHubsModel::PublicHubsModel( QObject* parent )
+: QAbstractTableModel( parent )
+{
+}
+
+void PublicHubs::on_connectButton_pressed()
+{
+ QModelIndex index = ui.hubList->selectionModel()->currentIndex();
+ backendConnection->createSession(model->getUrl( index.row() ));
+
+ emit close();
+}
+
+void PublicHubs::on_hubList_doubleClicked()
+{
+ on_connectButton_pressed();
+}
+
+void PublicHubs::on_refreshButton_pressed()
+{
+ backendConnection->getHubList(true);
+}
+
+QVariant PublicHubs::PublicHubsModel::data( const QModelIndex& index,
+ int role ) const
+{
+ if(role==Qt::DisplayRole) {
+ switch(index.column()) {
+ case 0: return hubEntries[shownEntries[index.row()]].getData().name.c_str();
+ case 1: return hubEntries[shownEntries[index.row()]].getData().server.c_str();
+ case 2: return hubEntries[shownEntries[index.row()]].getData().description.c_str();
+ case 3: return hubEntries[shownEntries[index.row()]].getData().country.c_str();
+ case 4: return Util::bytesToStr(hubEntries[shownEntries[index.row()]].getData().minShare);
+ case 5: return hubEntries[shownEntries[index.row()]].getData().users;
+ }
+ }
+ return QVariant();
+}
+
+QVariant PublicHubs::PublicHubsModel::headerData( int section,
+ Qt::Orientation orientation,
+ int role ) const
+{
+ if( orientation==Qt::Horizontal && role==Qt::DisplayRole )
+ {
+ switch(section) {
+ case 0: return tr("Name");
+ case 1: return tr("Server");
+ case 2: return tr("Description");
+ case 3: return tr("Country");
+ case 4: return tr("MinShare");
+ case 5: return tr("Users");
+ }
+ }
+ return QVariant();
+}
+
+
+const QString PublicHubs::PublicHubsModel::getUrl( int row ) const
+{
+ return hubEntries[shownEntries[row]].getData().server.c_str();
+}
+
+void PublicHubs::PublicHubsModel::setHubs( const QList<rpc_types::HubEntry> & hubs)
+{
+ if(!hubs.empty()) {
+ hubEntries.clear();
+ shownEntries.clear();
+ QList<rpc_types::HubEntry>::const_iterator it = hubs.begin();
+ int index = 0;
+ while(it != hubs.end() ) {
+ hubEntries.push_back(*it);
+ ++it;
+ shownEntries+=index;
+ index++;
+ }
+ emit layoutChanged();
+ }
+}
+
+void PublicHubs::PublicHubsModel::sort( int col, Qt::SortOrder order )
+{
+ PublicHubs::PublicHubsModel::Entry::sortCol = col;
+ PublicHubs::PublicHubsModel::Entry::sortOrder = order;
+ qSort(hubEntries);
+ applyFilter( filterPattern );
+ emit layoutChanged();
+}
+
+void PublicHubs::PublicHubsModel::applyFilter( const QString & pattern )
+{
+ filterPattern = pattern;
+ shownEntries.clear();
+
+ for(int i=0;i < hubEntries.size();i++)
+ {
+ if( QString::fromStdString(hubEntries[i].getData().name).contains(pattern,Qt::CaseInsensitive )) shownEntries+=i;
+ }
+
+ emit layoutChanged();
+}
+
+void PublicHubs::on_filerEdit_textChanged( )
+{
+ model->applyFilter( ui.filerEdit->text() );
+}
+
diff --git a/ui/publichubswidget.h b/ui/publichubswidget.h
new file mode 100644
index 0000000..af44004
--- /dev/null
+++ b/ui/publichubswidget.h
@@ -0,0 +1,134 @@
+/*
+ * publichubswidget.h
+ * ui
+ *
+ * Created by Mikael Gransell on 3/21/06.
+ *
+ */
+
+#include <boost/shared_ptr.hpp>
+
+#include <QWidget>
+#include <QAbstractTableModel>
+
+#include "rpctypes.h"
+#include "ui_publichublist.h"
+#include "backendconnection.h"
+
+class PublicHubs : public QWidget
+{
+ Q_OBJECT
+public:
+ PublicHubs( const BackendConnectionPtr& backendConn,
+ QWidget* parent = NULL );
+ ~PublicHubs() { }
+
+public slots:
+ //! Called when a new list of public hubs are received from the backend
+ void hubList( const QList<rpc_types::HubEntry>& hubs );
+ void on_filerEdit_textChanged();
+ void on_connectButton_pressed();
+ void on_refreshButton_pressed();
+ void on_hubList_doubleClicked();
+
+signals:
+ void close();
+
+private:
+ class PublicHubsModel;
+
+ Ui::PublicHubForm ui;
+ /// To send rpc commands to the backend
+ BackendConnectionPtr backendConnection;
+ /// Keep track of the data that is in the list
+ boost::shared_ptr<PublicHubsModel> model;
+
+ /**
+ * Class that holds the data displayed in the public hub list.
+ */
+ class PublicHubsModel : public QAbstractTableModel
+ {
+ public:
+ PublicHubsModel(QObject* parent);
+ ~PublicHubsModel()
+ {}
+ int rowCount ( const QModelIndex& ) const
+ {
+ //return hubEntries.size();
+ return shownEntries.size();
+ }
+ int columnCount ( const QModelIndex& ) const
+ {
+ return 6;
+ }
+ QVariant data ( const QModelIndex & index, int role = Qt::DisplayRole ) const;
+ QVariant headerData ( int section, Qt::Orientation orientation, int role = Qt::DisplayRole ) const;
+ const QString getUrl(int row) const;
+ void sort(int col,Qt::SortOrder order = Qt::AscendingOrder);
+
+ class Entry
+ {
+ public:
+ Entry( const Entry& rhs )
+ : data( rhs.getData() )
+ {}
+
+ Entry( const rpc_types::HubEntry& rawData )
+ : data( rawData )
+ {}
+
+ bool operator<(const PublicHubsModel::Entry& e) const
+ {
+ rpc_types::HubEntry data1 = data;
+ rpc_types::HubEntry data2 = e.getData();
+
+ if(sortOrder!=Qt::AscendingOrder) {
+ std::swap(data1, data2);
+ }
+
+ switch(sortCol)
+ {
+ case 1:
+ return data2.server < data2.server;
+ case 2:
+ return data1.description < data2.description;
+ case 3:
+ return data1.country < data2.country;
+ case 4:
+ return data1.minShare < data2.minShare;
+ case 5:
+ return data1.users < data2.users;
+ case 0:
+ default:
+ return data1.name < data2.name;
+ }
+ }
+
+ /// Return the data that this list entry represents as raw backend data
+ const rpc_types::HubEntry& getData() const { return data; }
+ /// The collumn that the list is sorted on
+ static int sortCol;
+ /// Ascending or descending sort order
+ static Qt::SortOrder sortOrder;
+
+ private:
+ /// The actual data stored as is from the backend
+ rpc_types::HubEntry data;
+ };
+
+ //const PublicHubsModel::Entry& entry(int row) const {
+ // if(row < 0 || row>=hubEntries.size()) throw "index out of bounds";
+ // return hubEntries.at(row);
+ //}
+
+ void setHubs( const QList<rpc_types::HubEntry>& );
+ void applyFilter(const QString& pattern);
+
+ private:
+ QList<Entry> hubEntries;
+ QList<int> shownEntries;
+ QString filterPattern;
+ };
+};
+
+
diff --git a/ui/queueitem.h b/ui/queueitem.h
new file mode 100644
index 0000000..b65edb3
--- /dev/null
+++ b/ui/queueitem.h
@@ -0,0 +1,30 @@
+//
+// C++ Interface: QueueItem
+//
+// Description:
+//
+//
+// Author: Rikard Björklind <olof@linux.nu>, (C) 2005
+//
+// Copyright: See COPYING file that comes with this distribution
+//
+//
+#ifndef QUEUE_ITEM_H__
+#define QUEUE_ITEM_H__
+
+//! Represents an item in the transfer queue.
+class QueueItem
+{
+public:
+ int id; //!< Internal ID
+ QString target; //!< Target file name
+ qint64 size; //!< File size
+ qint64 downloadedBytes; //!< Number of bytes downloaded so far
+ int status; //!< Transfer state
+ int priority; //!< DC++ Priority
+ int currentSource; //!< User ID of who we are downloading from
+ QList<int> sources; //!< List of other User IDs
+};
+
+
+#endif
diff --git a/ui/queuemodel.cpp b/ui/queuemodel.cpp
new file mode 100644
index 0000000..cc03d63
--- /dev/null
+++ b/ui/queuemodel.cpp
@@ -0,0 +1,125 @@
+//
+// C++ Implementation: QueueModel
+//
+// Description:
+//
+//
+// Author: Rikard Björklind <olof@linux.nu>, (C) 2005
+//
+// Copyright: See COPYING file that comes with this distribution
+//
+//
+#include "queuemodel.h"
+#include <QtCore>
+#include "log.h"
+#include "user.h"
+#include "util.h"
+
+QVariant QueueModel::data ( const QModelIndex & index, int role ) const
+{
+ User *u;
+ if(!index.isValid()) return QVariant();
+ if(index.row() < 0 || index.row() >= itemList.size()) return QVariant();
+ if(index.column() < 0 || index.column()>6) return QVariant();
+ if(role == Qt::DisplayRole) {
+ int i = index.row();
+ switch(index.column()) {
+
+ case 0:
+ return itemList[i]->status ? tr("RUN") : tr("WAIT");
+ case 1:
+ return itemList[i]->target;
+ case 2:
+ return Util::bytesToStr(itemList[i]->size);
+ case 3:
+ return itemList[i]->downloadedBytes;
+ case 4:
+ if(itemList[i]->currentSource==-1) return QVariant();
+ //u = sessionCfg->findUser(itemList[i]->currentSource);
+ u = userModel->getUser( itemList[i]->currentSource);
+ if(u) return u->nick;
+ return itemList[i]->currentSource;
+ case 5:
+ return itemList[i]->priority;
+ }
+ }
+ return QVariant();
+}
+
+QVariant QueueModel::headerData ( int section, Qt::Orientation orientation, int role ) const
+{
+ if (role != Qt::DisplayRole)
+ return QVariant();
+
+ if(orientation==Qt::Horizontal) {
+
+ if(section==0) return tr("Status");
+ if(section==1) return tr("Target");
+ if(section==2) return tr("Size");
+ if(section==3) return tr("Downloaded");
+ if(section==4) return tr("Current Source");
+ if(section==5) return tr("Priority");
+
+ }
+ return QVariant();
+}
+
+void QueueModel::onItemAdded( const QueueItem& item)
+{
+ logger->debug("onItemAdded: %d",item.id);
+ items[item.id] = item;
+ itemList.append( &items[item.id] );
+ emit layoutChanged();
+}
+
+void QueueModel::onItemsAdded( const QList< QueueItem > &ql )
+{
+ for(int i = 0;i < ql.size(); i++ )
+ {
+ items[ql[i].id] = ql[i];
+ itemList.append( &items[ql[i].id] );
+ }
+ emit layoutChanged();
+}
+
+void QueueModel::onItemRemoved( int id )
+{
+ logger->debug("onItemRemoved: %d",id);
+ itemList.removeAll(&items[id]);
+ items.remove(id);
+ emit layoutChanged();
+}
+
+void QueueModel::onFinished( int id)
+{
+ logger->debug("queueFinished: %d",id);
+ // hmm skip this for now...
+}
+
+void QueueModel::onSourcesUpdated( int id, QList<int> sources)
+{
+ logger->debug("sourcesUpdated: %d",id );
+ items[id].sources = sources;
+ if(sources.size()==0) {
+ items[id].currentSource=-1;
+ items[id].status=0;
+ }
+ emit layoutChanged();
+}
+
+void QueueModel::onStatusUpdated(int id,int status,int currentSource,int priority)
+{
+ logger->debug("statusUpdated: %id",id);
+ items[id].status = status;
+ items[id].currentSource = currentSource;
+ items[id].priority = priority;
+ int index = itemList.indexOf(&items[id],0);
+ emit dataChanged(createIndex(index,4),createIndex(index,4));
+}
+
+QueueItem * QueueModel::getItem( int row ) const throw( IndexOutOfBoundsException )
+{
+ if(row < 0 || row>= itemList.size()) throw IndexOutOfBoundsException();
+ return itemList[row];
+}
+
diff --git a/ui/queuemodel.h b/ui/queuemodel.h
new file mode 100644
index 0000000..eaec780
--- /dev/null
+++ b/ui/queuemodel.h
@@ -0,0 +1,53 @@
+#ifndef QUEUE_MODEL_H__
+#define QUEUE_MODEL_H__
+
+
+#include "global.h"
+#include "filetransfer.h"
+#include "exceptions.h"
+
+#include <QAbstractTableModel>
+#include <QList>
+#include "queueitem.h"
+#include "globalusermodel.h"
+#include <boost/shared_ptr.hpp>
+
+//! Transfer queue data model.
+class QueueModel : public QAbstractTableModel
+{
+ Q_OBJECT
+ public:
+ QueueModel( boost::shared_ptr<GlobalUserModel> aUserModel ) throw() : userModel(aUserModel) {}
+ virtual ~QueueModel() throw() {}
+
+ int rowCount ( const QModelIndex& ) const {return itemList.size();}
+ int columnCount ( const QModelIndex& ) const {return 6;}
+ QVariant data ( const QModelIndex & index, int role = Qt::DisplayRole ) const;
+ QVariant headerData ( int section, Qt::Orientation orientation, int role = Qt::DisplayRole ) const;
+ QueueItem* getItem(int row) const throw(IndexOutOfBoundsException);
+
+ public slots:
+ void onItemAdded( const QueueItem& );
+ void onItemsAdded( const QList<QueueItem>& );
+ void onItemRemoved( int );
+ void onFinished( int );
+ void onSourcesUpdated( int, QList<int> );
+ void onStatusUpdated(int,int,int,int);
+
+ private:
+ QMap<int,QueueItem> items;
+ QList<QueueItem*> itemList;
+ boost::shared_ptr<GlobalUserModel> userModel;
+};
+
+
+
+
+
+
+
+
+
+
+#endif
+
diff --git a/ui/res.qrc b/ui/res.qrc
new file mode 100644
index 0000000..4126b9d
--- /dev/null
+++ b/ui/res.qrc
@@ -0,0 +1,24 @@
+<RCC>
+ <qresource prefix="/" >
+ <file>images/browse.png</file>
+ <file>images/exit.png</file>
+ <file>images/publichubs.png</file>
+ <file>images/remove.png</file>
+ <file>images/search.png</file>
+ <file>images/settings.png</file>
+ <file>images/user-dcpp-op.png</file>
+ <file>images/user-dcpp-passive-op.png</file>
+ <file>images/user-dcpp-passive.png</file>
+ <file>images/user-dcpp.png</file>
+ <file>images/user-normal-op.png</file>
+ <file>images/user-normal.png</file>
+ <file>images/user-passive-op.png</file>
+ <file>images/user-passive.png</file>
+ <file>images/download.png</file>
+<file>images/downloadto.png</file>
+<file>images/dldir.png</file>
+<file>images/dldirto.png</file>
+<file>images/tthsearch.png</file>
+<file>images/filelist.png</file>
+ </qresource>
+</RCC>
diff --git a/ui/rpctypes.h b/ui/rpctypes.h
new file mode 100644
index 0000000..de07d3e
--- /dev/null
+++ b/ui/rpctypes.h
@@ -0,0 +1,85 @@
+
+
+#ifndef RPC_TYPES_H_
+#define RPC_TYPES_H_
+
+#include <string>
+#include <list>
+#include <rpcdriver/types.h>
+
+using std::string;
+using std::list;
+
+namespace rpc_types {
+
+
+class FavouriteHub
+{
+public:
+ string nick;
+ string userDescription;
+ string name;
+ string server;
+ string description;
+ string password;
+ bool autoConnect;
+
+ bool operator==(const FavouriteHub& rhs) {
+ return rhs.server==server;
+ }
+};
+
+class Share
+{
+public:
+ string virtualName;
+ string realName;
+ int64 bytesShared;
+};
+
+class HubEntry
+{
+public:
+ string name;
+ string server;
+ string description;
+ string country;
+ string rating;
+ int reliability;
+ int64 shared;
+ int64 minShare;
+ int users;
+ int minSlots;
+ int maxHubs;
+ int maxUsers;
+
+ /// The amount of elements a hub entry should contain
+ static const unsigned short HUB_ENTRY_SIZE = 12;
+};
+
+class User
+{
+public:
+ string nick;
+ int flags;
+ string email;
+ string description;
+ string connection;
+ string tag;
+ int64 shared;
+ int hubslots;
+ int userid;
+};
+
+class SessionInfo
+{
+public:
+ int id;
+ string hubname;
+ string url;
+ list<User> users;
+};
+
+}
+
+#endif
diff --git a/ui/search.ui b/ui/search.ui
new file mode 100644
index 0000000..466cbad
--- /dev/null
+++ b/ui/search.ui
@@ -0,0 +1,294 @@
+<ui version="4.0" >
+ <author></author>
+ <comment></comment>
+ <exportmacro></exportmacro>
+ <class>searchForm</class>
+ <widget class="QWidget" name="searchForm" >
+ <property name="geometry" >
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>682</width>
+ <height>429</height>
+ </rect>
+ </property>
+ <property name="windowTitle" >
+ <string>Form</string>
+ </property>
+ <layout class="QHBoxLayout" >
+ <property name="margin" >
+ <number>9</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item>
+ <layout class="QVBoxLayout" >
+ <property name="margin" >
+ <number>0</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item>
+ <widget class="QGroupBox" name="searchGroupBox" >
+ <property name="sizePolicy" >
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="title" >
+ <string>Search</string>
+ </property>
+ <layout class="QVBoxLayout" >
+ <property name="margin" >
+ <number>9</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item>
+ <widget class="QComboBox" name="searchCombo" >
+ <property name="sizePolicy" >
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>1</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="editable" >
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" >
+ <property name="margin" >
+ <number>0</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item>
+ <widget class="QLabel" name="minSizeLabel" >
+ <property name="text" >
+ <string>Size</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QComboBox" name="sizeModeCombo" >
+ <item>
+ <property name="text" >
+ <string>at least</string>
+ </property>
+ </item>
+ <item>
+ <property name="text" >
+ <string>at most</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item>
+ <widget class="QSpinBox" name="sizeSpin" >
+ <property name="sizePolicy" >
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>1</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="maximum" >
+ <number>9999</number>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QComboBox" name="sizeTypeCombo" >
+ <item>
+ <property name="text" >
+ <string>B</string>
+ </property>
+ </item>
+ <item>
+ <property name="text" >
+ <string>KiB</string>
+ </property>
+ </item>
+ <item>
+ <property name="text" >
+ <string>MiB</string>
+ </property>
+ </item>
+ <item>
+ <property name="text" >
+ <string>GiB</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" >
+ <property name="margin" >
+ <number>0</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item>
+ <widget class="QLabel" name="typeLabel" >
+ <property name="text" >
+ <string>Type:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QComboBox" name="typeCombo" >
+ <property name="sizePolicy" >
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>1</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <item>
+ <property name="text" >
+ <string>Any type</string>
+ </property>
+ </item>
+ <item>
+ <property name="text" >
+ <string>Audio</string>
+ </property>
+ </item>
+ <item>
+ <property name="text" >
+ <string>Compressed</string>
+ </property>
+ </item>
+ <item>
+ <property name="text" >
+ <string>Documents</string>
+ </property>
+ </item>
+ <item>
+ <property name="text" >
+ <string>Executables</string>
+ </property>
+ </item>
+ <item>
+ <property name="text" >
+ <string>Pictures</string>
+ </property>
+ </item>
+ <item>
+ <property name="text" >
+ <string>Video</string>
+ </property>
+ </item>
+ <item>
+ <property name="text" >
+ <string>Directories</string>
+ </property>
+ </item>
+ <item>
+ <property name="text" >
+ <string>TTH</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" >
+ <property name="margin" >
+ <number>0</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item>
+ <spacer>
+ <property name="orientation" >
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" >
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QPushButton" name="searchButton" >
+ <property name="text" >
+ <string>Search</string>
+ </property>
+ <property name="default" >
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="Line" name="line" >
+ <property name="orientation" >
+ <enum>Qt::Horizontal</enum>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="hideZeroSlots" >
+ <property name="text" >
+ <string>Hide results with zero slots</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <spacer>
+ <property name="orientation" >
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" >
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QTreeView" name="searchView" >
+ <property name="sizePolicy" >
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>7</vsizetype>
+ <horstretch>1</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <pixmapfunction></pixmapfunction>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/ui/searchdock.ui b/ui/searchdock.ui
new file mode 100644
index 0000000..a49d541
--- /dev/null
+++ b/ui/searchdock.ui
@@ -0,0 +1,58 @@
+<ui version="4.0" >
+ <author></author>
+ <comment></comment>
+ <exportmacro></exportmacro>
+ <class>Form</class>
+ <widget class="QWidget" name="Form" >
+ <property name="geometry" >
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>400</width>
+ <height>300</height>
+ </rect>
+ </property>
+ <property name="windowTitle" >
+ <string>Form</string>
+ </property>
+ <layout class="QHBoxLayout" >
+ <property name="margin" >
+ <number>9</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item>
+ <widget class="QDockWidget" name="searchDock" >
+ <widget class="QWidget" name="dockWidgetContents" >
+ <layout class="QHBoxLayout" >
+ <property name="margin" >
+ <number>9</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item>
+ <widget class="QTabWidget" name="searchTab" >
+ <widget class="QWidget" name="tab" >
+ <attribute name="title" >
+ <string>Tab 1</string>
+ </attribute>
+ </widget>
+ <widget class="QWidget" name="tab_2" >
+ <attribute name="title" >
+ <string>Tab 2</string>
+ </attribute>
+ </widget>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <pixmapfunction></pixmapfunction>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/ui/searchentry.h b/ui/searchentry.h
new file mode 100644
index 0000000..5c8ae0f
--- /dev/null
+++ b/ui/searchentry.h
@@ -0,0 +1,238 @@
+
+#ifndef _SEARCHRESULT_H_
+#define _SEARCHRESULT_H_
+
+#include "global.h"
+#include "log.h"
+#include <QTextCodec>
+#include <string>
+#include <rpcdriver/types.h>
+
+class SearchEntry {
+public:
+ SearchEntry(const std::string &file_as_in_dcpp,
+ const QString& n,
+ const QString& hName,
+ int uId,
+ int64 fSize,
+ int uSlots,
+ int fSlots,
+ int t,
+ const QString& tthIn,
+ bool isFileStringUTF8 = false) :
+ file( file_as_in_dcpp ),
+ nick( n ),
+ hubName( hName ),
+ userId( uId ),
+ fileSize( fSize ),
+ userSlots( uSlots ),
+ freeSlots( fSlots ),
+ type( static_cast<eType>( t ) ),
+ tth( tthIn ),
+ isUTF8( isFileStringUTF8 ),
+ fileName(""),
+ filePath("")
+ {
+ QTextCodec *codec;
+ if (isUTF8)
+ codec = QTextCodec::codecForName("utf-8");
+ else
+ codec = QTextCodec::codecForName("Windows-1252");
+
+ // if can't find matching codec, fall back on default codec for C-strings
+ if (!codec)
+ codec = QTextCodec::codecForCStrings();
+
+ QString fstr = codec->toUnicode( file.c_str() );
+
+ // Check if this is a directory or a file.
+ if( type == DIRECTORY ) {
+
+ // Get the second last section, i.e. the part before the last '\'
+ fileName = fstr.section( '\\', -2 );
+ }
+ else {
+
+ // Get the last part of the path
+ fileName = fstr.section('\\', -1);
+ }
+
+ filePath = fstr.left( fstr.lastIndexOf( fileName ) );
+
+ logger->debug("SearchEntry() : file is \"%s\", fileName is \"%s\"", file.c_str(), fileName.toLatin1().constData());
+ }
+
+ virtual ~SearchEntry(){}
+
+ enum eType
+ {
+ FILE,
+ DIRECTORY
+ };
+
+ /*! Returns full path + filename string, in original encoding from dc++ */
+ const std::string &getFile() const { return file; }
+
+ /*! Returns unicode file name without path, only for use in the GUI */
+ const QString& getFileName() const { return fileName; }
+
+ /*! Returns unicode file path without file name, only for use in the GUI */
+ const QString& getPath() const { return filePath; }
+
+ /**
+ */
+ const QString& getNick() const { return nick; }
+ /**
+ */
+ const QString& getHubName() const { return hubName; }
+ /**
+ */
+ int getUserId() const { return userId; }
+ /**
+ */
+ int64 getFileSize() const { return fileSize; }
+ /**
+ */
+ int getUserSlots() const { return userSlots; }
+ /**
+ */
+ int getFreeSlots() const { return freeSlots; }
+
+ /**
+ */
+ eType getType() const { return type; }
+
+ /**
+ */
+ const QString& getTTH() const { return tth; }
+
+ /**
+ Returns true if the string returned by getFile() has utf-8 encoding
+ */
+ bool getUTF8() const { return isUTF8; }
+
+ /**
+ */
+ void setFileName( const QString& name ) { fileName = name; }
+ /**
+ */
+ void setFileName( const std::string& name ) { fileName = name.c_str(); }
+
+ /**
+ */
+ void setNick( const QString& n ) { nick = n; }
+ /**
+ */
+ void setNick( const std::string& n ) { nick = n.c_str(); }
+
+ /**
+ */
+ void setHubName( const QString& name ) { hubName = name; }
+ /**
+ */
+ void setHubName( const std::string& name ) { hubName = name.c_str(); }
+
+ /**
+ */
+ void setFileSize( int64 s ) { fileSize = s; }
+ /**
+ */
+ void setUserSlots( int u ) { userSlots = u; }
+ /**
+ */
+ void setFreeSlots( int f ) { freeSlots = f; }
+ /**
+ */
+ void setType( eType t ) { type = t; }
+
+// SearchEntry& operator=(const SearchEntry& rhs)
+// {
+// if( this == &rhs )
+// return *this;
+//
+//
+//
+// return *this;
+// }
+
+ static int sortElement; //!< Defines the element to sort on. UGLY SHITCODE.
+ enum {SORT_FILENAME,
+ SORT_USER,
+ SORT_SIZE,
+ SORT_PATH,
+ SORT_SLOTS,
+ SORT_HUBNAME,
+ SORT_TYPE,
+ SORT_TTH};
+
+bool operator < (const SearchEntry& s) const
+{
+ switch(sortElement)
+ {
+ case SORT_FILENAME:
+ if(type != s.type) return s.type < type;
+ return fileName < s.fileName;
+ case SORT_USER:
+ return nick < s.nick;
+ case SORT_SIZE:
+ return fileSize < s.fileSize;
+ case SORT_PATH:
+ return filePath < s.filePath;
+ case SORT_SLOTS:
+ return freeSlots < s.freeSlots;
+ case SORT_HUBNAME:
+ return hubName < s.hubName;
+ case SORT_TYPE:
+ return type < s.type;
+ case SORT_TTH:
+ return tth < s.tth;
+ }
+ return false;
+}
+private:
+
+ /**
+ Unmodified full path + filename string, as received from dc++
+ */
+ std::string file;
+
+ /**
+ */
+ QString nick;
+ /**
+ */
+ QString hubName;
+ /**
+ */
+ int userId;
+ /**
+ */
+ int64 fileSize;
+ /**
+ */
+ int userSlots;
+ /**
+ */
+ int freeSlots;
+
+ /**
+ */
+ eType type;
+
+ /**
+ */
+ QString tth;
+
+ /**
+ True if the file string is originally encoded with utf-8
+ */
+ bool isUTF8;
+
+ /// Name of the file
+ QString fileName;
+
+ /// Path to the file on the remote users share
+ QString filePath;
+};
+
+#endif
diff --git a/ui/searchmanager.cpp b/ui/searchmanager.cpp
new file mode 100644
index 0000000..a6e70d8
--- /dev/null
+++ b/ui/searchmanager.cpp
@@ -0,0 +1,143 @@
+#include "searchmanager.h"
+#include "../backend/CommonTypes.h"
+
+#include <QtGui>
+
+SearchManager::SearchManager(boost::shared_ptr<BackendConnection> aConnection,QObject *parent)
+ : QObject(parent),backendConnection(aConnection), dockWidgetAdded(false)
+{
+ QMainWindow *mw = static_cast<QMainWindow*>(parent);
+ displayMode = SHOW_AS_DOCKWINDOW;
+ searchTabs = new QTabWidget(0);
+ setupUi(mw);
+
+ QPushButton* b = new QPushButton(searchTabs);
+ QIcon i(":/images/remove.png");
+ b->setIcon(i);
+ b->setFlat(true);
+ b->setToolTip(tr("Close search"));
+ searchTabs->setCornerWidget(b);
+ connect(b,SIGNAL(pressed()),SLOT(onCloseCurrentSearch()));
+
+ // create an initial search widget
+ //activeSearch = new SearchWidget(0);
+ //searchTabs->addTab(activeSearch,tr("Search"));
+ //connect(activeSearch,SIGNAL(search( int , qint64 , int , int , const QString& )),
+ // this,SLOT(search( int , qint64 , int , int , const QString & )));
+ //connect(activeSearch,SIGNAL(downloadFile(const SearchEntry&)),backendConnection.get(),SLOT(downloadFile(const SearchEntry&)));
+ //connect(activeSearch,SIGNAL(downloadFile( int, const QString& , int64 , const QString& )),backendConnection.get(),SLOT(downloadFile( int, const QString& , int64 , const QString& )));
+ //connect(activeSearch,SIGNAL(downloadFileTo(const SearchEntry&, const QString&)),backendConnection.get(),SLOT(downloadFileTo(const SearchEntry&,const QString&)));
+ //connect(activeSearch,
+ //// SIGNAL(downloadFileTo( int, const QString&, int64, const QString&, const QString& )),
+ // backendConnection.get(),
+ // SLOT(downloadFileTo( int, const QString&, int64, const QString&, const QString& )) );
+ //connect(activeSearch,SIGNAL(getUserFileList(int)),backendConnection.get(),SLOT(getUserFileList(int)));
+ activeSearch = newSearchWidget( tr("Search") );
+}
+
+
+SearchManager::~SearchManager()
+{
+}
+
+void SearchManager::setupUi( QMainWindow* mw)
+{
+ searchTabs->hide();
+ searchTabs->setParent(0);
+ if(dialog) delete dialog;
+ if(dock) delete dock;
+
+ if(displayMode==SHOW_AS_DIALOG) {
+ dialog = new QDialog;
+ QVBoxLayout *layout = new QVBoxLayout(dialog);
+ layout->addWidget(searchTabs);
+ searchTabs->setParent(dialog);
+ }
+ else {
+ dock = new QDockWidget(tr("Searches"),mw);
+ searchTabs->setParent(dock);
+ dock->setWidget(searchTabs);
+ dock->setFeatures(QDockWidget::DockWidgetClosable|QDockWidget::DockWidgetMovable);
+ }
+ searchTabs->show();
+}
+
+void SearchManager::show(QMainWindow* mw )
+{
+ if(displayMode==SHOW_AS_DIALOG) dialog->show();
+ else {
+ if(dock->isVisible()) {
+ newSearchWidget( tr("Search") );
+ }
+ if(!dockWidgetAdded) {
+ mw->addDockWidget(Qt::RightDockWidgetArea,dock);
+ dockWidgetAdded = true;
+ }
+ else
+ dock->show();
+ }
+}
+
+void SearchManager::search( int session, qint64 size, int sizeMode, int typeMode, const QString & search, SearchWidget* destination )
+{
+ if( NULL == destination ) {
+ activeSearch = newSearchWidget(search);
+ }
+ else {
+ activeSearch = destination;
+ }
+ activeSearchType = typeMode;
+ activeSearchString = search;
+ searchTokens = activeSearchString.split(" ");
+ backendConnection->sendSearch( session, size, sizeMode, typeMode, search );
+}
+
+void SearchManager::searchResults( int session, QList<SearchEntry> &results)
+{
+ // Filter
+ QList< SearchEntry >::iterator it = results.begin();
+ while( it!=results.end() ) {
+ if( (activeSearchType==backend::TYPE_TTH && activeSearchString!=it->getTTH()) ||
+ (activeSearchType!=backend::TYPE_TTH && !isSearchForActiveWidget(QString::fromStdString(it->getFile()))) ) {
+ logger->debug("Erasing search because of filter");
+ it = results.erase(it);
+ }
+ else it++;
+ }
+
+ // Put them in the widget
+ activeSearch->searchResults( session, results );
+}
+
+SearchWidget * SearchManager::newSearchWidget(const QString& name)
+{
+ SearchWidget *sw = new SearchWidget(0);
+ searchTabs->addTab(sw,name);
+ connect(sw,SIGNAL(search( int , qint64 , int , int , const QString&,SearchWidget* )),
+ this,SLOT(search( int , qint64 , int , int , const QString &,SearchWidget* )));
+ connect(sw,SIGNAL(downloadFile(const SearchEntry&)),backendConnection.get(),SLOT(downloadFile(const SearchEntry&)));
+ connect(sw,SIGNAL(downloadFile( int, const QString& , int64 , const QString& )),backendConnection.get(),SLOT(downloadFile( int, const QString& , int64 , const QString& )));
+ connect(sw,SIGNAL(downloadFileTo(const SearchEntry&, const QString&)),backendConnection.get(),SLOT(downloadFileTo(const SearchEntry&,const QString&)));
+ connect(sw, SIGNAL(downloadFileTo( int, const QString&, int64, const QString&, const QString& )),
+ backendConnection.get(), SLOT(downloadFileTo( int, const QString&, int64, const QString&, const QString& )) );
+ connect(sw,SIGNAL(getUserFileList(int)),backendConnection.get(),SLOT(getUserFileList(int)));
+ return sw;
+}
+
+void SearchManager::onCloseCurrentSearch( )
+{
+ if(searchTabs->count() <= 1) return;
+ SearchWidget* curr = qobject_cast<SearchWidget*>( searchTabs->currentWidget() );
+ int curri = searchTabs->currentIndex();
+ searchTabs->removeTab(curri);
+ if( curr == activeSearch ) activeSearch = 0;
+ delete curr;
+}
+
+bool SearchManager::isSearchForActiveWidget( const QString& searchStr ) const
+{
+ QStringList::const_iterator it = searchTokens.begin();
+ while( it != searchTokens.end() && searchStr.contains( *it, Qt::CaseInsensitive ) ) { ++it; }
+
+ return it == searchTokens.end();
+}
diff --git a/ui/searchmanager.h b/ui/searchmanager.h
new file mode 100644
index 0000000..c10d7c7
--- /dev/null
+++ b/ui/searchmanager.h
@@ -0,0 +1,79 @@
+//
+// C++ Interface: searchmanager
+//
+// Description:
+//
+//
+// Author: Rikard Bjorklind <olof@users.sourceforge.net>, (C) 2006
+//
+// Copyright: See COPYING file that comes with this distribution
+//
+//
+#ifndef SEARCHMANAGER_H
+#define SEARCHMANAGER_H
+
+#include <QObject>
+#include <QDialog>
+#include <QDockWidget>
+#include <QPointer>
+#include "searchwidget.h"
+#include "backendconnection.h"
+
+class QTabWidget;
+
+/*!
+ \brief Manages the search dialog and subwidgets.
+
+ This class acts as a facade to the search interface. It receives search results and
+ updates the current search widget, which it owns. It also performs searches using the
+ BackendConnection that is assigned during construction. Furthermore, it implements the
+ search dialog/dockwidget switching.
+*/
+class SearchManager : public QObject
+{
+ Q_OBJECT
+public:
+ //! constructor
+ SearchManager(boost::shared_ptr<BackendConnection> aConnection,QObject *parent = 0);
+
+ //! destructor
+ ~SearchManager();
+
+ //! Display modes
+ enum DisplayMode {
+ SHOW_AS_DIALOG,
+ SHOW_AS_DOCKWINDOW
+ };
+
+public slots:
+ //! Called to show the search UI
+ void show(QMainWindow*);
+
+ //! Call to execute a search
+ void search(int session, qint64 size, int sizeMode, int typeMode, const QString& search,SearchWidget* originator);
+
+ //! Called when new search results arrive
+ void searchResults(int,QList<SearchEntry>&);
+
+private:
+ boost::shared_ptr<BackendConnection> backendConnection;
+ DisplayMode displayMode;
+ QTabWidget* searchTabs;
+ QPointer<QDialog> dialog;
+ QPointer<QDockWidget> dock;
+
+ SearchWidget* activeSearch; //!< Search results will be sent to this widget
+ int activeSearchType; //!< Set to the search type of the last search, used for filtering
+ QString activeSearchString; //!< Set to the string of the last made search, used for filtering
+ QStringList searchTokens;
+
+ SearchWidget* newSearchWidget(const QString& name);
+ bool isSearchForActiveWidget( const QString& searchStr ) const;
+ void setupUi(QMainWindow*);
+ bool dockWidgetAdded;
+
+private slots:
+ void onCloseCurrentSearch();
+};
+
+#endif
diff --git a/ui/searchtablemodel.cpp b/ui/searchtablemodel.cpp
new file mode 100644
index 0000000..9ab7a2b
--- /dev/null
+++ b/ui/searchtablemodel.cpp
@@ -0,0 +1,211 @@
+//
+// C++ Implementation: searchlistmodel
+//
+// Description:
+//
+//
+// Author: Mikael Gransell <mikael.gransell@bredband.net>, (C) 2005
+//
+// Copyright: See COPYING file that comes with this distribution
+//
+//
+
+#include "searchtablemodel.h"
+#include "log.h"
+#include "util.h"
+
+#include <QtCore>
+
+#include <stdexcept>
+
+
+int SearchEntry::sortElement = SearchEntry::SORT_FILENAME;
+
+/*
+#include <iostream>
+
+SearchTableModel::~SearchTableModel()
+{
+ std::cerr << "~SearchTableModel()\n";
+}
+*/
+
+void SearchTableModel::onSearchResult( int /* session */, const SearchEntry& entry ) {
+ int n = filteredResults.size();
+ sortedInsert( entry );
+ // Tell the views that data we have added a row
+ if(n!=filteredResults.size()) emit layoutChanged();
+}
+
+void SearchTableModel::onSearchResult( int /* session */, const QList<SearchEntry>& entries )
+{
+ int n = filteredResults.size();
+ for(int i=0; i < entries.size(); i++ ) //searchResults.append( entries.at(i) );
+ sortedInsert( entries.at(i) );
+ if(n!=filteredResults.size()) emit layoutChanged();
+}
+
+QVariant SearchTableModel::data( const QModelIndex& index, int role ) const {
+ if( !index.isValid() )
+ return QVariant();
+
+ if( index.row() < 0 || index.row() >= filteredResults.size() )
+ return QVariant();
+
+ QVariant ret;
+ int i = index.row();
+
+ if( role == Qt::DisplayRole || role == Qt::ToolTipRole ) {
+ switch( index.column() ) {
+ case FILENAME_COLUMN:
+ ret = filteredResults.at(i).getFileName();
+ break;
+
+ case USER_COLUMN:
+ ret = filteredResults.at(i).getNick();
+ break;
+
+ case SIZE_COLUMN:
+ ret = Util::bytesToStr(filteredResults.at(i).getFileSize());
+ break;
+
+ case PATH_COLUMN:
+ ret = filteredResults.at(i).getPath();
+ break;
+
+ case HUBNAME_COLUMN:
+ ret = filteredResults.at(i).getHubName();
+ break;
+
+ //case TYPE_COLUMN:
+ // ret = filteredResults.at(i).getType();
+ // break;
+
+ case SLOTS_COLUMN:
+ ret = QString::number( filteredResults.at(i).getFreeSlots() ) + "/" +
+ QString::number( filteredResults.at(i).getUserSlots() );
+ break;
+
+ case TTH_COLUMN:
+ ret = filteredResults.at(i).getTTH();
+ break;
+ }
+ }
+ else if( role == Qt::DecorationRole ) {
+
+ // only display icon for the first column
+ if ( index.column() == 0 ) {
+
+ // Get an icon from the icon provider
+ ret = m_pIconProvider->icon( filteredResults.at(i).getType() == SearchEntry::DIRECTORY ? QFileIconProvider::Folder : QFileIconProvider::File );
+ }
+ }
+
+ return ret;
+}
+
+
+QVariant SearchTableModel::headerData ( int section, Qt::Orientation orientation, int role ) const
+{
+ if (role != Qt::DisplayRole)
+ return QVariant();
+
+ if(orientation==Qt::Horizontal) {
+ switch( section ) {
+ case FILENAME_COLUMN:
+ return tr("File Name");
+
+ case USER_COLUMN:
+ return tr("User");
+
+ case SIZE_COLUMN:
+ return tr("Size");
+
+ case PATH_COLUMN:
+ return tr("Path");
+
+ case SLOTS_COLUMN:
+ return tr("Slots");
+
+ case HUBNAME_COLUMN:
+ return tr("Hub");
+
+ //case TYPE_COLUMN:
+ // return tr("Type");
+
+ case TTH_COLUMN:
+ return tr("TTH");
+ }
+ }
+ return QVariant();
+}
+
+const SearchEntry& SearchTableModel::getEntry( int index ) const
+{
+ // Make sure the index is OK
+ if( index < 0 || index >= filteredResults.size() ) {
+ logger->error("SearchTableModel::getEntry() index out of valid range");
+ throw std::out_of_range("Index out of range when fetching SearchEntry from model");
+ }
+
+ return filteredResults.at(index);
+}
+
+void SearchTableModel::sort( int column, Qt::SortOrder order )
+{
+ sortOrder = order;
+ switch( column ) {
+ case FILENAME_COLUMN: SearchEntry::sortElement = SearchEntry::SORT_FILENAME;break;
+ case USER_COLUMN: SearchEntry::sortElement = SearchEntry::SORT_USER ;break;
+ case SIZE_COLUMN:SearchEntry::sortElement = SearchEntry::SORT_SIZE;break;
+ case PATH_COLUMN:SearchEntry::sortElement = SearchEntry::SORT_PATH;break;
+ case SLOTS_COLUMN:SearchEntry::sortElement = SearchEntry::SORT_SLOTS;break;
+ case HUBNAME_COLUMN:SearchEntry::sortElement = SearchEntry::SORT_HUBNAME;break;
+ //case TYPE_COLUMN:SearchEntry::sortElement = SearchEntry::SORT_TYPE;break;
+ case TTH_COLUMN:SearchEntry::sortElement = SearchEntry::SORT_TTH;break;
+ }
+ if(order==Qt::DescendingOrder)
+ qSort(filteredResults.begin(),filteredResults.end(),qGreater<SearchEntry>());
+ else
+ qSort(filteredResults.begin(),filteredResults.end());
+
+
+ emit layoutChanged();
+}
+
+void SearchTableModel::clear()
+{
+ searchResults.clear();
+ filteredResults.clear();
+ emit layoutChanged();
+}
+
+void SearchTableModel::sortedInsert(const SearchEntry &e )
+{
+ // This one doesnt need sorting since we only use it for building the filtered set.
+ searchResults.append(e);
+
+ if(!filterEnabled || e.getFreeSlots() > 0) {
+ QList<SearchEntry>::iterator i = (sortOrder==Qt::AscendingOrder) ?
+ qUpperBound(filteredResults.begin(), filteredResults.end(), e) :
+ qLowerBound(filteredResults.begin(), filteredResults.end(), e);
+ filteredResults.insert(i, e);
+ }
+}
+
+void SearchTableModel::enableFilter( bool yes )
+{
+ if( yes == filterEnabled ) return;
+ filterEnabled = yes;
+ filteredResults.clear();
+ if(!filterEnabled) filteredResults = searchResults;
+ else
+ for(int i=0; i < searchResults.size(); i++)
+ if( searchResults.at(i).getFreeSlots() > 0 )
+ filteredResults += searchResults.at(i);
+ if(sortOrder==Qt::DescendingOrder)
+ qSort(filteredResults.begin(),filteredResults.end(),qGreater<SearchEntry>());
+ else
+ qSort(filteredResults.begin(),filteredResults.end());
+ emit layoutChanged();
+}
diff --git a/ui/searchtablemodel.h b/ui/searchtablemodel.h
new file mode 100644
index 0000000..587c56a
--- /dev/null
+++ b/ui/searchtablemodel.h
@@ -0,0 +1,139 @@
+//
+// C++ Interface: searchlistmodel
+//
+// Description:
+//
+//
+// Author: Mikael Gransell <mikael.gransell@bredband.net>, (C) 2005
+//
+// Copyright: See COPYING file that comes with this distribution
+//
+//
+
+#ifndef SEARCH_TABLE_MODEL_H_
+#define SEARCH_TABLE_MODEL_H_
+
+#include <QAbstractTableModel>
+#include <QList>
+#include <QPointer>
+#include <QFileIconProvider>
+
+#include "searchentry.h"
+
+/**
+ * This class acts as the model for the search table. Data is added to
+ * this model and the view that displays the data is updated according to
+ * the Model/View way of programming.
+ */
+class SearchTableModel : public QAbstractTableModel
+{
+ Q_OBJECT
+public:
+ /**
+ * Constructor.
+ */
+ SearchTableModel()
+ {
+ // Create an icon provider that will suply the list wiht icons
+ m_pIconProvider = new QFileIconProvider();
+ filterEnabled = false;
+ sortOrder = Qt::AscendingOrder;
+ }
+ /**
+ * Destructor.
+ */
+ virtual ~SearchTableModel()
+ {
+ delete m_pIconProvider;
+ }
+
+ /**
+ * Enumerates the different columns in the list model.
+ */
+ enum eSearchColumnHeader {
+ FILENAME_COLUMN = 0,
+ USER_COLUMN,
+ SIZE_COLUMN,
+ PATH_COLUMN,
+ SLOTS_COLUMN,
+ HUBNAME_COLUMN,
+ //TYPE_COLUMN,
+ TTH_COLUMN,
+ NUM_COLUMNS // Make sure this is allways last
+ };
+
+ /**
+ * Returns the amount of rows that should be displayed by the view.
+ * @param index Index of parent.
+ */
+ virtual int rowCount( const QModelIndex& ) const { return filteredResults.size(); }
+ /**
+ * Returns the amount of columns that the view shoulf display.
+ * @param index Index of parent.
+ */
+ virtual int columnCount( const QModelIndex& ) const { return NUM_COLUMNS; } // Make sure to change this if more parameters are added to the search entry
+
+ /**
+ * Returns the data that should be displayed for the specified index with the specified role.
+ * @param index Index of the item in the model that data is requested for.
+ * @param role The type of data that should be returned.
+ * @see Qt::DisplayRole.
+ */
+ virtual QVariant data( const QModelIndex& index, int role = Qt::DisplayRole ) const;
+
+ /**
+ * Returns the data that should be displayed in the header for a certain role.
+ * @param section The column
+ * @param orientation If it is horisontal or vertical header information.
+ * @param role The type of data, icon, text etc.
+ */
+ virtual QVariant headerData( int section, Qt::Orientation orientation, int role = Qt::DisplayRole ) const;
+
+ /**
+ * Returns the SearchEntry specified by index.
+ * @param index The index of the search entry we are interested in.
+ * @throws std::out_of_range
+ */
+ const SearchEntry& getEntry( int index ) const;
+
+ virtual void sort ( int column, Qt::SortOrder order = Qt::AscendingOrder );
+
+ /**
+ * Clears the model of any search results and emit a layout changed signal.
+ */
+ void clear();
+
+ //bool isEmpty() { return searchResults.size()==0; }
+
+public slots:
+ /**
+ * Called when search results have been received. Updates the model by adding the result and
+ * notifies all views that data has been added.
+ */
+ void onSearchResult( int session, const SearchEntry& sr );
+ void onSearchResult( int session, const QList<SearchEntry>& sr );
+ //! Enable / disable the filter. Currently only zero slot filtering is supported, this will change.
+ void enableFilter(bool yes);
+
+private:
+
+ void sortedInsert(const SearchEntry&);
+
+ /**
+ * Contains all the search results.
+ */
+ QList<SearchEntry> searchResults;
+
+ //! Contains the results that are displayed.
+ QList<SearchEntry> filteredResults;
+
+ /**
+ * Provides icons for the items in the list.
+ */
+ QFileIconProvider* m_pIconProvider;
+
+ bool filterEnabled;
+ Qt::SortOrder sortOrder;
+};
+
+#endif
diff --git a/ui/searchwidget.cpp b/ui/searchwidget.cpp
new file mode 100644
index 0000000..c1f5cb4
--- /dev/null
+++ b/ui/searchwidget.cpp
@@ -0,0 +1,165 @@
+//
+// C++ Implementation: searchwidget
+//
+// Description:
+//
+//
+// Author: Rikard Bjorklind <olof@users.sourceforge.net>, (C) 2006
+//
+// Copyright: See COPYING file that comes with this distribution
+//
+//
+#include "searchwidget.h"
+#include "../backend/CommonTypes.h"
+#include "log.h"
+#include <stdexcept>
+#include <QtGui>
+
+SearchWidget::SearchWidget(QWidget *parent)
+ : QWidget(parent)
+{
+
+ ui.setupUi(this);
+ ui.searchView->setModel(&model);
+ ui.searchView->header()->setClickable(true);
+ ui.searchView->header()->setSortIndicatorShown(true);
+ ui.searchView->setContextMenuPolicy(Qt::CustomContextMenu);
+ connect(ui.searchView,SIGNAL(customContextMenuRequested ( const QPoint&)),SLOT(onContextMenu(const QPoint&)));
+ contextMenu = new QMenu(this);
+ /*QAction* dlAction = */contextMenu->addAction(QIcon(":/images/download.png"), tr("Download"),this,SLOT(onContextDownload()));
+ /*QAction* dltoAction = */contextMenu->addAction(QIcon(":/images/downloadto.png"), tr("Download to..."),this,SLOT(onContextDownloadTo()));
+ /*QAction* dldirAction = */contextMenu->addAction(QIcon(":/images/dldir.png"), tr("Download whole directory"),this,SLOT(onContextDownloadDir() ));
+ /*QAction* dldirtoAction = */contextMenu->addAction(QIcon(":/images/dldirto.png"), tr("Download directory to..."),this,SLOT(onContextDownloadDirTo() ));
+ /*QAction* tthSearchAction = */contextMenu->addAction(QIcon(":/images/tthsearch.png"), tr("Search for alternates" ),this, SLOT(onContextTTHSearch()));
+ /*QAction* getFileListAction = */contextMenu->addAction(QIcon(":/images/filelist.png"), tr("Get file list" ), this, SLOT(onContextGetFileList()));
+}
+
+
+SearchWidget::~SearchWidget()
+{
+ logger->debug("SearchWidget dtor");
+}
+
+void SearchWidget::on_searchButton_pressed( )
+{
+ qint64 size = ui.sizeSpin->value();
+ switch(ui.sizeTypeCombo->currentIndex())
+ {
+ case 3: // gib
+ size*=1024;
+ case 2: //mb
+ size*=1024;
+ case 1:
+ size*=1024;
+ default:
+ ;
+ }
+ int sizemode = ui.sizeModeCombo->currentIndex()==0 ? backend::SIZE_ATLEAST : backend::SIZE_ATMOST;
+ int typemode = ui.typeCombo->currentIndex();
+
+ logger->info(QString("emitting search with size: %1 sizemode: %2 typemode: %3 text: %4").
+ arg(size).arg(sizemode).arg(typemode).arg(ui.searchCombo->currentText()));
+
+ model.clear();
+
+ emit search(0,size,sizemode,typemode,ui.searchCombo->currentText(),this);
+}
+
+void SearchWidget::on_searchCombo_activated( const QString &)
+{
+ on_searchButton_pressed();
+}
+
+void SearchWidget::searchResults( int session, const QList< SearchEntry > sr)
+{
+ model.onSearchResult( session, sr );
+}
+
+void SearchWidget::on_searchView_doubleClicked( const QModelIndex &index )
+{
+ download(index);
+}
+
+void SearchWidget::download( const QModelIndex &index )
+{
+ try
+ {
+ if (index.isValid())
+ {
+ SearchEntry entry = model.getEntry( index.row() );
+ logger->debug( "Downloading file:" + entry.getFileName() );
+ emit downloadFile( entry );
+ }
+ else
+ logger->error("SearchWidget::download() : index.isValid() == false \n");
+ }
+ catch(const std::out_of_range& e)
+ {
+ logger->error("SearchWidget::download() : out_of_range exception\n");
+ }
+}
+
+
+void SearchWidget::downloadTo( const QModelIndex &index )
+{
+ if(!index.isValid()) return;
+ SearchEntry entry = model.getEntry(index.row());
+ QString dir = QFileDialog::getExistingDirectory ( this, tr("Select destination directory"), QString());
+ if(!dir.isEmpty())
+ emit downloadFileTo(entry, dir);
+}
+
+void SearchWidget::onContextMenu( const QPoint &p )
+{
+ if( ui.searchView->indexAt(p).isValid() ) {
+ contextMenu->exec(QCursor::pos());
+ }
+}
+
+void SearchWidget::onContextDownload( )
+{
+ download( ui.searchView->currentIndex() );
+}
+
+void SearchWidget::onContextDownloadTo( )
+{
+ downloadTo( ui.searchView->currentIndex() );
+}
+
+void SearchWidget::onContextDownloadDir( )
+{
+ QModelIndex index = ui.searchView->currentIndex();
+ SearchEntry entry = model.getEntry( index.row() );
+ emit downloadFile( entry.getUserId(), entry.getPath(), -1, QString() );
+}
+
+void SearchWidget::onContextDownloadDirTo( )
+{
+ QModelIndex index = ui.searchView->currentIndex();
+ SearchEntry entry = model.getEntry( index.row() );
+ QString dir = QFileDialog::getExistingDirectory ( this, tr("Select destination directory"), QString());
+ if(!dir.isEmpty())
+ emit downloadFileTo(entry.getUserId(), entry.getPath(), -1, QString(), dir);
+}
+
+void SearchWidget::onContextTTHSearch( )
+{
+ QModelIndex index = ui.searchView->currentIndex();
+ SearchEntry entry = model.getEntry( index.row() );
+ if(!entry.getTTH().isEmpty())
+ emit search(0,entry.getFileSize(),backend::SIZE_ATLEAST,backend::TYPE_TTH,entry.getTTH(),NULL);
+ else
+ emit search(0,entry.getFileSize(),backend::SIZE_ATLEAST,backend::TYPE_ANY,entry.getFileName(),NULL);
+}
+
+void SearchWidget::onContextGetFileList( )
+{
+ QModelIndex index = ui.searchView->currentIndex();
+ SearchEntry entry = model.getEntry( index.row() );
+ emit getUserFileList( entry.getUserId() );
+}
+
+void SearchWidget::on_hideZeroSlots_stateChanged( int state )
+{
+ model.enableFilter( state == Qt::Checked );
+}
diff --git a/ui/searchwidget.h b/ui/searchwidget.h
new file mode 100644
index 0000000..2d674a3
--- /dev/null
+++ b/ui/searchwidget.h
@@ -0,0 +1,57 @@
+#ifndef SEARCHWIDGET_H
+#define SEARCHWIDGET_H
+
+#include <QWidget>
+#include "ui_search.h"
+#include "searchtablemodel.h"
+#include <rpcdriver/types.h>
+
+/**
+Contains the search widget. Implements associated operations
+
+ @author Rikard Bjorklind <olof@users.sourceforge.net>
+*/
+class SearchWidget : public QWidget
+{
+ Q_OBJECT
+public:
+ SearchWidget(QWidget *parent = 0);
+ ~SearchWidget();
+ //bool isEmpty() { return model.isEmpty(); }
+
+public slots:
+ //! Called on arriving results
+ void searchResults(int,const QList<SearchEntry>);
+
+private:
+ Ui::searchForm ui;
+ SearchTableModel model;
+ QMenu *contextMenu;
+
+private slots:
+ void on_searchButton_pressed();
+ void on_searchCombo_activated(const QString&);
+ void on_searchView_doubleClicked(const QModelIndex&);
+ void on_hideZeroSlots_stateChanged(int);
+
+ //! Downloads a file given a search result index.
+ void download(const QModelIndex&);
+ void downloadTo(const QModelIndex&);
+ void onContextMenu(const QPoint&);
+ void onContextDownload();
+ void onContextDownloadTo();
+ void onContextDownloadDir();
+ void onContextDownloadDirTo();
+ void onContextTTHSearch();
+ void onContextGetFileList();
+
+signals:
+ void search(int session, qint64 size, int sizeMode, int typeMode, const QString& search, SearchWidget* originator);
+ void downloadFile( const SearchEntry& );
+ void downloadFile( int uId, const QString& file, int64 size, const QString& tth );
+ void downloadFileTo( const SearchEntry&, const QString& );
+ void downloadFileTo( int uId, const QString& file, int64 size, const QString& tth, const QString& dir );
+ void getUserFileList(int);
+};
+
+#endif
diff --git a/ui/session.cpp b/ui/session.cpp
new file mode 100644
index 0000000..9973fb8
--- /dev/null
+++ b/ui/session.cpp
@@ -0,0 +1,117 @@
+//
+// C++ Implementation: session
+//
+// Description:
+//
+//
+// Author: Rikard Bjorklind <olof@users.sourceforge.net>, (C) 2006
+//
+// Copyright: See COPYING file that comes with this distribution
+//
+//
+#include "session.h"
+#include "userlistmodel.h"
+#include <QtGui>
+#include <QMenu>
+
+Session::Session(int aId,
+ QWidget *parent)
+: QWidget(parent),
+ contextMenu(new QMenu(this)),
+ myId(aId)
+{
+ ui.setupUi(this);
+ userModel = new UserListModel;
+ ui.userView->setModel(userModel);
+
+ ui.userView->header()->setClickable(true);
+ ui.userView->header()->setSortIndicatorShown(true);
+
+ createMenu();
+}
+
+
+Session::~Session()
+{
+ delete userModel;
+}
+
+void Session::onFailed( const QString &reason )
+{
+ ui.mainChat->append(reason);
+}
+
+void Session::onPrivateChatMessage( const QString & /*from*/, const QString & msg )
+{
+ ui.mainChat->append(QString(tr("Private Message: ")) + msg);
+}
+
+void Session::onUsersUpdated( QList<User*> users )
+{
+ userModel->enableUpdateSignals( false );
+ for(int i=0;i < users.size();i++)
+ userModel->setUser( users[i] );
+ userModel->enableUpdateSignals( true );
+ userModel->signalLayoutChanged();
+}
+
+void Session::onUserRemoved( int id )
+{
+ userModel->removeUser(id);
+}
+
+void Session::onChatMessage( const QString &msg )
+{
+ ui.mainChat->append(msg);
+}
+
+void Session::onHubStats( qint64 totshared )
+{
+ shared = totshared;
+}
+
+void Session::onGetFileList()
+{
+ QModelIndexList selected = ui.userView->selectionModel()->selectedIndexes();
+ // We should only have one selected item
+ getFileList(selected.front());
+}
+
+void Session::on_chatEdit_returnPressed( )
+{
+ if( ui.chatEdit->text().length() > 0 )
+ emit sendChat(myId,ui.chatEdit->text());
+ ui.chatEdit->clear();
+}
+
+void Session::on_userView_doubleClicked( const QModelIndex& index )
+{
+ getFileList( index );
+}
+
+void Session::on_userView_customContextMenuRequested( const QPoint& pos )
+{
+ // Show the menu
+ contextMenu->popup( ui.userView->mapToGlobal(pos) );
+}
+
+void Session::createMenu()
+{
+ QAction* getFileListAct = contextMenu->addAction( tr("Get file list") );
+ connect( getFileListAct, SIGNAL(triggered()), this, SLOT(onGetFileList()) );
+
+ QAction* sendMsgAct = contextMenu->addAction( tr("Open private chat") );
+ connect( sendMsgAct, SIGNAL(triggered()), this, SLOT(onOpenChat()) );
+
+ QAction* addFavUserAct = contextMenu->addAction( tr("Add to favourites") );
+ connect( addFavUserAct, SIGNAL(triggered()), this, SLOT( onAddFav() ) );
+}
+
+
+void Session::getFileList( const QModelIndex& index )
+{
+ if( index.isValid() ) {
+ emit getUserFileList(userModel->getUser(index)->id);
+ }
+}
+
diff --git a/ui/session.h b/ui/session.h
new file mode 100644
index 0000000..c276a6c
--- /dev/null
+++ b/ui/session.h
@@ -0,0 +1,73 @@
+//
+// C++ Interface: session
+//
+// Description:
+//
+//
+// Author: Rikard Bjorklind <olof@users.sourceforge.net>, (C) 2006
+//
+// Copyright: See COPYING file that comes with this distribution
+//
+//
+#ifndef SESSION_H
+#define SESSION_H
+
+#include <boost/shared_ptr.hpp>
+
+#include <QWidget>
+#include "ui_hub.h"
+
+class QMennu;
+
+class UserListModel;
+class User;
+
+/**
+ Holds the session ui.
+ @author Rikard Bjorklind <olof@users.sourceforge.net>
+*/
+class Session : public QWidget
+{
+Q_OBJECT
+public:
+ Session(int aId,
+ QWidget *parent = 0);
+ ~Session();
+ int getId() {return myId;}
+ qint64 getTotalShared() {return shared;}
+
+public slots:
+ void onFailed(const QString&);
+ void onPrivateChatMessage(const QString& from,const QString& msg);
+ void onUsersUpdated(QList<User*> users);
+ void onChatMessage(const QString&);
+ void onUserRemoved(int id);
+ void onHubStats(qint64 totshared);
+
+ void onGetFileList();
+ void onOpenChat() {}
+ void onAddFav() {}
+
+ // Auto-slots
+ void on_chatEdit_returnPressed();
+ void on_userView_doubleClicked( const QModelIndex& index);
+ void on_userView_customContextMenuRequested( const QPoint& pos );
+
+private:
+ void createMenu();
+
+ void getFileList( const QModelIndex& index );
+
+ Ui::HubForm ui;
+ UserListModel* userModel;
+
+ boost::shared_ptr<QMenu> contextMenu;
+
+ int myId;
+ qint64 shared;
+signals:
+ void sendChat(int,const QString&);
+ void getUserFileList(int);
+};
+
+#endif
diff --git a/ui/sessionmanager.cpp b/ui/sessionmanager.cpp
new file mode 100644
index 0000000..6623748
--- /dev/null
+++ b/ui/sessionmanager.cpp
@@ -0,0 +1,124 @@
+//
+// C++ Implementation: sessionmanager
+//
+// Description:
+//
+//
+// Author: Rikard Bjorklind <olof@users.sourceforge.net>, (C) 2006
+//
+// Copyright: See COPYING file that comes with this distribution
+//
+//
+#include "sessionmanager.h"
+#include "session.h"
+#include <QtGui>
+
+SessionManager::SessionManager( QTabWidget * aSessionTabs, boost::shared_ptr< BackendConnection > aConnection, QObject * parent ) :
+ QObject(parent), backendConnection(aConnection), sessionTabs(aSessionTabs)
+{
+ connect( sessionTabs, SIGNAL(currentChanged(int)), SLOT(onActiveSessionChanged(int)));
+}
+
+SessionManager::~SessionManager()
+{
+}
+
+void SessionManager::createSession( int id )
+{
+ if(!sessionMap.contains(id)) {
+ Session* session = new Session(id); // no parent here since we add it to a tabbar.
+ // This connect forwards chat signals from the session to us so that we send them to the backend.
+ QObject::connect(session,SIGNAL(sendChat( int,const QString&)),this,SLOT(sendChat( int, const QString& )));
+ QObject::connect(session,SIGNAL(getUserFileList(int)),this,SLOT(getUserFileList( int )));
+ sessionMap[id] = session;
+ sessionTabs->addTab(session,tr("Connecting..."));
+ }
+}
+
+void SessionManager::hubUpdated( int id, const QString& hubName)
+{
+ if(sessionMap.contains(id)) {
+ Session* session = sessionMap[id];
+ int index = sessionTabs->indexOf(session);
+ QString hubn = hubName;
+ if(hubName.length() > 30) {
+ hubn.truncate( 30 );
+ hubn = hubn + "...";
+ }
+ sessionTabs->setTabText(index,hubn);
+ sessionTabs->setCurrentIndex(index);
+ }
+}
+
+void SessionManager::connectionFailed( int id, const QString& reason)
+{
+ if(sessionMap.contains(id)) {
+ sessionMap[id]->onFailed(reason);
+ } else QMessageBox::critical(0,tr("Connection Failed"),tr("Connection failed: ") + reason);
+}
+
+void SessionManager::privateChat(int id,const QString& from,const QString& msg)
+{
+ if(sessionMap.contains(id))
+ sessionMap[id]->onPrivateChatMessage(from,msg);
+}
+
+void SessionManager::usersUpdated( int id , QList< User * > users )
+{
+ if(sessionMap.contains(id)) sessionMap[id]->onUsersUpdated(users);
+}
+
+void SessionManager::chatMessage( int id, const QString &msg )
+{
+ if(sessionMap.contains(id)) sessionMap[id]->onChatMessage(msg);
+}
+
+void SessionManager::sendChat( int id, const QString &msg )
+{
+ backendConnection->sendChat(id,msg);
+}
+
+void SessionManager::getUserFileList( int userId )
+{
+ backendConnection->getUserFileList( userId );
+}
+
+void SessionManager::sessionInfo( int id, const QString &hubName, const QString &/*url*/, const QList< User * > users )
+{
+ createSession( id );
+ hubUpdated( id,hubName );
+ usersUpdated( id,users );
+ // TODO put the url in the session so that the fav command works.
+}
+
+void SessionManager::closeCurrentSession( )
+{
+ try {
+ Session* session = qobject_cast<Session*>(sessionTabs->currentWidget());
+ sessionTabs->removeTab(sessionTabs->currentIndex());
+ sessionMap.remove( session->getId() );
+ backendConnection->closeSession( session->getId() );
+ delete session;
+ }
+ catch(...) {}
+}
+
+void SessionManager::userRemoved( int session, int userid )
+{
+ if(sessionMap.contains( session )) sessionMap[session]->onUserRemoved(userid);
+}
+
+void SessionManager::onHubStats( int session, qint64 sharesize)
+{
+ if(sessionMap.contains(session)) {
+ sessionMap[session]->onHubStats(sharesize);
+ if( sessionTabs->currentWidget() == sessionMap[session] ) emit currentHubTotalShare( sharesize );
+ }
+
+}
+
+void SessionManager::onActiveSessionChanged( int /*session*/ )
+{
+ Session* sess = (Session*)sessionTabs->currentWidget();
+ emit currentHubTotalShare( sess->getTotalShared() );
+}
diff --git a/ui/sessionmanager.h b/ui/sessionmanager.h
new file mode 100644
index 0000000..75b12e3
--- /dev/null
+++ b/ui/sessionmanager.h
@@ -0,0 +1,66 @@
+//
+// C++ Interface: sessionmanager
+//
+// Description:
+//
+//
+// Author: Rikard Bjorklind <olof@users.sourceforge.net>, (C) 2006
+//
+// Copyright: See COPYING file that comes with this distribution
+//
+//
+#ifndef SESSIONMANAGER_H
+#define SESSIONMANAGER_H
+
+#include <QObject>
+#include <QMap>
+#include "backendconnection.h"
+
+class QTabWidget;
+class Session;
+class User;
+
+/**
+ Manages the mapping between integers and sessions, creates session widgets.
+ @author Rikard Bjorklind <olof@users.sourceforge.net>
+*/
+class SessionManager : public QObject
+{
+ Q_OBJECT
+public:
+ SessionManager(QTabWidget *aSessionTabs,
+ boost::shared_ptr< BackendConnection > aConnection,
+ QObject *parent = 0);
+
+ ~SessionManager();
+
+public slots:
+ void createSession(int id);
+ void closeCurrentSession();
+ void hubUpdated(int id,const QString& hubName);
+ void connectionFailed(int id, const QString& reason);
+ void privateChat(int,const QString&,const QString&);
+ void usersUpdated(int, QList<User*>);
+ void userRemoved(int,int);
+ void chatMessage(int,const QString&);
+ void sessionInfo(int,const QString&,const QString&,const QList<User*>);
+
+signals:
+ void currentHubTotalShare(qint64);
+
+private slots:
+ void sendChat(int,const QString&);
+ void getUserFileList(int);
+ void onHubStats(int,qint64);
+ void onActiveSessionChanged(int);
+
+private:
+ boost::shared_ptr< BackendConnection > backendConnection;
+ QTabWidget* sessionTabs;
+ QMap<int,Session*> sessionMap;
+
+
+
+};
+
+#endif
diff --git a/ui/settings.ui b/ui/settings.ui
new file mode 100644
index 0000000..3ec3c6f
--- /dev/null
+++ b/ui/settings.ui
@@ -0,0 +1,536 @@
+<ui version="4.0" >
+ <author></author>
+ <comment></comment>
+ <exportmacro></exportmacro>
+ <class>SettingsDialog</class>
+ <widget class="QDialog" name="SettingsDialog" >
+ <property name="geometry" >
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>493</width>
+ <height>669</height>
+ </rect>
+ </property>
+ <property name="sizePolicy" >
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="windowTitle" >
+ <string>Settings</string>
+ </property>
+ <layout class="QVBoxLayout" >
+ <property name="margin" >
+ <number>9</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item>
+ <widget class="QTabWidget" name="tabWidget" >
+ <widget class="QWidget" name="tab" >
+ <attribute name="title" >
+ <string>Basic</string>
+ </attribute>
+ <layout class="QVBoxLayout" >
+ <property name="margin" >
+ <number>9</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item>
+ <widget class="QGroupBox" name="groupBox_3" >
+ <property name="title" >
+ <string>Downloads</string>
+ </property>
+ <layout class="QGridLayout" >
+ <property name="margin" >
+ <number>9</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item row="1" column="2" >
+ <widget class="QToolButton" name="finisheddirButton" >
+ <property name="text" >
+ <string>...</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1" >
+ <widget class="QLineEdit" name="finisheddirEdit" />
+ </item>
+ <item row="1" column="0" >
+ <widget class="QLabel" name="label_8" >
+ <property name="text" >
+ <string>Finished directory</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="2" >
+ <widget class="QToolButton" name="dldirButton" >
+ <property name="text" >
+ <string>...</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1" >
+ <widget class="QLineEdit" name="dldirEdit" />
+ </item>
+ <item row="0" column="0" >
+ <widget class="QLabel" name="label_7" >
+ <property name="text" >
+ <string>Download directory</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="groupBox" >
+ <property name="title" >
+ <string>User Information</string>
+ </property>
+ <layout class="QGridLayout" >
+ <property name="margin" >
+ <number>9</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item row="2" column="1" >
+ <widget class="QLineEdit" name="emailEdit" />
+ </item>
+ <item row="1" column="1" >
+ <widget class="QLineEdit" name="descriptionEdit" />
+ </item>
+ <item row="0" column="1" >
+ <widget class="QLineEdit" name="nickEdit" />
+ </item>
+ <item row="2" column="0" >
+ <widget class="QLabel" name="label_3" >
+ <property name="text" >
+ <string>Email</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0" >
+ <widget class="QLabel" name="label" >
+ <property name="text" >
+ <string>Nick</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0" >
+ <widget class="QLabel" name="label_2" >
+ <property name="text" >
+ <string>Description</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="groupBox_2" >
+ <property name="title" >
+ <string>Connection</string>
+ </property>
+ <layout class="QGridLayout" >
+ <property name="margin" >
+ <number>9</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item row="3" column="1" >
+ <widget class="QLineEdit" name="udpEdit" />
+ </item>
+ <item row="2" column="1" >
+ <widget class="QLineEdit" name="tcpEdit" />
+ </item>
+ <item row="3" column="0" >
+ <widget class="QLabel" name="label_6" >
+ <property name="text" >
+ <string>UDP Port</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0" >
+ <widget class="QLabel" name="label_5" >
+ <property name="text" >
+ <string>TCP Port</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1" >
+ <widget class="QLineEdit" name="ipEdit" />
+ </item>
+ <item row="1" column="0" >
+ <widget class="QLabel" name="label_4" >
+ <property name="text" >
+ <string>IP (empty for auto)</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0" >
+ <widget class="QCheckBox" name="activeCheck" >
+ <property name="text" >
+ <string>Active mode</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="groupBox_4" >
+ <property name="title" >
+ <string>Shared Directories</string>
+ </property>
+ <layout class="QVBoxLayout" >
+ <property name="margin" >
+ <number>9</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item>
+ <widget class="NiceTreeView" name="sharedView" >
+ <property name="acceptDrops" >
+ <bool>true</bool>
+ </property>
+ <property name="showDropIndicator" stdset="0" >
+ <bool>true</bool>
+ </property>
+ <property name="dragEnabled" >
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="tab_2" >
+ <attribute name="title" >
+ <string>Advanced</string>
+ </attribute>
+ <layout class="QVBoxLayout" >
+ <property name="margin" >
+ <number>9</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item>
+ <widget class="QGroupBox" name="groupBox_5" >
+ <property name="title" >
+ <string>Backend settings</string>
+ </property>
+ <layout class="QVBoxLayout" >
+ <property name="margin" >
+ <number>9</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item>
+ <layout class="QHBoxLayout" >
+ <property name="margin" >
+ <number>0</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item>
+ <widget class="QRadioButton" name="localBackendRadio" >
+ <property name="text" >
+ <string>Use a local backend instance on port</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QSpinBox" name="localBackendPort" >
+ <property name="maximum" >
+ <number>65535</number>
+ </property>
+ <property name="minimum" >
+ <number>1</number>
+ </property>
+ <property name="value" >
+ <number>6161</number>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer>
+ <property name="orientation" >
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" >
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QRadioButton" name="remoteBackendRadio" >
+ <property name="text" >
+ <string>Connect to the following backend on startup</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" >
+ <property name="margin" >
+ <number>0</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item>
+ <widget class="QLabel" name="label_9" >
+ <property name="text" >
+ <string>&amp;Hostname / IP: </string>
+ </property>
+ <property name="buddy" >
+ <cstring>remoteBackendURL</cstring>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLineEdit" name="remoteBackendURL" />
+ </item>
+ <item>
+ <widget class="QLabel" name="label_10" >
+ <property name="text" >
+ <string>Port: </string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QSpinBox" name="remoteBackendPort" >
+ <property name="suffix" >
+ <string/>
+ </property>
+ <property name="maximum" >
+ <number>65535</number>
+ </property>
+ <property name="minimum" >
+ <number>1</number>
+ </property>
+ <property name="value" >
+ <number>6161</number>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" >
+ <property name="margin" >
+ <number>0</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item>
+ <widget class="QLabel" name="label_11" >
+ <property name="text" >
+ <string>Password:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLineEdit" name="passwordEdit" >
+ <property name="maxLength" >
+ <number>100</number>
+ </property>
+ <property name="echoMode" >
+ <enum>QLineEdit::Password</enum>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer>
+ <property name="orientation" >
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" >
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <spacer>
+ <property name="orientation" >
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" >
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" >
+ <property name="margin" >
+ <number>0</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item>
+ <spacer>
+ <property name="orientation" >
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" >
+ <size>
+ <width>131</width>
+ <height>31</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QPushButton" name="okButton" >
+ <property name="text" >
+ <string>&amp;OK</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="cancelButton" >
+ <property name="text" >
+ <string>&amp;Cancel</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <pixmapfunction></pixmapfunction>
+ <customwidgets>
+ <customwidget>
+ <class>NiceTreeView</class>
+ <extends>QTreeView</extends>
+ <header>nicetreeview.h</header>
+ <container>0</container>
+ <pixmap></pixmap>
+ </customwidget>
+ </customwidgets>
+ <tabstops>
+ <tabstop>tabWidget</tabstop>
+ <tabstop>dldirEdit</tabstop>
+ <tabstop>dldirButton</tabstop>
+ <tabstop>finisheddirEdit</tabstop>
+ <tabstop>finisheddirButton</tabstop>
+ <tabstop>nickEdit</tabstop>
+ <tabstop>descriptionEdit</tabstop>
+ <tabstop>emailEdit</tabstop>
+ <tabstop>activeCheck</tabstop>
+ <tabstop>ipEdit</tabstop>
+ <tabstop>tcpEdit</tabstop>
+ <tabstop>udpEdit</tabstop>
+ <tabstop>sharedView</tabstop>
+ <tabstop>okButton</tabstop>
+ <tabstop>cancelButton</tabstop>
+ <tabstop>remoteBackendRadio</tabstop>
+ <tabstop>remoteBackendURL</tabstop>
+ </tabstops>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>cancelButton</sender>
+ <signal>clicked()</signal>
+ <receiver>SettingsDialog</receiver>
+ <slot>reject()</slot>
+ <hints>
+ <hint type="sourcelabel" >
+ <x>369</x>
+ <y>253</y>
+ </hint>
+ <hint type="destinationlabel" >
+ <x>179</x>
+ <y>282</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>localBackendRadio</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>remoteBackendURL</receiver>
+ <slot>setDisabled(bool)</slot>
+ <hints>
+ <hint type="sourcelabel" >
+ <x>161</x>
+ <y>215</y>
+ </hint>
+ <hint type="destinationlabel" >
+ <x>242</x>
+ <y>281</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>localBackendRadio</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>remoteBackendPort</receiver>
+ <slot>setDisabled(bool)</slot>
+ <hints>
+ <hint type="sourcelabel" >
+ <x>161</x>
+ <y>215</y>
+ </hint>
+ <hint type="destinationlabel" >
+ <x>427</x>
+ <y>281</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>localBackendRadio</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>localBackendPort</receiver>
+ <slot>setEnabled(bool)</slot>
+ <hints>
+ <hint type="sourcelabel" >
+ <x>161</x>
+ <y>215</y>
+ </hint>
+ <hint type="destinationlabel" >
+ <x>379</x>
+ <y>215</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>
diff --git a/ui/settingsdialog.cpp b/ui/settingsdialog.cpp
new file mode 100644
index 0000000..851998b
--- /dev/null
+++ b/ui/settingsdialog.cpp
@@ -0,0 +1,215 @@
+//
+// C++ Implementation: settingsdialog
+//
+// Description:
+//
+//
+// Author: Rikard Bjorklind <olof@users.sourceforge.net>, (C) 2006
+//
+// Copyright: See COPYING file that comes with this distribution
+//
+//
+#include "settingsdialog.h"
+#include "shareitemmodel.h"
+
+#include "log.h"
+#include <QtGui>
+
+SettingsDialog::SettingsDialog(boost::shared_ptr<BackendConnection> aBackendConnection,QWidget *parent)
+ : QDialog(parent),backendConnection(aBackendConnection)
+{
+ ui.setupUi(this);
+ // Create the shareitem model for the shared dir treeview.
+ shareItemModel = new ShareItemModel;
+ ui.sharedView->setModel(shareItemModel);
+ ui.sharedView->setContextMenuPolicy(Qt::CustomContextMenu);
+ connect(ui.sharedView,SIGNAL(customContextMenuRequested ( const QPoint&)),SLOT(onContextMenu(const QPoint&)));
+ connect(shareItemModel,SIGNAL(dirDropped( const QString& )),SLOT(onDirDropped( const QString&)));
+
+ // Init local settings
+ QSettings settings("dc-qt.sf.net","dcqt");
+ BackendConnectionType backendConnectionType = (BackendConnectionType)settings.value("bctype",(int)USE_LOCAL_BACKEND).toInt();
+ QString backendConnectionURL = settings.value("bcurl","localhost").toString();
+ int localPort = settings.value("bclocalport",6161).toInt();
+ int remotePort = settings.value("bcremoteport",6161).toInt();
+ srand(time(NULL));
+ QString defaultPassword = QString::number(rand() % 100000 + 100000);
+ QString password = settings.value("bcpassword",defaultPassword).toString();
+
+ // Set the loaded settings in the gui
+ ui.localBackendPort->setValue(localPort);
+ ui.remoteBackendPort->setValue(remotePort);
+ ui.remoteBackendURL->setText(backendConnectionURL);
+ ui.passwordEdit->setText(password);
+ // Preselect the radio button
+ if(backendConnectionType==USE_LOCAL_BACKEND) ui.localBackendRadio->setChecked(true);
+ else ui.remoteBackendRadio->setChecked(true);
+
+ connect(ui.sharedView,SIGNAL(leftMousePressed()),SLOT(onLeftMouse()));
+
+ contextMenu = new QMenu(this);
+ /*QAction* removeAction = */contextMenu->addAction( tr("Remove") );
+}
+
+
+SettingsDialog::~SettingsDialog()
+{
+}
+
+QList< QString > SettingsDialog::getRequiredSettings( )
+{
+ QList<QString> settings;
+ settings += "NICK";
+ settings += "DESCRIPTION";
+ settings += "EMAIL";
+ settings += "CONNECTION_TYPE";
+ settings += "SERVER";
+ settings += "IN_PORT";
+ settings += "UDP_PORT";
+ settings += "DOWNLOAD_DIRECTORY";
+ settings += "TEMP_DOWNLOAD_DIRECTORY";
+ return settings;
+}
+
+void SettingsDialog::settingsInfo(const QList<QString>& keys,const QList<QVariant>& values)
+{
+ QListIterator<QString> ki(keys);
+ QListIterator<QVariant> vi(values);
+
+ while(ki.hasNext())
+ {
+ const QString key = ki.next();
+ const QVariant value = vi.next();
+ if(key=="NICK") ui.nickEdit->setText( value.toString() );
+ else if(key=="DESCRIPTION") ui.descriptionEdit->setText( value.toString() );
+ else if(key=="EMAIL") ui.emailEdit->setText( value.toString() );
+ else if(key=="CONNECTION_TYPE") ui.activeCheck->setChecked( value.toInt()==0 );
+ else if(key=="SERVER") ui.ipEdit->setText(value.toString());
+ else if(key=="IN_PORT") ui.tcpEdit->setText(QString::number(value.toInt()));
+ else if(key=="UDP_PORT") ui.udpEdit->setText(QString::number(value.toInt()));
+ else if(key=="DOWNLOAD_DIRECTORY") ui.finisheddirEdit->setText(value.toString());
+ else if(key=="TEMP_DOWNLOAD_DIRECTORY") ui.dldirEdit->setText(value.toString());
+ }
+}
+
+void SettingsDialog::showEvent(QShowEvent*)
+{
+ backendConnection->getSettings(getRequiredSettings());
+ backendConnection->getSharedDirs();
+}
+
+void SettingsDialog::on_okButton_pressed()
+{
+ bool ok1,ok2;
+ int tcpport = ui.tcpEdit->text().toInt(&ok1);
+ int udpport = ui.udpEdit->text().toInt(&ok2);
+
+ if(!ok1) {
+ QMessageBox::critical(this,tr("Settings Error"),tr("TCP port must be a number"));
+ return;
+ }
+ if(!ok2) {
+ QMessageBox::critical(this,tr("Settings Error"),tr("UDP port must be a number"));
+ return;
+ }
+
+ QList<QPair<QString,QVariant> > settings;
+ settings += QPair<QString,QVariant>("NICK",ui.nickEdit->text());
+ settings += QPair<QString,QVariant>("DESCRIPTION",ui.descriptionEdit->text());
+ settings += QPair<QString,QVariant>("EMAIL",ui.emailEdit->text());
+ settings += QPair<QString,QVariant>("CONNECTION_TYPE",ui.activeCheck->isChecked()?0:1);
+ settings += QPair<QString,QVariant>("SERVER",ui.ipEdit->text());
+ settings += QPair<QString,QVariant>("IN_PORT",tcpport);
+ settings += QPair<QString,QVariant>("UDP_PORT",udpport);
+ settings += QPair<QString,QVariant>("DOWNLOAD_DIRECTORY",ui.finisheddirEdit->text());
+ settings += QPair<QString,QVariant>("TEMP_DOWNLOAD_DIRECTORY",ui.dldirEdit->text());
+
+ backendConnection->setSettings(settings);
+
+ // Save local settings
+ QSettings ls("dc-qt.sf.net","dcqt");
+ ls.setValue("bctype", ui.localBackendRadio->isChecked() ? USE_LOCAL_BACKEND : USE_REMOTE_BACKEND);
+ ls.setValue("bcurl", ui.remoteBackendURL->text());
+ ls.setValue("bclocalport", ui.localBackendPort->value());
+ ls.setValue("bcremoteport", ui.remoteBackendPort->value());
+ ls.setValue("bcpassword", ui.passwordEdit->text());
+
+ accept();
+}
+
+void SettingsDialog::on_dldirButton_pressed()
+{
+ // TODO
+//if( BackendConnection::instance()->isConnectionLocal())
+ // {
+ QString dir = QFileDialog::getExistingDirectory(this,tr("Choose incomplete directory"),
+ QDir::homePath());
+ if(dir!=QString::null && !dir.isEmpty())
+ ui.dldirEdit->setText(dir);
+ // }
+ // else
+ // QMessageBox::information(this,tr("Problematic request"),tr("You cannot browse when connected to a remote backend."),tr("Ok"));
+}
+
+void SettingsDialog::on_finisheddirButton_pressed()
+{
+ // TODO
+ //if( BackendConnection::instance()->isConnectionLocal())
+ // {
+// logger->info(findirEdit->text());
+ QString dir = QFileDialog::getExistingDirectory(this,tr("Choose finished directory"),
+ QDir::homePath());
+ if(dir!=QString::null && !dir.isEmpty())
+ ui.finisheddirEdit->setText(dir);
+ // }
+ // else
+ // QMessageBox::information(this,tr("Problematic request"),tr("You cannot browse when connected to a remote backend."),tr("Ok"));
+
+}
+
+void SettingsDialog::sharedDirs( const QList<ShareItem> dirs)
+{
+ shareItemModel->clear();
+ for(int i=0;i<dirs.size();i++)
+ shareItemModel->add( dirs[i] );
+}
+/*
+void SettingsDialog::on_removeShareButton_pressed( )
+{
+ QModelIndex current = ui.sharedView->currentIndex();
+ QString name = shareItemModel->getDir(current.row());
+ backendConnection->delShare(name);
+ shareItemModel->remove(current.row());
+}
+*/
+void SettingsDialog::onDirDropped( const QString &dir )
+{
+ QString name = QInputDialog::getText(0,tr("Enter name for share"), "Enter a name for the share " + dir);
+ if(!name.isEmpty() && !name.isNull()) {
+ backendConnection->addShare(name,dir);
+ shareItemModel->add(name,dir);
+ }
+}
+
+void SettingsDialog::onLeftMouse( )
+{
+ QString vName = QInputDialog::getText(this,tr("Enter name"),tr("Enter a name for this share"));
+ if(!vName.isEmpty()) {
+ QString dir = QFileDialog::getExistingDirectory(this,tr("Choose a directory"));
+ if(!dir.isEmpty()) {
+ backendConnection->addShare(vName,dir);
+ shareItemModel->add(vName,dir);
+ }
+ }
+}
+
+void SettingsDialog::onContextMenu( const QPoint & )
+{
+ QAction* a = contextMenu->exec(QCursor::pos());
+ if(a) {
+ QModelIndex current = ui.sharedView->currentIndex();
+ QString name = shareItemModel->getDir(current.row());
+ backendConnection->delShare(name);
+ shareItemModel->remove(current.row());
+ }
+}
diff --git a/ui/settingsdialog.h b/ui/settingsdialog.h
new file mode 100644
index 0000000..f64aff8
--- /dev/null
+++ b/ui/settingsdialog.h
@@ -0,0 +1,71 @@
+//
+// C++ Interface: settingsdialog
+//
+// Description:
+//
+//
+// Author: Rikard Bjorklind <olof@users.sourceforge.net>, (C) 2006
+//
+// Copyright: See COPYING file that comes with this distribution
+//
+//
+#ifndef SETTINGSDIALOG_H
+#define SETTINGSDIALOG_H
+
+#include <QDialog>
+#include <boost/shared_ptr.hpp>
+#include "ui_settings.h"
+#include "backendconnection.h"
+#include "shareitemmodel.h"
+
+class QShowEvent;
+class ShareItemModel;
+class QMenu;
+
+/**
+ The settings dialog.
+ @author Rikard Bjorklind <olof@users.sourceforge.net>
+*/
+class SettingsDialog : public QDialog
+{
+Q_OBJECT
+public:
+ //! Constructor
+ SettingsDialog(boost::shared_ptr<BackendConnection> aBackendConnection,QWidget *parent = 0);
+ //! Destructor
+ virtual ~SettingsDialog();
+ enum BackendConnectionType {USE_LOCAL_BACKEND,USE_REMOTE_BACKEND};
+
+public slots:
+ //! Called when new settings arrive from the RpcDriver.
+ void settingsInfo(const QList<QString>&,const QList<QVariant>&);
+ //! Called when the shared dirs arrive.
+ void sharedDirs(const QList<ShareItem>);
+
+protected:
+ //! Requests settings data from the backend.
+ virtual void showEvent(QShowEvent *event);
+
+private:
+ Ui::SettingsDialog ui;
+ //! Returns a list of setting keys required for normal operation.
+ QList<QString> getRequiredSettings();
+ boost::shared_ptr<BackendConnection> backendConnection;
+ ShareItemModel* shareItemModel;
+ QMenu *contextMenu;
+
+
+private slots:
+ void on_dldirButton_pressed();
+ void on_finisheddirButton_pressed();
+
+ //! Called when the ok button is pressed.
+ //! Stores the settings and accepts the dialog.
+ void on_okButton_pressed();
+
+ void onDirDropped(const QString&);
+ void onLeftMouse();
+ void onContextMenu(const QPoint&);
+};
+
+#endif
diff --git a/ui/shareitemmodel.cpp b/ui/shareitemmodel.cpp
new file mode 100644
index 0000000..9f4b5ff
--- /dev/null
+++ b/ui/shareitemmodel.cpp
@@ -0,0 +1,120 @@
+//
+// C++ Implementation: shareitemmodel
+//
+// Description:
+//
+//
+// Author: Rikard Björklind <olof@linux.nu>, (C) 2005
+//
+// Copyright: See COPYING file that comes with this distribution
+//
+//
+#include "shareitemmodel.h"
+#include <QtGui>
+#include "log.h"
+
+ShareItemModel::ShareItemModel()
+{
+}
+
+QVariant ShareItemModel::data ( const QModelIndex & index, int role ) const
+{
+ if(!index.isValid()) return QVariant();
+
+ // Special case
+ if(role == Qt::DisplayRole && items.size()==0) return tr("Click or drag to add directories");
+ if(role == Qt::TextColorRole && items.size()==0) return QColor(100,100,100,255);
+ if(role == Qt::TextAlignmentRole && items.size()==0) return Qt::AlignCenter;
+
+ if(index.row() < 0 || index.row() >= items.size()) return QVariant();
+ if(index.column() < 0 || index.column()>2) return QVariant();
+ if(role == Qt::DisplayRole) {
+
+ switch(index.column()) {
+ case 0:
+ return items[index.row()].virtualDir;
+ case 1:
+ return items[index.row()].realDir;
+ case 2:
+ if(items[index.row()].shareSize==-1) return "pending";
+ return items[index.row()].shareSize;
+ }
+ }
+ return QVariant();
+}
+
+QVariant ShareItemModel::headerData ( int section, Qt::Orientation orientation, int role ) const
+{
+ if (role != Qt::DisplayRole)
+ return QVariant();
+
+ if(items.size()==0) return QVariant();
+
+ if(orientation==Qt::Horizontal) {
+ if(section==0) return "Name";
+ if(section==1) return "Directory";
+ if(section==2) return "Size";
+ }
+ return QVariant();
+}
+
+void ShareItemModel::add(const QString& name,const QString& dir)
+{
+ items.push_back(ShareItem(name,dir,-1));
+ emit layoutChanged();
+}
+
+void ShareItemModel::remove(int row)
+{
+ vector<ShareItem>::iterator it = items.begin();
+ while(row--) it++;
+ items.erase(it);
+ emit layoutChanged();
+}
+
+void ShareItemModel::clear( )
+{
+ items.clear();
+}
+
+bool ShareItemModel::dropMimeData( const QMimeData * data, Qt::DropAction /*action*/, int /*row*/, int /*column*/, const QModelIndex & /*parent*/ )
+{
+ if(data->hasUrls()) {
+ QList<QUrl> urls = data->urls();
+ for(int i =0;i < urls.size();i++) {
+ QString url = urls[i].toLocalFile();
+ if(!url.isEmpty()) {
+ // Obtain a name for this url
+ //QString name = QInputDialog::getText(0,"Enter a name", "Enter a name for " + url);
+ //if(!name.isEmpty() && !name.isNull())
+ // add(name,url);
+ emit dirDropped(url);
+ }
+ }
+ }
+ return true;
+}
+
+Qt::DropActions ShareItemModel::supportedDropActions( ) const
+{
+ return Qt::ActionMask;
+}
+
+ Qt::ItemFlags ShareItemModel::flags(const QModelIndex &index) const
+{
+ Qt::ItemFlags defaultFlags = QAbstractTableModel::flags(index);
+
+ if (index.isValid())
+ return Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | defaultFlags;
+ else
+ return Qt::ItemIsDropEnabled | defaultFlags;
+}
+
+QStringList ShareItemModel::mimeTypes( ) const
+{
+ QStringList sl;
+ sl+="text/uri-list";
+ return sl;
+}
+
+
diff --git a/ui/shareitemmodel.h b/ui/shareitemmodel.h
new file mode 100644
index 0000000..e44b639
--- /dev/null
+++ b/ui/shareitemmodel.h
@@ -0,0 +1,96 @@
+//
+// C++ Interface: shareitemmodel
+//
+// Description:
+//
+//
+// Author: Rikard Björklind <olof@linux.nu>, (C) 2005
+//
+// Copyright: See COPYING file that comes with this distribution
+//
+//
+#ifndef SHAREITEMMODEL_H
+#define SHAREITEMMODEL_H
+
+#include "global.h"
+#include <vector>
+#include <QAbstractTableModel>
+using namespace std;
+
+//! Represents an item in the ShareItemModel.
+struct ShareItem
+{
+ ShareItem()
+ {}
+ ShareItem(const QString& v,const QString& r,qint64 s)
+ {
+ virtualDir=v;
+ realDir=r;
+ shareSize=s;
+ }
+ QString virtualDir;
+ QString realDir;
+ qint64 shareSize;
+};
+
+/**
+ Data model for shared directories / files.
+ @author Rikard Björklind
+*/
+class ShareItemModel : public QAbstractTableModel
+{
+Q_OBJECT
+public:
+ ShareItemModel();
+
+ virtual ~ShareItemModel()
+ {}
+
+ /*
+ * From QAbstractTableModel
+ */
+ int rowCount ( const QModelIndex& ) const
+ {
+ return items.size()>0?items.size():1;
+ }
+ int columnCount ( const QModelIndex& ) const
+ {
+ return items.size()>0?3:1;
+ }
+ QVariant data ( const QModelIndex & index, int role = Qt::DisplayRole ) const;
+ QVariant headerData ( int section, Qt::Orientation orientation, int role = Qt::DisplayRole ) const;
+ virtual bool dropMimeData ( const QMimeData * data, Qt::DropAction action, int row, int column, const QModelIndex & parent );
+ virtual Qt::DropActions supportedDropActions () const;
+
+ /* Public interface */
+ void add
+ (const ShareItem& item)
+ {
+ items.push_back(item);
+ emit layoutChanged();
+ }
+ void add
+ (const QString& name,const QString& dir);
+ const QString& getName(int row)
+ {
+ return items[row].virtualDir;
+ }
+ const QString& getDir(int row)
+ {
+ return items[row].realDir;
+ }
+ void remove
+ (int row);
+ void clear();
+ virtual Qt::ItemFlags flags(const QModelIndex &index) const;
+ virtual QStringList mimeTypes () const;
+
+ signals:
+ void dirDropped(const QString&);
+
+
+private:
+ vector<ShareItem> items;
+};
+
+#endif
diff --git a/ui/transferlistitemdelegate.cpp b/ui/transferlistitemdelegate.cpp
new file mode 100644
index 0000000..a33d38b
--- /dev/null
+++ b/ui/transferlistitemdelegate.cpp
@@ -0,0 +1,155 @@
+//
+// C++ Implementation: TransferListItemDelegate
+//
+// Description:
+//
+//
+// Author: Arsenij Vodjanov <arsenij@gmail.com>, (C) 2005
+//
+// Copyright: See COPYING file that comes with this distribution
+//
+//
+#include "transferlistmodel.h"
+#include "transferlistitemdelegate.h"
+#include <QPainter>
+#include <QFont>
+#include <QRect>
+#include <QStyleOptionViewItem>
+#include <QModelIndex>
+#include "log.h"
+
+TransferListItemDelegate::TransferListItemDelegate( QObject *parent )
+ : QItemDelegate( parent )
+{
+}
+
+
+TransferListItemDelegate::~TransferListItemDelegate()
+{
+}
+
+
+/*! Reimplemented from QItemDelegate */
+QSize TransferListItemDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
+{
+ if ( index.column() != TransferListModel::COL_PROGRESS_BAR )
+ return QItemDelegate::sizeHint(option, index);
+
+ const QAbstractItemModel *model = index.model();
+ if (model==0) {
+ logger->warn("Warning: model==0 in TransferListItemDelegate::sizeHint()");
+ return QSize(0,0);
+ }
+ QString text = "BLA 00:00:00 BLA"; // just some string for a size hint
+ QVariant value = model->data(index, Qt::FontRole);
+ QFont fnt = value.isValid() ? qvariant_cast<QFont>(value) : option.font;
+ QFontMetrics fontMetrics(fnt);
+ return QSize( fontMetrics.width(text), fontMetrics.lineSpacing()+2 ); // extra pixel at top and bottom
+}
+
+
+
+/*! Reimplemented from QItemDelegate */
+void TransferListItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
+{
+ if ( index.column() != TransferListModel::COL_PROGRESS_BAR )
+ return QItemDelegate::paint(painter, option, index);
+
+ const QAbstractItemModel *model = index.model();
+ if (model==0) {
+ logger->warn("Warning: model==0 in TransferListItemDelegate::paint()");
+ return;
+ }
+
+ // This is stupid. Probably should have reimplemented some item view class instead.
+ QList<QVariant> pdata = model->data(index, Qt::DisplayRole).toList();
+ int secs = pdata.at( TransferListModel::PDATA_SECONDS_LEFT ).toInt();
+ bool isDownload = pdata.at(TransferListModel::PDATA_IS_DOWNLOAD).toBool();
+ qlonglong startPos = pdata.at( TransferListModel::PDATA_START_POS ).toLongLong();
+ qlonglong pos = pdata.at( TransferListModel::PDATA_POS ).toLongLong();
+ qlonglong actual = pdata.at( TransferListModel::PDATA_ACTUAL ).toLongLong();
+ qlonglong fsize = pdata.at( TransferListModel::PDATA_SIZE ).toLongLong();
+
+ QString text = QString("%1% %2:%3:%4").arg((qlonglong)(pos*100/fsize)).arg( (qlonglong)(secs / (60*60))).arg( (qlonglong)(secs / 60)).arg( (qlonglong)(secs % 60));
+
+ // Draw progressbar and text
+
+ QStyleOptionViewItem opt = option;
+ QRect rect = option.rect;
+ QFont font = painter->font();
+ QPen pen = painter->pen();
+ QBrush brush = painter->brush();
+
+ // set font
+ QVariant value = model->data(index, Qt::FontRole);
+ if (value.isValid())
+ opt.font = qvariant_cast<QFont>(value);
+
+ // set text alignment
+ opt.displayAlignment = Qt::AlignCenter;
+
+ // set text color
+ value = model->data(index, Qt::TextColorRole);
+ if (value.isValid() && qvariant_cast<QColor>(value).isValid())
+ opt.palette.setColor(QPalette::Text, qvariant_cast<QColor>(value));
+
+ // draw the background color
+ QPalette::ColorGroup cg = opt.state & QStyle::State_Enabled ? QPalette::Normal : QPalette::Disabled;
+ if (opt.showDecorationSelected && (option.state & QStyle::State_Selected)) {
+ painter->fillRect(rect, opt.palette.brush(cg, QPalette::Highlight));
+ } else {
+ value = model->data(index, Qt::BackgroundColorRole);
+ if (value.isValid() && qvariant_cast<QColor>(value).isValid())
+ painter->fillRect(rect, qvariant_cast<QColor>(value));
+ }
+
+ // draw bar borders
+ painter->setPen( opt.palette.color(cg, QPalette::Mid) );
+ painter->drawRoundRect(rect.adjusted(2,2,-3,-3), 5,5);
+ QRect barRect = rect.adjusted(4,4,-4,-4);
+ // figure out rectangle positions for progress bar
+ int x = barRect.x(), y = barRect.y(), height = barRect.height();
+ qlonglong width = barRect.width();
+ int x1 = x + startPos*width/fsize;
+ int x2 = x + (pos-actual)*width/fsize;
+ int x3 = x + pos*width/fsize;
+ // draw progress bar in different color for upload and download... ugly if-statement.
+ if (isDownload) {
+ painter->fillRect( x, y, x1-x, height, QColor( 16,140,16 ) );
+ painter->fillRect( x1, y, x2-x1, height, QColor( 16,180,16 ) );
+ painter->fillRect( x2, y, x3-x2, height, QColor( 20,220,20 ) );
+ } else {
+ painter->fillRect( x, y, x1-x, height, QColor( 160,0,0 ) );
+ painter->fillRect( x1, y, x2-x1, height, QColor( 190,10,10 ) );
+ painter->fillRect( x2, y, x3-x2, height, QColor( 255,20,20 ) );
+ }
+ painter->fillRect( x3, y, (width-x3)+x+1, height, QColor( 220,220,245 ) );
+
+ // draw text
+ QRect textRect = rect.adjusted(1, 0, -1, -1); // remove width padding ( const int textMargin = 1 in qitemdelegate.cpp )
+ QRect textShadowRect = textRect.adjusted(1,1,1,1);
+ QColor textColor = QColor(220,220,220);
+ painter->setFont(opt.font);
+ painter->setPen( QColor( 255-textColor.red(), 255-textColor.green(), 255-textColor.blue() ) );
+ if ((painter->fontMetrics().width(text)+1) > textRect.width()) {
+ // inverted text color for shadow
+ painter->drawText(textShadowRect, opt.displayAlignment,
+ elidedText(painter->fontMetrics(), textShadowRect.width(), // QAbstractItemDelegate::elidedText()
+ opt.textElideMode, text));
+ painter->setPen(textColor);
+ painter->drawText(textRect, opt.displayAlignment,
+ elidedText(painter->fontMetrics(), textRect.width(), // QAbstractItemDelegate::elidedText()
+ opt.textElideMode, text));
+ } else {
+ painter->drawText(textShadowRect, opt.displayAlignment, text);
+ painter->setPen(textColor);
+ painter->drawText(textRect, opt.displayAlignment, text);
+ }
+
+ painter->setFont(font);
+ painter->setPen(pen);
+ painter->setBrush(brush);
+
+ drawFocus(painter, opt, textRect);
+}
+
diff --git a/ui/transferlistitemdelegate.h b/ui/transferlistitemdelegate.h
new file mode 100644
index 0000000..f0aa88f
--- /dev/null
+++ b/ui/transferlistitemdelegate.h
@@ -0,0 +1,38 @@
+//
+// C++ Interface: TransferListItemDelegate
+//
+// Description:
+//
+//
+// Author: Arsenij Vodjanov <arsenij@gmail.com>, (C) 2005
+//
+// Copyright: See COPYING file that comes with this distribution
+//
+//
+#ifndef TRANSFERLISTITEMDELEGATE_H
+#define TRANSFERLISTITEMDELEGATE_H
+
+#include <QItemDelegate>
+
+/**
+Provides custom rendering in some columns for items in the transfers list
+
+@author Arsenij Vodjanov
+*/
+class TransferListItemDelegate : public QItemDelegate
+{
+
+Q_OBJECT
+
+public:
+ TransferListItemDelegate( QObject *parent = 0 );
+ ~TransferListItemDelegate();
+
+ /*! Reimplemented from QItemDelegate */
+ void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const;
+
+ /*! Reimplemented from QItemDelegate */
+ QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const;
+};
+
+#endif
diff --git a/ui/transferlistmodel.cpp b/ui/transferlistmodel.cpp
new file mode 100644
index 0000000..45665b1
--- /dev/null
+++ b/ui/transferlistmodel.cpp
@@ -0,0 +1,158 @@
+//
+// C++ Implementation: transferlistmodel
+//
+// Description:
+//
+//
+// Author: Rikard Björklind <olof@linux.nu>, (C) 2005
+//
+// Copyright: See COPYING file that comes with this distribution
+//
+//
+#include <QtCore>
+#include "transferlistmodel.h"
+#include <iostream>
+#include "util.h"
+#include "log.h"
+using namespace std;
+
+QVariant TransferListModel::data ( const QModelIndex & index, int role ) const
+{
+ if(!index.isValid()) return QVariant();
+ if(index.row() < 0 || index.row() >= transfers.size()) return QVariant();
+ if(index.column() < 0 || index.column() >= NUM_COLUMNS) return QVariant();
+ if(role == Qt::DisplayRole) {
+ int i = index.row();
+ switch(index.column()) {
+ /*
+ case 0: return transfers[i].pos;
+ case 1: return transfers[i].startpos;
+ case 2: return transfers[i].actual;
+ case 3: return transfers[i].size;
+ case 4: return transfers[i].averageSpeed;
+ case 5: return transfers[i].secondsLeft;
+ case 6: return transfers[i].bytesLeft;
+ case 7: return transfers[i].filename;
+ case 8: return transfers[i].localfilename;
+ */
+ case COL_USER: return transfers[i].userid;
+ case COL_FILE_NAME: return transfers[i].filename;
+ case COL_SIZE_LEFT: return Util::bytesToStr(transfers[i].bytesLeft);
+ case COL_SIZE_TOTAL: return Util::bytesToStr(transfers[i].size);
+ case COL_AVG_SPEED: return Util::bytesToStr(transfers[i].averageSpeed);
+ case COL_STATUS: return transfers[i].status;
+ case COL_PROGRESS_BAR: {
+ QList<QVariant> pdata;
+ // Data position in the QList must match its index in the eProgressData enum.
+ // This silly-looking loop populates the QList in the correct order.
+ for (int ii=0; ii<PDATA_MAX_INDEX; ii++) {
+ switch (ii) {
+ case PDATA_IS_DOWNLOAD:
+ pdata.append( transfers[i].type == FileTransfer::DOWNLOAD ); break;
+ case PDATA_START_POS:
+ pdata.append( static_cast<qlonglong>(transfers[i].startpos) ); break;
+ case PDATA_POS:
+ pdata.append( static_cast<qlonglong>(transfers[i].pos) ); break;
+ case PDATA_ACTUAL:
+ pdata.append( static_cast<qlonglong>(transfers[i].actual) ); break;
+ case PDATA_SIZE:
+ pdata.append( static_cast<qlonglong>(transfers[i].size) ); break;
+ case PDATA_SECONDS_LEFT:
+ pdata.append( transfers[i].secondsLeft); break;
+ default:
+ break;
+ }
+ }
+ return pdata;
+ }
+ default:
+ break;
+ }
+ }
+ return QVariant();
+}
+
+QVariant TransferListModel::headerData ( int section, Qt::Orientation orientation, int role ) const
+{
+ if (role != Qt::DisplayRole)
+ return QVariant();
+
+ if(orientation==Qt::Horizontal) {
+ switch(section) {
+ case COL_USER: return tr("User");
+ case COL_FILE_NAME: return tr("File");
+ case COL_PROGRESS_BAR: return tr("Progress");
+ case COL_SIZE_LEFT: return tr("Size left");
+ case COL_SIZE_TOTAL: return tr("Total size");
+ case COL_AVG_SPEED: return tr("Avg. Speed");
+ case COL_STATUS: return tr("Status");
+ default:
+ break;
+ }
+ /*
+ if(section==0) return "Position";
+ if(section==1) return "StartPos";
+ if(section==2) return "Actual";
+ if(section==3) return "Size";
+ if(section==4) return "AvgSpeed";
+ if(section==5) return "TimeLeft";
+ if(section==6) return "BytesLeft";
+ if(section==7) return "Filename";
+ if(section==8) return "Localname";
+ */
+ }
+ return QVariant();
+}
+
+void TransferListModel::onTransferStart( const FileTransfer& f )
+{
+ logger->debug(f.toString());
+ if( !transfers.contains(f) ) transfers.append(f);
+ emit layoutChanged();
+}
+
+void TransferListModel::onTransferTick( const QList<FileTransfer>& fl)
+{
+ //logger->debug("TransferListModel::onTransferTick");
+ int minIndex=2000000000;
+ int maxIndex=0;
+ bool newItems = false;
+
+ for(int i=0;i < fl.size();i++) {
+ if( !transfers.contains( fl[i] ) ) {
+ transfers.append(fl[i]);
+ newItems=true;
+ }
+ else {
+ int ind = transfers.indexOf(fl[i]);
+ transfers[ind] = fl[i];
+ if(ind < minIndex) minIndex=ind;
+ if(ind > maxIndex) maxIndex=ind;
+ }
+ }
+ if(newItems) emit layoutChanged();
+ if(maxIndex>=minIndex) emit dataChanged(createIndex(minIndex,0),createIndex(maxIndex,NUM_COLUMNS-1));
+}
+
+void TransferListModel::onTransferComplete( const FileTransfer& f)
+{
+ logger->info(QString("Transfer complete: ") + f.toString());
+ transfers.removeAll( f );
+ emit layoutChanged();
+}
+
+void TransferListModel::onTransferFailed( const FileTransfer& f, const QString& errorMsg)
+{
+ logger->error("Transfer failed: " + errorMsg);
+ logger->info(f.toString());
+ //transfers.removeAll( f );
+
+ // Mark as failed, update status message field.
+ int i = transfers.indexOf( f );
+ if( i != -1 ) {
+ transfers[i].status = errorMsg;
+ }
+
+ emit layoutChanged();
+}
+
diff --git a/ui/transferlistmodel.h b/ui/transferlistmodel.h
new file mode 100644
index 0000000..ff33927
--- /dev/null
+++ b/ui/transferlistmodel.h
@@ -0,0 +1,60 @@
+#ifndef TRANSFER_LIST_MODEL_H__
+#define TRANSFER_LIST_MODEL_H__
+
+
+#include "global.h"
+#include "filetransfer.h"
+
+#include <QAbstractTableModel>
+#include <QList>
+
+
+class TransferListModel : public QAbstractTableModel
+{
+Q_OBJECT
+public:
+ //!< Column indexes
+ enum eTransferColumnHeader {
+ COL_USER = 0, //!< User nick
+ COL_FILE_NAME, //!< stripped path filename
+ COL_PROGRESS_BAR, //!< data() for this column returns QVariant( QList<QVariant> )
+ COL_SIZE_LEFT,
+ COL_SIZE_TOTAL,
+ COL_AVG_SPEED,
+ COL_STATUS,
+ NUM_COLUMNS
+ };
+
+ //! Index into QList<QVariant> returned by data() for COL_PROGRESS_BAR
+ enum eProgressData {
+ PDATA_IS_DOWNLOAD, //!< bool, true if this transfer is a download
+ PDATA_START_POS, //!< qlonglong, start position
+ PDATA_POS, //!< qlonglong, write position in file
+ PDATA_ACTUAL, //!< qlonglong, bytes transferred this sessions
+ PDATA_SIZE, //!< qlonglong, target size
+ PDATA_SECONDS_LEFT, //!< int, estimated time left, in seconds
+ PDATA_MAX_INDEX
+ };
+
+ TransferListModel() {}
+ virtual ~TransferListModel() {}
+
+ int rowCount ( const QModelIndex& ) const {return transfers.size();}
+ int columnCount ( const QModelIndex& ) const {return NUM_COLUMNS;}
+ QVariant data ( const QModelIndex & index, int role = Qt::DisplayRole ) const;
+ QVariant headerData ( int section, Qt::Orientation orientation, int role = Qt::DisplayRole ) const;
+ FileTransfer data(int row) {Q_ASSERT(row>=0 && row < transfers.size());return transfers[row];}
+
+
+public slots:
+ void onTransferStart( const FileTransfer& );
+ void onTransferTick( const QList<FileTransfer>& );
+ void onTransferComplete( const FileTransfer& );
+ void onTransferFailed( const FileTransfer&,const QString& );
+
+private:
+ QList<FileTransfer> transfers;
+};
+
+#endif
+
diff --git a/ui/ui.pro b/ui/ui.pro
new file mode 100644
index 0000000..958d6ff
--- /dev/null
+++ b/ui/ui.pro
@@ -0,0 +1,82 @@
+# File generated by kdevelop's qmake manager.
+# -------------------------------------------
+# Subdir relative project main directory: .
+# Target is an application:
+
+FORMS += createfavourite.ui \
+ favouritehublist.ui \
+ hub.ui \
+ mainwindow.ui \
+ publichublist.ui \
+ search.ui \
+ searchdock.ui \
+ settings.ui \
+ connectdialog.ui \
+ userfilelisting.ui
+IDLS += settings.ui
+HEADERS += mainwindow.h \
+ backendconnection.h \
+ filelog.h \
+ settingsdialog.h \
+ publichubswidget.h \
+ rpctypes.h \
+ commandhandlers.h \
+ session.h \
+ sessionmanager.h \
+ userlistmodel.h \
+ user.h \
+ blockallocator.h \
+ shareitemmodel.h \
+ searchwidget.h \
+ searchmanager.h \
+ searchentry.h \
+ connectdialog.h \
+ searchtablemodel.h \
+ queueitem.h \
+ queuemodel.h \
+ filetransfer.h \
+ exceptions.h \
+ globalusermodel.h \
+ finisheditem.h \
+ finishedmodel.h \
+ transferlistitemdelegate.h \
+ transferlistmodel.h \
+ favouritehubswidget.h \
+ userfilemodel.h \
+ userfiledialog.h \
+ nicetreeview.h \
+ createfavouritedialog.h
+SOURCES += main.cpp \
+ mainwindow.cpp \
+ backendconnection.cpp \
+ filelog.cpp \
+ settingsdialog.cpp \
+ publichubswidget.cpp \
+ commandhandlers.cpp \
+ session.cpp \
+ sessionmanager.cpp \
+ userlistmodel.cpp \
+ shareitemmodel.cpp \
+ searchwidget.cpp \
+ searchmanager.cpp \
+ searchtablemodel.cpp \
+ queuemodel.cpp \
+ globalusermodel.cpp \
+ finishedmodel.cpp \
+ transferlistitemdelegate.cpp \
+ transferlistmodel.cpp \
+ favouritehubswidget.cpp \
+ connectdialog.cpp \
+ userfilemodel.cpp \
+ nicetreeview.cpp \
+ createfavouritedialog.cpp \
+ userfiledialog.cpp
+TEMPLATE = app
+TARGET +=
+DEPENDPATH += .
+CONFIG += qt release
+INCPATH += . ../rpcdriver ../ ../backend /usr/include
+RESOURCES += res.qrc
+unix{
+ LIBS = -L../rpcdriver -L/usr/local/lib -lrpc -lboost_thread -lboost_program_options
+}
diff --git a/ui/user.h b/ui/user.h
new file mode 100644
index 0000000..a7e770c
--- /dev/null
+++ b/ui/user.h
@@ -0,0 +1,47 @@
+#ifndef USER_H__
+#define USER_H__
+
+#include "global.h"
+#include "blockallocator.h"
+
+#include <QString>
+using namespace std;
+
+class User : public BlockAllocator<User>
+{
+
+ enum {
+ OP_BIT,
+ ONLINE_BIT,
+ DCPLUSPLUS_BIT,
+ PASSIVE_BIT,
+ QUIT_HUB_BIT,
+ HIDDEN_BIT,
+ HUB_BIT,
+ BOT_BIT
+ };
+ public:
+ enum {
+ OP = 1<<OP_BIT,
+ ONLINE = 1<<ONLINE_BIT,
+ DCPLUSPLUS = 1<<DCPLUSPLUS_BIT,
+ PASSIVE = 1<<PASSIVE_BIT,
+ QUIT_HUB = 1<<QUIT_HUB_BIT,
+ HIDDEN = 1<<HIDDEN_BIT,
+ HUB = 1<<HUB_BIT,
+ BOT = 1<<BOT_BIT
+ };
+
+ int id;
+ QString nick;
+ int flags;
+ QString email;
+ QString description;
+ QString connection;
+ QString tag;
+ qint64 shared;
+ int numSlots;
+ int index;
+};
+
+#endif
diff --git a/ui/userfiledialog.cpp b/ui/userfiledialog.cpp
new file mode 100644
index 0000000..568748f
--- /dev/null
+++ b/ui/userfiledialog.cpp
@@ -0,0 +1,148 @@
+/*
+ * userfiledialog.cpp
+ * ui
+ *
+ * Created by Mikael Gransell on 4/26/06.
+ * Copyright 2006 __MyCompanyName__. All rights reserved.
+ *
+ */
+
+#include "log.h"
+#include "userfiledialog.h"
+#include "globalusermodel.h"
+
+
+UserFileDialog::UserFileDialog( const UserFileModelPtr& mdl,
+ BackendConnectionPtr backendConn,
+ boost::shared_ptr<GlobalUserModel> userModel,
+ QWidget* parent )
+: QDialog(parent),
+ contextMenu(new QMenu(this)),
+ backendConnection(backendConn),
+ model( mdl )
+{
+ ui.setupUi(this);
+ ui.treeView->setModel( model.get() );
+
+ setWindowTitle(userModel->getUser(model->getUserId())->nick);
+
+ createMenu();
+}
+
+void UserFileDialog::on_treeView_customContextMenuRequested( const QPoint& pos )
+{
+ // Show the menu
+ contextMenu->popup( ui.treeView->mapToGlobal(pos) );
+}
+
+void UserFileDialog::on_treeView_doubleClicked()
+{
+ logger->debug( "Double click download" );
+ // Try to get all the selected indexes and issue a download for each one.
+ QModelIndexList selected = ui.treeView->selectionModel()->selectedIndexes();
+ for( int i = 0; i < selected.size() - 1; i++ ) {
+ QModelIndex index = selected[i];
+ if( !model->getItem(index)->isDir() ) {
+ download( index );
+ }
+ }
+}
+
+void UserFileDialog::onDownload()
+{
+ logger->debug( "Download triggered" );
+ // Try to get all the selected indexes and issue a download for each one.
+ QModelIndexList selected = ui.treeView->selectionModel()->selectedIndexes();
+ for( int i = 0; i < selected.size() - 1; i++ ) {
+ download( selected[i] );
+ }
+}
+
+void UserFileDialog::onDownloadTo()
+{
+ logger->debug( "DownloadTo triggered" );
+ QString dir = QFileDialog::getExistingDirectory( this, tr("Choose a download directory") );
+ if( !dir.isEmpty() ){
+ dir += "/";
+ // Try to get all the selected indexes and issue a download for each one.
+ QModelIndexList selected = ui.treeView->selectionModel()->selectedIndexes();
+ for( int i = 0; i < selected.size() - 1; i++ ) {
+ downloadTo( selected[i], dir );
+ }
+ }
+}
+
+
+void UserFileDialog::download( const QModelIndex& index )
+{
+ try {
+ // Throws std::out_of_range
+ UserFileModel::TreeItem* item = model->getItem( index );
+
+ if ( item ) {
+ QString file = formatFileName( item );
+
+ int64 size = item->isDir() ? -1 : item->getSize();
+ QString tth = item->getTTH();
+
+ logger->debug( "Downloading: " + file + " with size " + QString::number(size) + " and tth:" + tth );
+
+ backendConnection->downloadFile( model->getUserId(),
+ file,
+ size,
+ tth );
+ }
+ }
+ catch(const std::out_of_range& e) {
+ logger->error("Bad index when trying to download file\n");
+ }
+}
+
+void UserFileDialog::downloadTo( const QModelIndex& index, const QString& path )
+{
+ try {
+ // Throws std::out_of_range
+ UserFileModel::TreeItem* item = model->getItem( index );
+
+ if ( item ) {
+ QString file = formatFileName( item );
+
+ int64 size = item->isDir() ? -1 : item->getSize();
+ QString tth = item->getTTH();
+
+ logger->debug( "Downloading: " + file + " with size " + QString::number(size) + " and tth:" + tth + " to " + path );
+
+ backendConnection->downloadFileTo( model->getUserId(),
+ file,
+ size,
+ tth,
+ path );
+ }
+ }
+ catch(const std::out_of_range& e) {
+ logger->error("Bad index when trying to download file\n");
+ }
+}
+
+QString UserFileDialog::formatFileName( const UserFileModel::TreeItem* item ) const
+{
+ QString file = item->getFullName();
+ // Strip the first part of the path since this is the user name
+ file = file.section( '/', 2 );
+
+ // If this is a directory we need to add the final /
+ if ( item->isDir() ) {
+ file += '/';
+ }
+ return file;
+}
+
+void UserFileDialog::createMenu()
+{
+ QAction* downloadAct = contextMenu->addAction( tr("Download") );
+ connect(downloadAct, SIGNAL(triggered()), this, SLOT(onDownload()));
+
+ QAction* downloadToAct = contextMenu->addAction( tr("Download to...") );
+ connect(downloadToAct, SIGNAL(triggered()), this, SLOT(onDownloadTo()));
+}
+
diff --git a/ui/userfiledialog.h b/ui/userfiledialog.h
new file mode 100644
index 0000000..9fa001f
--- /dev/null
+++ b/ui/userfiledialog.h
@@ -0,0 +1,55 @@
+/*
+ * userfiledialog.h
+ * ui
+ *
+ * Created by Mikael Gransell on 4/13/06.
+ * Copyright 2006 __MyCompanyName__. All rights reserved.
+ *
+ */
+
+
+#ifndef USER_FILE_DIALOG_H
+#define USER_FILE_DIALOG_H
+
+#include <boost/shared_ptr.hpp>
+
+#include <QDialog>
+
+#include "backendconnection.h"
+#include "userfilemodel.h"
+#include "ui_userfilelisting.h"
+
+class GlobalUserModel;
+
+class UserFileDialog : public QDialog
+{
+ Q_OBJECT
+public:
+ UserFileDialog( const UserFileModelPtr& mdl,
+ BackendConnectionPtr backendConn,
+ boost::shared_ptr<GlobalUserModel> userModel,
+ QWidget* parent = NULL );
+ ~UserFileDialog() { }
+
+public slots:
+ void on_treeView_customContextMenuRequested( const QPoint& pos );
+ void on_treeView_doubleClicked();
+ void onDownload();
+ void onDownloadTo();
+
+private:
+ void download( const QModelIndex& index );
+ void downloadTo( const QModelIndex& index, const QString& path );
+
+ QString formatFileName( const UserFileModel::TreeItem* item ) const;
+
+ void createMenu();
+
+ boost::shared_ptr<QMenu> contextMenu;
+
+ Ui::UserFileDialog ui;
+ BackendConnectionPtr backendConnection;
+ UserFileModelPtr model;
+};
+
+#endif
diff --git a/ui/userfilelisting.ui b/ui/userfilelisting.ui
new file mode 100644
index 0000000..a353985
--- /dev/null
+++ b/ui/userfilelisting.ui
@@ -0,0 +1,75 @@
+<ui version="4.0" >
+ <author></author>
+ <comment></comment>
+ <exportmacro></exportmacro>
+ <class>UserFileDialog</class>
+ <widget class="QDialog" name="UserFileDialog" >
+ <property name="geometry" >
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>621</width>
+ <height>318</height>
+ </rect>
+ </property>
+ <property name="contextMenuPolicy" >
+ <enum>Qt::CustomContextMenu</enum>
+ </property>
+ <property name="windowTitle" >
+ <string>Dialog</string>
+ </property>
+ <layout class="QVBoxLayout" >
+ <property name="margin" >
+ <number>9</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item>
+ <widget class="QTreeView" name="treeView" >
+ <property name="contextMenuPolicy" >
+ <enum>Qt::CustomContextMenu</enum>
+ </property>
+ <property name="selectionMode" >
+ <enum>QAbstractItemView::ExtendedSelection</enum>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="findGroupBox" >
+ <property name="title" >
+ <string>Search</string>
+ </property>
+ <layout class="QHBoxLayout" >
+ <property name="margin" >
+ <number>9</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item>
+ <widget class="QLineEdit" name="findEdit" />
+ </item>
+ <item>
+ <widget class="QPushButton" name="findButton" >
+ <property name="text" >
+ <string>Find</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="findNextButton" >
+ <property name="text" >
+ <string>Next</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <pixmapfunction></pixmapfunction>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/ui/userfilemodel.cpp b/ui/userfilemodel.cpp
new file mode 100644
index 0000000..fea5590
--- /dev/null
+++ b/ui/userfilemodel.cpp
@@ -0,0 +1,235 @@
+//
+// C++ Implementation: UserFileModel
+//
+// Description:
+//
+//
+// Author: Mikael Gransell <mikael.gransell@bredband.net>, (C) 2005
+//
+// Copyright: See COPYING file that comes with this distribution
+//
+//
+
+#include "userfilemodel.h"
+
+#include <QFileIconProvider>
+
+#include <stdexcept>
+
+QVariant UserFileModel::data( const QModelIndex &index, int role ) const
+{
+ QVariant ret;
+
+ // If we access an item that is out of range
+ if (!index.isValid())
+ return QVariant();
+
+ // Get the item this concerns
+ TreeItem *item = static_cast<TreeItem*>(index.internalPointer());
+
+ // Check if it wants the text that should be displayed or the decorator.
+ switch (role) {
+ case Qt::DisplayRole:
+
+ ret = item->data(index.column());
+ break;
+
+ case Qt::DecorationRole:
+
+ // only display icon for the first column
+ if ( index.column() == 0 ) {
+
+ // Get an icon from the icon provider
+ ret = m_pIconProvider->icon( item->isDir() ? QFileIconProvider::Folder : QFileIconProvider::File );
+ }
+
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+
+Qt::ItemFlags UserFileModel::flags( const QModelIndex &index ) const
+{
+ if (!index.isValid())
+ return Qt::ItemIsEnabled;
+
+ return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
+}
+
+
+QVariant UserFileModel::headerData( int section, Qt::Orientation orientation, int role ) const
+{
+ if (role != Qt::DisplayRole)
+ return QVariant();
+
+ if(orientation==Qt::Horizontal) {
+ switch( section ) {
+ case TreeItem::FILE_COLUMN:
+ return tr("File Name");
+
+ case TreeItem::SIZE_COLUMN:
+ return tr("Size");
+
+ case TreeItem::TTH_COLUMN:
+ return tr("TTH");
+ }
+ }
+ return QVariant();
+}
+
+QModelIndex UserFileModel::index( int row, int column, const QModelIndex &parent ) const
+{
+ TreeItem *parentItem;
+
+ if (!parent.isValid())
+ parentItem = root;
+ else
+ parentItem = static_cast<TreeItem*>(parent.internalPointer());
+
+ TreeItem *childItem = parentItem->child(row);
+ if (childItem)
+ return createIndex(row, column, childItem);
+ else
+ return QModelIndex();
+}
+
+
+QModelIndex UserFileModel::parent( const QModelIndex &index ) const
+{
+ if (!index.isValid())
+ return QModelIndex();
+
+ TreeItem *childItem = static_cast<TreeItem*>(index.internalPointer());
+ TreeItem *parentItem = childItem->parent();
+
+ if (parentItem == root)
+ return QModelIndex();
+
+ return createIndex(parentItem->row(), 0, parentItem);
+}
+
+
+int UserFileModel::rowCount( const QModelIndex &parent ) const
+{
+ TreeItem *parentItem;
+
+ if (!parent.isValid())
+ parentItem = root;
+ else
+ parentItem = static_cast<TreeItem*>(parent.internalPointer());
+
+ return parentItem->childCount();
+}
+
+
+int UserFileModel::columnCount(const QModelIndex &parent) const
+{
+ if (parent.isValid())
+ return static_cast<TreeItem*>(parent.internalPointer())->columnCount();
+ else
+ return root->columnCount();
+}
+
+UserFileModel::TreeItem* UserFileModel::getItem( const QModelIndex& index ) const
+{
+ if ( !index.isValid() )
+ throw std::out_of_range("Bad QModelIndex");
+
+ TreeItem *item = static_cast<TreeItem*>(index.internalPointer());
+
+ return item;
+}
+
+int UserFileModel::getUserId() const
+{
+ return userId;
+}
+
+bool UserFileModel::setTree( const QString& tree )
+{
+ root = new TreeItem( "", NULL );
+ // Start parsing the tree.s
+ handleSubTree( tree, root );
+
+ // Calculate the size of each node.
+ root->calculateSize();
+
+ emit layoutChanged();
+
+ return true;
+}
+
+
+void UserFileModel::handleSubTree( const QString& tree, TreeItem* parent ) const
+{
+ // Check if this is a directory
+ int start = 0;
+ int endDir = -1;
+
+ while ( (start = tree.indexOf( '/', endDir + 1 )) != -1 ) {
+ // Retrieve directory name
+ QString dirName = tree.mid( endDir + 1, start - (endDir + 1) );
+
+ TreeItem* item = new TreeItem( dirName, parent );
+ //logger->info( "Adding directory " + dirName );
+
+ // Find end of this dir
+ endDir = start + 1;
+ int level = 1;
+ while ( level ) {
+ QChar c = tree[endDir];
+ // Go down a level if we find a new dir
+ if( c == '/' ) level++;
+
+ // Go up if we find the end of a dir
+ if( c == '\\' ) level--;
+
+ // Step forward
+ endDir++;
+ }
+
+ QString dir = tree.mid( start + 1, endDir - start - 1 );
+ handleSubTree( dir, item );
+ }
+
+ // Find the end of this directory
+ int endFiles = tree.indexOf( '\\', endDir + 1 );
+ // Get the files string
+ QString files = tree.mid( endDir + 1, endFiles );
+ //logger->info( "files: " + files );
+ // Split all files and go through them
+ QStringList fileList = files.split( '*' );
+ for ( int i = 0; i < fileList.size(); i++ ) {
+ addFile( fileList[i], parent );
+ }
+}
+
+void UserFileModel::addFile( const QString& file, TreeItem* parent ) const
+{
+ // Split the file with sep :
+ QStringList lst = file.split( '|' );
+
+ if ( lst.size() >= 2 ) {
+ // Pos 1 is the file name
+ QString name = lst[0];
+
+ // Pos 2 is the size
+ QString size = lst[1];
+
+ // Pos 3 is TTH
+ QString tth = lst[2];
+
+ // Create item
+ TreeItem* item = new TreeItem( name, parent );
+ item->setSize( size.toLongLong() );
+ item->setTTH( tth );
+
+ //logger->info( "Added file: " + name + " with size: " + size + " and tth: " + tth );
+ }
+ else {
+ //logger->error( "Bad file string format: " + file );
+ }
+}
diff --git a/ui/userfilemodel.h b/ui/userfilemodel.h
new file mode 100644
index 0000000..767c146
--- /dev/null
+++ b/ui/userfilemodel.h
@@ -0,0 +1,315 @@
+//
+// C++ Interface: UserFileModel
+//
+// Description:
+//
+//
+// Author: Mikael Gransell <mikael.gransell@bredband.net>, (C) 2005
+//
+// Copyright: See COPYING file that comes with this distribution
+//
+//
+
+#ifndef USER_FILE_TREE_MODEL_H_
+#define USER_FILE_TREE_MODEL_H_
+
+#include <boost/shared_ptr.hpp>
+
+#include <QtGui>
+#include <QAbstractItemModel>
+#include <QPointer>
+
+#include "global.h"
+#include "util.h"
+
+class QFileIconProvider;
+
+class UserFileModel : public QAbstractItemModel {
+ Q_OBJECT
+public:
+ class TreeItem {
+ public:
+
+ /**
+ * The columns that are available fo the items in the list.
+ */
+ enum eUserTreeColumns
+ {
+ FILE_COLUMN = 0,
+ SIZE_COLUMN,
+ TTH_COLUMN
+ };
+
+ /**
+ * Ctor. Creates a node with n as name.
+ * @param n This is the name of the item.
+ * @param parent Pointer to this items parent node.
+ */
+ TreeItem(const QString& n, TreeItem *parent = 0) : parentItem( parent ), name( n ), size( 0 ), tth("") {
+ if ( parent ) {
+ parent->appendChild( this );
+ }
+ }
+
+ /**
+ * Ctor. Creates a node with an empty name
+ * @param parent Pointer to this items parent node.
+ */
+ TreeItem( TreeItem* parent = 0 ) : parentItem( parent ) {
+ if ( parent ) {
+ parent->appendChild( this );
+ }
+ }
+
+ /**
+ * Dtor.
+ * Remove all children.
+ */
+ ~TreeItem() {
+ qDeleteAll( childItems );
+ }
+
+ /**
+ * Adds a child to the list.
+ * @param child The new child.
+ */
+ void appendChild(TreeItem *child) {
+ childItems.append( child );
+ }
+
+ /**
+ * Get the child at row row, i.e. at positioin row in the child list.
+ * @param row The row of the child that we want.
+ * @return Pointer to the child.
+ */
+ TreeItem *child(int row) {
+ return childItems.value( row );
+ }
+
+ /**
+ * Get the amount of children.
+ * @return The number of children.
+ */
+ int childCount() const {
+ return childItems.count();
+ }
+
+ /**
+ * Get the number of columns for this item.
+ * @return Number of columns.
+ */
+ int columnCount() const {
+ return 2;
+ }
+
+ /**
+ * Get the data that corresponds to the column.
+ * @param column The column we want data for.
+ */
+ QVariant data(int column) const {
+ QVariant ret;
+ switch( column ) {
+ case FILE_COLUMN:
+ ret = name;
+ break;
+
+ case SIZE_COLUMN:
+ ret = Util::bytesToStr( size );
+ break;
+
+ case TTH_COLUMN:
+ ret = tth;
+ break;
+
+ default:
+ ret = "";
+ }
+
+ return ret;
+ }
+
+ /**
+ * The row of this item.
+ */
+ int row() const {
+ if( !parentItem ) {
+ return 0;
+ }
+ return parentItem->childItems.indexOf( const_cast<TreeItem*>(this) );
+ }
+
+ /**
+ * Get the parent of this item.
+ * @return Pointer to the parent.
+ */
+ TreeItem *parent() {
+ return parentItem;
+ }
+
+ /**
+ * Return the calculated size of this item.
+ * @return The size.
+ */
+ qint64 getSize() const {
+ return size;
+ }
+
+ /**
+ * Return name of the item
+ */
+ const QString& getName() const {
+ return name;
+ }
+
+ /**
+ * Return the full name of the item, i.e. the entire path
+ */
+ QString getFullName() const {
+ // If we are not the root item we return the full path of our parent with our name appended.
+ return parentItem ? parentItem->getFullName() + "/" + name : "";
+ }
+
+ /**
+ * Return tth for this item.
+ */
+ const QString& getTTH() const
+ {
+ return tth;
+ }
+
+ /**
+ * If this is a directory or not
+ * @return true if this is a directory
+ */
+ bool isDir() const {
+ return childCount() != 0;
+ }
+
+ /**
+ * Calculate the size of this node. This is either
+ * based on the size that was set while the tree was
+ * parsed, i.e. this item is a leaf, or on the sum
+ * of all of this items child nodes, i.e. this item
+ * is a directory.
+ * @return The size that we have calculated.
+ */
+ qint64 calculateSize() {
+ // The size is already calculated, i.e. we are a leaf or this method has already been called.
+ if( !size ) {
+ // Calculate the size of al children and sum them up.
+ for( int i = 0; i < childItems.size(); i++ ) {
+ size += childItems[i]->calculateSize();
+ }
+ }
+ return size;
+ }
+
+ void setSize( qint64 s ) {
+ size = s;
+ }
+
+ void setName( const QString& n ) {
+ name = n;
+ }
+
+ void setTTH( const QString& t ) {
+ tth = t;
+ }
+
+ private:
+ /**
+ * List of child items.
+ */
+ QList<TreeItem*> childItems;
+
+ /**
+ * Pointer to our parent.
+ */
+ TreeItem *parentItem;
+
+ /**
+ * The name of the item. This will be either a directory name or a file name.
+ */
+ QString name;
+
+ /**
+ * Size of the item. This will either be the size of a file, or if
+ * the item is a directory, the size of all its chlidren.
+ */
+ qint64 size;
+
+ /**
+ TTH for the item
+ */
+ QString tth;
+ };
+
+
+ UserFileModel( int uid, const QString& fileTree ) :
+ userId( uid )
+ {
+ setTree( fileTree );
+
+ // Create an icon provider that will suply the list wiht icons
+ m_pIconProvider = new QFileIconProvider();
+ }
+
+ virtual ~UserFileModel()
+ {
+ // Delete the root item. This should delete all childrem too
+ delete root;
+
+ // Delete icon provider
+ delete m_pIconProvider;
+ }
+ virtual QVariant data( const QModelIndex &index, int role ) const;
+
+ virtual Qt::ItemFlags flags( const QModelIndex &index ) const;
+
+ virtual QVariant headerData( int section, Qt::Orientation orientation, int role = Qt::DisplayRole ) const;
+
+ virtual QModelIndex index( int row, int column, const QModelIndex &parent = QModelIndex() ) const;
+
+ virtual QModelIndex parent( const QModelIndex &index ) const;
+
+ virtual int rowCount( const QModelIndex &parent = QModelIndex() ) const;
+
+ virtual int columnCount( const QModelIndex &parent = QModelIndex() ) const;
+
+ TreeItem* getItem( const QModelIndex &index ) const;
+
+ int getUserId() const;
+
+public slots:
+ /**
+ * This is the big as method that builds the tree based on the string that we get from the backend.
+ * This string is based on the Newick format of string representation of a tree. This is very much
+ * like Lisp syntax.
+ * @param tree The string representation of the tree.
+ * @param user Name of the user, this will act as the root of the tree.
+ * @return true if everything wen't as expected.
+ */
+ bool setTree( const QString& tree );
+
+protected:
+ void handleSubTree( const QString& tree, TreeItem* parent ) const;
+ void addFile( const QString& file, TreeItem* parent ) const;
+
+private:
+ /**
+ * Root of the tree. This node should be the user.
+ */
+ TreeItem* root;
+
+ /**
+ * Provides icons for the items in the list.
+ */
+ QFileIconProvider* m_pIconProvider;
+
+ /// Holds the id of the user that this model is associated with
+ const int userId;
+};
+
+typedef boost::shared_ptr<UserFileModel> UserFileModelPtr;
+
+#endif
diff --git a/ui/userlistmodel.cpp b/ui/userlistmodel.cpp
new file mode 100644
index 0000000..8c96aa8
--- /dev/null
+++ b/ui/userlistmodel.cpp
@@ -0,0 +1,141 @@
+//
+// C++ Implementation: UserList
+//
+// Description:
+//
+//
+// Author: Rikard Björklind <olof@linux.nu>, (C) 2005
+//
+// Copyright: See COPYING file that comes with this distribution
+//
+//
+#include "userlistmodel.h"
+#include "util.h"
+
+template <class T, size_t blocksPerBatch, size_t blockAlignment>
+typename BlockAllocator<T,blocksPerBatch,blockAlignment>::BlockStore BlockAllocator<T,blocksPerBatch,blockAlignment>::s_Store;
+
+QVariant UserListModel::data( const QModelIndex & index, int role ) const
+{
+ if(index.row() < 0 || index.row() >= users.size()) return QVariant();
+ //User* u = userIndexMap.value(index.row());
+ User *u = users[index.row()];
+ if(u==0) return QVariant();
+
+ if(role==Qt::DisplayRole) {
+ switch(index.column()) {
+ case 0:
+ return u->nick;
+ case 1:
+ return Util::bytesToStr(u->shared);
+ case 2:
+ return u->description;
+ default:
+ Q_ASSERT_X(false,"data","wrong column");
+ }
+ } else if(role==Qt::DecorationRole) {
+ if(index.column()==0) {
+ bool op = u->flags & User::OP;
+ bool passive = u->flags & User::PASSIVE;
+ bool dcpp = u->flags & User::DCPLUSPLUS;
+ if(op) {
+ if(passive) {
+ if(dcpp) return iconDcppPassiveOp;
+ else return iconPassiveOp;
+ } else if(dcpp) return iconDcppOp;
+ else return iconOp;
+ } else {
+ if(passive) { if(dcpp) return iconDcppPassive; else return iconPassive;}
+ else if(dcpp) return iconDcpp; else return iconNormal;
+ }
+ return iconNormal;
+ }
+
+ }
+ return QVariant();
+}
+
+QVariant UserListModel::headerData( int section, Qt::Orientation orientation, int role ) const
+{
+ if( orientation==Qt::Horizontal && role==Qt::DisplayRole )
+ {
+ switch(section) {
+ case 0: return tr("Nick");
+ case 1: return tr("Shared");
+ case 2: return tr("Description");
+ }
+ }
+ return QVariant();
+}
+
+void UserListModel::setUser( User *u )
+{
+ // See if the userid is already in the list. In that case, just copy the new values.
+ if(userIdMap.contains(u->id)) {
+ User* oldUser = userIdMap[u->id];
+ u->index = oldUser->index;
+ (*oldUser) = *u;
+ delete u;
+ emit dataChanged(createIndex(oldUser->index,0),createIndex(oldUser->index,2));
+ } else { // Not in list - put it in.
+ userIdMap[u->id] = u;
+ //int index = userIndexMap.size();
+ int index = users.size();
+ u->index = index;
+ //userIndexMap[index] = u;
+ users.append(u);
+ if(updateSignals) emit layoutChanged();
+ }
+}
+
+void UserListModel::removeUser( int userId )
+{
+ User* u = userIdMap.take(userId);
+ if(u) {
+ //userIndexMap.take(u->index);
+
+ users.removeAt(u->index);
+ for(int i=u->index;i<users.size();i++) users[i]->index=i;
+
+
+ delete u;
+ if(updateSignals) emit layoutChanged();
+ }
+}
+
+bool userNickLessThan(User* a,User*b) {return a->nick < b->nick;}
+bool userShareLessThan(User* a,User*b) {return a->shared < b->shared;}
+bool userDescLessThan(User* a,User*b) {return a->description < b->description;}
+bool userNickG(User* a,User*b) {return a->nick > b->nick;}
+bool userShareG(User* a,User*b) {return a->shared > b->shared;}
+bool userDescG(User* a,User*b) {return a->description > b->description;}
+
+void UserListModel::sort( int col, Qt::SortOrder order )
+{
+ if( order==Qt::AscendingOrder ) {
+ switch(col) {
+ case 0:
+ qSort(users.begin(),users.end(),userNickLessThan);break;
+ case 1:
+ qSort(users.begin(),users.end(),userShareLessThan);break;
+ case 2:
+ qSort(users.begin(),users.end(),userDescLessThan);break;
+ }
+ }
+ else {
+ switch(col) {
+ case 0:
+ qSort(users.begin(),users.end(),userNickG);break;
+ case 1:
+ qSort(users.begin(),users.end(),userShareG);break;
+ case 2:
+ qSort(users.begin(),users.end(),userDescG);break;
+ }
+
+ }
+
+ // Fix indexes
+ for(int i=0;i < users.size();i++) users[i]->index = i;
+
+ if(updateSignals) emit layoutChanged();
+}
diff --git a/ui/userlistmodel.h b/ui/userlistmodel.h
new file mode 100644
index 0000000..6c7c56b
--- /dev/null
+++ b/ui/userlistmodel.h
@@ -0,0 +1,85 @@
+//
+// C++ Interface: UserListModel
+//
+// Description:
+// Defines the UserList data model
+//
+// Author: Rikard Björklind <olof@linux.nu>, (C) 2005.2006
+//
+// Copyright: See COPYING file that comes with this distribution
+//
+//
+#ifndef USER_LIST_H__
+#define USER_LIST_H__
+
+#include <QAbstractTableModel>
+#include <QHash>
+#include <QIcon>
+#include "user.h"
+
+class UserListModel : public QAbstractTableModel
+{
+ Q_OBJECT
+public:
+ UserListModel() : QAbstractTableModel()
+ {
+ iconNormal = QIcon(":/images/user-normal.png");
+ iconOp= QIcon(":/images/user-normal-op.png");
+ iconDcpp= QIcon(":/images/user-dcpp.png");
+ iconDcppOp= QIcon(":/images/user-dcpp-op.png");
+ iconDcppPassive= QIcon(":/images/user-dcpp-passive.png");
+ iconPassive= QIcon(":/images/user-passive.png");
+ iconPassiveOp= QIcon(":/images/user-passive-op.png");
+ iconDcppPassiveOp= QIcon(":/images/user-dcpp-passive-op.png");
+ updateSignals = true;
+ }
+ ~UserListModel()
+ {}
+
+ // QAbstractTableModel methods...
+ int rowCount ( const QModelIndex& ) const
+ {
+ //return userIndexMap.size();
+ return users.size();
+ }
+ int columnCount ( const QModelIndex& ) const
+ {
+ return 3; // Nick, share, description
+ }
+ QVariant data ( const QModelIndex & index, int role = Qt::DisplayRole ) const;
+ QVariant headerData ( int section, Qt::Orientation orientation, int role = Qt::DisplayRole ) const;
+
+ User* getUser(const QModelIndex& i)
+ {
+ return users[i.row()];
+ }
+
+ void setUser(User*);
+ User* getUser(int userId)
+ {
+ return userIdMap.value(userId);
+ }
+ void removeUser(int userId);
+ void sort(int col,Qt::SortOrder order = Qt::AscendingOrder);
+
+ //! Used to turn of emission of the layoutchanhed signal, default true.
+ void enableUpdateSignals(bool yes)
+ {
+ updateSignals=yes;
+ }
+
+ void signalLayoutChanged()
+ {
+ emit layoutChanged();
+ }
+
+protected:
+ bool updateSignals;
+ QHash<int,User*> userIdMap;
+ QList<User*> users;
+ QIcon iconNormal,iconOp,iconDcpp,iconDcppOp,iconDcppPassive,iconPassive,iconPassiveOp,iconDcppPassiveOp;
+};
+
+
+
+#endif
diff --git a/ui/util.h b/ui/util.h
new file mode 100644
index 0000000..77efa7a
--- /dev/null
+++ b/ui/util.h
@@ -0,0 +1,30 @@
+#ifndef UTIL_H__
+#define UTIL_H__
+
+#include "global.h"
+#include <QString>
+#include <QStringList>
+
+class Util
+{
+ public:
+ static QString bytesToStr(qint64 bytes,int prec = 1)
+ {
+ double b = bytes;
+ QStringList sl;
+ sl+="%1 B";
+ sl+="%1 KiB";
+ sl+="%1 MiB";
+ sl+="%1 GiB";
+ sl+="%1 TiB";
+
+ for(int i = 0;i<sl.size()-1;i++)
+ if(b < 1024) return sl[i].arg(b,0,'f',prec); else b/=1024;
+
+ return sl[sl.size()-1].arg(b,0,'f',prec);
+ }
+
+};
+
+
+#endif