summaryrefslogtreecommitdiffstats
path: root/libktorrent
diff options
context:
space:
mode:
authortpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2010-01-20 02:37:40 +0000
committertpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2010-01-20 02:37:40 +0000
commit9ad5c7b5e23b4940e7a3ea3ca3a6fb77e6a8fab0 (patch)
treed088b5210e77d9fa91d954d8550e00e372b47378 /libktorrent
downloadktorrent-9ad5c7b5e23b4940e7a3ea3ca3a6fb77e6a8fab0.tar.gz
ktorrent-9ad5c7b5e23b4940e7a3ea3ca3a6fb77e6a8fab0.zip
Updated to final KDE3 ktorrent release (2.2.6)
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/applications/ktorrent@1077377 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'libktorrent')
-rw-r--r--libktorrent/Makefile.am22
-rw-r--r--libktorrent/datachecker/Makefile.am8
-rw-r--r--libktorrent/datachecker/datachecker.cpp34
-rw-r--r--libktorrent/datachecker/datachecker.h78
-rw-r--r--libktorrent/datachecker/datacheckerlistener.cpp33
-rw-r--r--libktorrent/datachecker/datacheckerlistener.h80
-rw-r--r--libktorrent/datachecker/datacheckerthread.cpp58
-rw-r--r--libktorrent/datachecker/datacheckerthread.h61
-rw-r--r--libktorrent/datachecker/multidatachecker.cpp201
-rw-r--r--libktorrent/datachecker/multidatachecker.h49
-rw-r--r--libktorrent/datachecker/singledatachecker.cpp103
-rw-r--r--libktorrent/datachecker/singledatachecker.h44
-rw-r--r--libktorrent/expandablewidget.cpp179
-rw-r--r--libktorrent/expandablewidget.h89
-rw-r--r--libktorrent/functions.cpp40
-rw-r--r--libktorrent/functions.h35
-rw-r--r--libktorrent/interfaces/Makefile.am15
-rw-r--r--libktorrent/interfaces/chunkdownloadinterface.cpp33
-rw-r--r--libktorrent/interfaces/chunkdownloadinterface.h62
-rw-r--r--libktorrent/interfaces/coreinterface.cpp33
-rw-r--r--libktorrent/interfaces/coreinterface.h258
-rw-r--r--libktorrent/interfaces/exitoperation.cpp47
-rw-r--r--libktorrent/interfaces/exitoperation.h67
-rw-r--r--libktorrent/interfaces/filetreediritem.cpp295
-rw-r--r--libktorrent/interfaces/filetreediritem.h140
-rw-r--r--libktorrent/interfaces/filetreeitem.cpp182
-rw-r--r--libktorrent/interfaces/filetreeitem.h91
-rw-r--r--libktorrent/interfaces/functions.cpp62
-rw-r--r--libktorrent/interfaces/functions.h47
-rw-r--r--libktorrent/interfaces/guiinterface.cpp54
-rw-r--r--libktorrent/interfaces/guiinterface.h218
-rw-r--r--libktorrent/interfaces/ipblockinginterface.cpp33
-rw-r--r--libktorrent/interfaces/ipblockinginterface.h48
-rw-r--r--libktorrent/interfaces/logmonitorinterface.cpp33
-rw-r--r--libktorrent/interfaces/logmonitorinterface.h50
-rw-r--r--libktorrent/interfaces/monitorinterface.cpp33
-rw-r--r--libktorrent/interfaces/monitorinterface.h79
-rw-r--r--libktorrent/interfaces/peerinterface.cpp33
-rw-r--r--libktorrent/interfaces/peerinterface.h91
-rw-r--r--libktorrent/interfaces/peersource.cpp64
-rw-r--r--libktorrent/interfaces/peersource.h118
-rw-r--r--libktorrent/interfaces/plugin.cpp49
-rw-r--r--libktorrent/interfaces/plugin.h152
-rw-r--r--libktorrent/interfaces/prefpageinterface.cpp32
-rw-r--r--libktorrent/interfaces/prefpageinterface.h84
-rw-r--r--libktorrent/interfaces/torrentfileinterface.cpp44
-rw-r--r--libktorrent/interfaces/torrentfileinterface.h131
-rw-r--r--libktorrent/interfaces/torrentinterface.cpp43
-rw-r--r--libktorrent/interfaces/torrentinterface.h509
-rw-r--r--libktorrent/interfaces/trackerslist.cpp51
-rw-r--r--libktorrent/interfaces/trackerslist.h89
-rw-r--r--libktorrent/kademlia/Makefile.am12
-rw-r--r--libktorrent/kademlia/announcetask.cpp154
-rw-r--r--libktorrent/kademlia/announcetask.h74
-rw-r--r--libktorrent/kademlia/database.cpp186
-rw-r--r--libktorrent/kademlia/database.h129
-rw-r--r--libktorrent/kademlia/dht.cpp378
-rw-r--r--libktorrent/kademlia/dht.h136
-rw-r--r--libktorrent/kademlia/dhtbase.cpp37
-rw-r--r--libktorrent/kademlia/dhtbase.h129
-rw-r--r--libktorrent/kademlia/dhttrackerbackend.cpp154
-rw-r--r--libktorrent/kademlia/dhttrackerbackend.h75
-rw-r--r--libktorrent/kademlia/kbucket.cpp355
-rw-r--r--libktorrent/kademlia/kbucket.h212
-rw-r--r--libktorrent/kademlia/kclosestnodessearch.cpp84
-rw-r--r--libktorrent/kademlia/kclosestnodessearch.h90
-rw-r--r--libktorrent/kademlia/key.cpp110
-rw-r--r--libktorrent/kademlia/key.h129
-rw-r--r--libktorrent/kademlia/node.cpp287
-rw-r--r--libktorrent/kademlia/node.h103
-rw-r--r--libktorrent/kademlia/nodelookup.cpp98
-rw-r--r--libktorrent/kademlia/nodelookup.h52
-rw-r--r--libktorrent/kademlia/pack.cpp62
-rw-r--r--libktorrent/kademlia/pack.h48
-rw-r--r--libktorrent/kademlia/rpccall.cpp79
-rw-r--r--libktorrent/kademlia/rpccall.h110
-rw-r--r--libktorrent/kademlia/rpcmsg.cpp596
-rw-r--r--libktorrent/kademlia/rpcmsg.h269
-rw-r--r--libktorrent/kademlia/rpcserver.cpp243
-rw-r--r--libktorrent/kademlia/rpcserver.h122
-rw-r--r--libktorrent/kademlia/task.cpp134
-rw-r--r--libktorrent/kademlia/task.h174
-rw-r--r--libktorrent/kademlia/taskmanager.cpp79
-rw-r--r--libktorrent/kademlia/taskmanager.h69
-rw-r--r--libktorrent/ktorrent.kcfg233
-rw-r--r--libktorrent/ktversion.h36
-rw-r--r--libktorrent/labelview.cpp257
-rw-r--r--libktorrent/labelview.h125
-rw-r--r--libktorrent/labelviewitembase.ui73
-rw-r--r--libktorrent/migrate/Makefile.am7
-rw-r--r--libktorrent/migrate/cachemigrate.cpp120
-rw-r--r--libktorrent/migrate/cachemigrate.h34
-rw-r--r--libktorrent/migrate/ccmigrate.cpp167
-rw-r--r--libktorrent/migrate/ccmigrate.h36
-rw-r--r--libktorrent/migrate/migrate.cpp75
-rw-r--r--libktorrent/migrate/migrate.h53
-rw-r--r--libktorrent/mse/Makefile.am9
-rw-r--r--libktorrent/mse/bigint.cpp100
-rw-r--r--libktorrent/mse/bigint.h98
-rw-r--r--libktorrent/mse/encryptedauthenticate.cpp302
-rw-r--r--libktorrent/mse/encryptedauthenticate.h82
-rw-r--r--libktorrent/mse/encryptedserverauthenticate.cpp354
-rw-r--r--libktorrent/mse/encryptedserverauthenticate.h80
-rw-r--r--libktorrent/mse/functions.cpp74
-rw-r--r--libktorrent/mse/functions.h39
-rw-r--r--libktorrent/mse/rc4encryptor.cpp100
-rw-r--r--libktorrent/mse/rc4encryptor.h96
-rw-r--r--libktorrent/mse/streamsocket.cpp326
-rw-r--r--libktorrent/mse/streamsocket.h185
-rw-r--r--libktorrent/net/Makefile.am10
-rw-r--r--libktorrent/net/address.cpp67
-rw-r--r--libktorrent/net/address.h60
-rw-r--r--libktorrent/net/bufferedsocket.cpp217
-rw-r--r--libktorrent/net/bufferedsocket.h150
-rw-r--r--libktorrent/net/circularbuffer.cpp146
-rw-r--r--libktorrent/net/circularbuffer.h89
-rw-r--r--libktorrent/net/downloadthread.cpp137
-rw-r--r--libktorrent/net/downloadthread.h64
-rw-r--r--libktorrent/net/networkthread.cpp165
-rw-r--r--libktorrent/net/networkthread.h113
-rw-r--r--libktorrent/net/portlist.cpp73
-rw-r--r--libktorrent/net/portlist.h103
-rw-r--r--libktorrent/net/socket.cpp326
-rw-r--r--libktorrent/net/socket.h83
-rw-r--r--libktorrent/net/socketgroup.cpp186
-rw-r--r--libktorrent/net/socketgroup.h90
-rw-r--r--libktorrent/net/socketmonitor.cpp173
-rw-r--r--libktorrent/net/socketmonitor.h118
-rw-r--r--libktorrent/net/speed.cpp78
-rw-r--r--libktorrent/net/speed.h51
-rw-r--r--libktorrent/net/uploadthread.cpp91
-rw-r--r--libktorrent/net/uploadthread.h61
-rw-r--r--libktorrent/pluginmanager.cpp312
-rw-r--r--libktorrent/pluginmanager.h118
-rw-r--r--libktorrent/pluginmanagerprefpage.cpp213
-rw-r--r--libktorrent/pluginmanagerprefpage.h70
-rw-r--r--libktorrent/pluginmanagerwidget.ui127
-rw-r--r--libktorrent/settings.kcfgc6
-rw-r--r--libktorrent/torrent/Makefile.am33
-rw-r--r--libktorrent/torrent/advancedchokealgorithm.cpp259
-rw-r--r--libktorrent/torrent/advancedchokealgorithm.h52
-rw-r--r--libktorrent/torrent/announcelist.cpp195
-rw-r--r--libktorrent/torrent/announcelist.h107
-rw-r--r--libktorrent/torrent/authenticate.cpp156
-rw-r--r--libktorrent/torrent/authenticate.h98
-rw-r--r--libktorrent/torrent/authenticatebase.cpp159
-rw-r--r--libktorrent/torrent/authenticatebase.h125
-rw-r--r--libktorrent/torrent/authenticationmonitor.cpp149
-rw-r--r--libktorrent/torrent/authenticationmonitor.h80
-rw-r--r--libktorrent/torrent/bdecoder.cpp224
-rw-r--r--libktorrent/torrent/bdecoder.h70
-rw-r--r--libktorrent/torrent/bencoder.cpp137
-rw-r--r--libktorrent/torrent/bencoder.h150
-rw-r--r--libktorrent/torrent/bnode.cpp177
-rw-r--r--libktorrent/torrent/bnode.h210
-rw-r--r--libktorrent/torrent/cache.cpp55
-rw-r--r--libktorrent/torrent/cache.h165
-rw-r--r--libktorrent/torrent/cachefile.cpp507
-rw-r--r--libktorrent/torrent/cachefile.h149
-rw-r--r--libktorrent/torrent/cap.cpp123
-rw-r--r--libktorrent/torrent/cap.h113
-rw-r--r--libktorrent/torrent/choker.cpp86
-rw-r--r--libktorrent/torrent/choker.h123
-rw-r--r--libktorrent/torrent/chunk.cpp81
-rw-r--r--libktorrent/torrent/chunk.h165
-rw-r--r--libktorrent/torrent/chunkcounter.cpp80
-rw-r--r--libktorrent/torrent/chunkcounter.h83
-rw-r--r--libktorrent/torrent/chunkdownload.cpp484
-rw-r--r--libktorrent/torrent/chunkdownload.h207
-rw-r--r--libktorrent/torrent/chunkmanager.cpp1157
-rw-r--r--libktorrent/torrent/chunkmanager.h366
-rw-r--r--libktorrent/torrent/chunkselector.cpp185
-rw-r--r--libktorrent/torrent/chunkselector.h80
-rw-r--r--libktorrent/torrent/dndfile.cpp268
-rw-r--r--libktorrent/torrent/dndfile.h89
-rw-r--r--libktorrent/torrent/downloadcap.cpp43
-rw-r--r--libktorrent/torrent/downloadcap.h48
-rw-r--r--libktorrent/torrent/downloader.cpp688
-rw-r--r--libktorrent/torrent/downloader.h221
-rw-r--r--libktorrent/torrent/globals.cpp97
-rw-r--r--libktorrent/torrent/globals.h78
-rw-r--r--libktorrent/torrent/httptracker.cpp462
-rw-r--r--libktorrent/torrent/httptracker.h77
-rw-r--r--libktorrent/torrent/ipblocklist.cpp400
-rw-r--r--libktorrent/torrent/ipblocklist.h175
-rw-r--r--libktorrent/torrent/movedatafilesjob.cpp103
-rw-r--r--libktorrent/torrent/movedatafilesjob.h68
-rw-r--r--libktorrent/torrent/multifilecache.cpp867
-rw-r--r--libktorrent/torrent/multifilecache.h74
-rw-r--r--libktorrent/torrent/newchokealgorithm.cpp345
-rw-r--r--libktorrent/torrent/newchokealgorithm.h54
-rw-r--r--libktorrent/torrent/oldchokealgorithm.cpp223
-rw-r--r--libktorrent/torrent/oldchokealgorithm.h54
-rw-r--r--libktorrent/torrent/packet.cpp175
-rw-r--r--libktorrent/torrent/packet.h91
-rw-r--r--libktorrent/torrent/packetreader.cpp247
-rw-r--r--libktorrent/torrent/packetreader.h68
-rw-r--r--libktorrent/torrent/packetwriter.cpp399
-rw-r--r--libktorrent/torrent/packetwriter.h185
-rw-r--r--libktorrent/torrent/peer.cpp593
-rw-r--r--libktorrent/torrent/peer.h324
-rw-r--r--libktorrent/torrent/peerdownloader.cpp311
-rw-r--r--libktorrent/torrent/peerdownloader.h231
-rw-r--r--libktorrent/torrent/peerid.cpp253
-rw-r--r--libktorrent/torrent/peerid.h61
-rw-r--r--libktorrent/torrent/peermanager.cpp607
-rw-r--r--libktorrent/torrent/peermanager.h251
-rw-r--r--libktorrent/torrent/peersourcemanager.cpp556
-rw-r--r--libktorrent/torrent/peersourcemanager.h182
-rw-r--r--libktorrent/torrent/peeruploader.cpp130
-rw-r--r--libktorrent/torrent/peeruploader.h93
-rw-r--r--libktorrent/torrent/piece.cpp34
-rw-r--r--libktorrent/torrent/piece.h44
-rw-r--r--libktorrent/torrent/preallocationthread.cpp134
-rw-r--r--libktorrent/torrent/preallocationthread.h94
-rw-r--r--libktorrent/torrent/queuemanager.cpp811
-rw-r--r--libktorrent/torrent/queuemanager.h173
-rw-r--r--libktorrent/torrent/request.cpp52
-rw-r--r--libktorrent/torrent/request.h96
-rw-r--r--libktorrent/torrent/server.cpp200
-rw-r--r--libktorrent/torrent/server.h125
-rw-r--r--libktorrent/torrent/serverauthenticate.cpp126
-rw-r--r--libktorrent/torrent/serverauthenticate.h72
-rw-r--r--libktorrent/torrent/singlefilecache.cpp232
-rw-r--r--libktorrent/torrent/singlefilecache.h64
-rw-r--r--libktorrent/torrent/speedestimater.cpp105
-rw-r--r--libktorrent/torrent/speedestimater.h55
-rw-r--r--libktorrent/torrent/statsfile.cpp120
-rw-r--r--libktorrent/torrent/statsfile.h91
-rw-r--r--libktorrent/torrent/timeestimator.cpp278
-rw-r--r--libktorrent/torrent/timeestimator.h119
-rw-r--r--libktorrent/torrent/torrent.cpp449
-rw-r--r--libktorrent/torrent/torrent.h218
-rw-r--r--libktorrent/torrent/torrentcontrol.cpp1770
-rw-r--r--libktorrent/torrent/torrentcontrol.h394
-rw-r--r--libktorrent/torrent/torrentcreator.cpp388
-rw-r--r--libktorrent/torrent/torrentcreator.h114
-rw-r--r--libktorrent/torrent/torrentfile.cpp200
-rw-r--r--libktorrent/torrent/torrentfile.h158
-rw-r--r--libktorrent/torrent/torrentmonitor.cpp33
-rw-r--r--libktorrent/torrent/torrentmonitor.h47
-rw-r--r--libktorrent/torrent/tracker.cpp93
-rw-r--r--libktorrent/torrent/tracker.h136
-rw-r--r--libktorrent/torrent/udptracker.cpp291
-rw-r--r--libktorrent/torrent/udptracker.h105
-rw-r--r--libktorrent/torrent/udptrackersocket.cpp222
-rw-r--r--libktorrent/torrent/udptrackersocket.h139
-rw-r--r--libktorrent/torrent/uploadcap.cpp46
-rw-r--r--libktorrent/torrent/uploadcap.h56
-rw-r--r--libktorrent/torrent/uploader.cpp67
-rw-r--r--libktorrent/torrent/uploader.h75
-rw-r--r--libktorrent/torrent/upspeedestimater.cpp148
-rw-r--r--libktorrent/torrent/upspeedestimater.h86
-rw-r--r--libktorrent/torrent/utpex.cpp155
-rw-r--r--libktorrent/torrent/utpex.h71
-rw-r--r--libktorrent/torrent/value.cpp91
-rw-r--r--libktorrent/torrent/value.h67
-rw-r--r--libktorrent/util/Makefile.am18
-rw-r--r--libktorrent/util/array.cpp27
-rw-r--r--libktorrent/util/array.h73
-rw-r--r--libktorrent/util/autorotatelogjob.cpp88
-rw-r--r--libktorrent/util/autorotatelogjob.h59
-rw-r--r--libktorrent/util/bitset.cpp111
-rw-r--r--libktorrent/util/bitset.h157
-rw-r--r--libktorrent/util/constants.h96
-rw-r--r--libktorrent/util/error.cpp33
-rw-r--r--libktorrent/util/error.h44
-rw-r--r--libktorrent/util/file.cpp150
-rw-r--r--libktorrent/util/file.h114
-rw-r--r--libktorrent/util/fileops.cpp466
-rw-r--r--libktorrent/util/fileops.h165
-rw-r--r--libktorrent/util/functions.cpp239
-rw-r--r--libktorrent/util/functions.h72
-rw-r--r--libktorrent/util/httprequest.cpp122
-rw-r--r--libktorrent/util/httprequest.h98
-rw-r--r--libktorrent/util/log.cpp249
-rw-r--r--libktorrent/util/log.h209
-rw-r--r--libktorrent/util/mmapfile.cpp294
-rw-r--r--libktorrent/util/mmapfile.h146
-rw-r--r--libktorrent/util/profiler.cpp138
-rw-r--r--libktorrent/util/profiler.h108
-rw-r--r--libktorrent/util/ptrmap.cpp24
-rw-r--r--libktorrent/util/ptrmap.h181
-rw-r--r--libktorrent/util/sha1hash.cpp131
-rw-r--r--libktorrent/util/sha1hash.h148
-rw-r--r--libktorrent/util/sha1hashgen.cpp340
-rw-r--r--libktorrent/util/sha1hashgen.h90
-rw-r--r--libktorrent/util/timer.cpp64
-rw-r--r--libktorrent/util/timer.h49
-rw-r--r--libktorrent/util/urlencoder.cpp92
-rw-r--r--libktorrent/util/urlencoder.h40
-rw-r--r--libktorrent/util/waitjob.cpp86
-rw-r--r--libktorrent/util/waitjob.h78
293 files changed, 45035 insertions, 0 deletions
diff --git a/libktorrent/Makefile.am b/libktorrent/Makefile.am
new file mode 100644
index 0000000..cea2eaa
--- /dev/null
+++ b/libktorrent/Makefile.am
@@ -0,0 +1,22 @@
+INCLUDES = -I$(srcdir)/. $(all_includes)
+SUBDIRS = util torrent kademlia interfaces migrate mse datachecker net
+METASOURCES = AUTO
+
+lib_LTLIBRARIES = libktorrent.la
+libktorrent_la_LDFLAGS = ${KDE_RPATH} $(all_libraries) -release $(VERSION)
+
+kde_kcfg_DATA = ktorrent.kcfg
+
+# make sure settings.h is built before anything else
+BUILT_SOURCES=settings.h
+
+noinst_HEADERS = expandablewidget.h functions.h ktversion.h labelview.h \
+ pluginmanager.h pluginmanagerprefpage.h
+libktorrent_la_SOURCES = expandablewidget.cpp functions.cpp labelview.cpp \
+ labelviewitembase.ui pluginmanager.cpp pluginmanagerprefpage.cpp pluginmanagerwidget.ui \
+ settings.kcfgc
+libktorrent_la_LIBADD = ../libktorrent/net/libnet.la \
+ ../libktorrent/datachecker/libdatachecker.la ../libktorrent/mse/libmse.la ../libktorrent/migrate/libmigrate.la \
+ ../libktorrent/util/libutil.la ../libktorrent/torrent/libtorrent.la \
+ ../libktorrent/kademlia/libkademlia.la ../libktorrent/interfaces/libinterfaces.la $(LIB_KPARTS)
+KDE_CXXFLAGS = $(USE_EXCEPTIONS) $(USE_RTTI)
diff --git a/libktorrent/datachecker/Makefile.am b/libktorrent/datachecker/Makefile.am
new file mode 100644
index 0000000..b5e9ee0
--- /dev/null
+++ b/libktorrent/datachecker/Makefile.am
@@ -0,0 +1,8 @@
+INCLUDES = -I$(srcdir)/.. -I$(srcdir)/. $(all_includes)
+METASOURCES = AUTO
+libdatachecker_la_LDFLAGS = $(all_libraries)
+noinst_LTLIBRARIES = libdatachecker.la
+libdatachecker_la_SOURCES = datachecker.cpp multidatachecker.cpp \
+ singledatachecker.cpp datacheckerlistener.cpp datacheckerthread.cpp
+noinst_HEADERS = datacheckerlistener.h datacheckerthread.h
+KDE_CXXFLAGS = $(USE_EXCEPTIONS) $(USE_RTTI)
diff --git a/libktorrent/datachecker/datachecker.cpp b/libktorrent/datachecker/datachecker.cpp
new file mode 100644
index 0000000..04bd08e
--- /dev/null
+++ b/libktorrent/datachecker/datachecker.cpp
@@ -0,0 +1,34 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include "datachecker.h"
+
+namespace bt {
+
+ DataChecker::DataChecker() : listener(0)
+ {
+ }
+
+
+ DataChecker::~DataChecker()
+ {
+ }
+
+
+}
diff --git a/libktorrent/datachecker/datachecker.h b/libktorrent/datachecker/datachecker.h
new file mode 100644
index 0000000..e181925
--- /dev/null
+++ b/libktorrent/datachecker/datachecker.h
@@ -0,0 +1,78 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef BTDATACHECKER_H
+#define BTDATACHECKER_H
+
+#include <util/bitset.h>
+#include "datacheckerlistener.h"
+
+class QString;
+
+
+namespace bt
+{
+ class Torrent;
+
+
+ /**
+ * @author Joris Guisson
+ *
+ * Checks which data is downloaded, given a torrent and a file or directory containing
+ * files of the torrent.
+ */
+ class DataChecker
+ {
+ public:
+ DataChecker();
+ virtual ~DataChecker();
+
+ /// Set the listener
+ void setListener(DataCheckerListener* l) {listener = l;}
+
+ /**
+ * Check to see which chunks have been downloaded of a torrent, and which chunks fail.
+ * The corresponding bitsets should be filled with this information.
+ * If anything goes wrong and Error should be thrown.
+ * @param path path to the file or dir (this needs to end with the name suggestion of the torrent)
+ * @param tor The torrent
+ * @param dnddir DND dir, optional argument if we know this
+ */
+ virtual void check(const QString & path,const Torrent & tor,const QString & dnddir) = 0;
+
+ /**
+ * Get the BitSet representing all the downloaded chunks.
+ */
+ const BitSet & getDownloaded() const {return downloaded;}
+
+ /**
+ * Get the BitSet representing all the failed chunks.
+ */
+ const BitSet & getFailed() const {return failed;}
+
+ /// Get the listener
+ DataCheckerListener* getListener() {return listener;}
+ protected:
+ BitSet failed,downloaded;
+ DataCheckerListener* listener;
+ };
+
+}
+
+#endif
diff --git a/libktorrent/datachecker/datacheckerlistener.cpp b/libktorrent/datachecker/datacheckerlistener.cpp
new file mode 100644
index 0000000..a4a2201
--- /dev/null
+++ b/libktorrent/datachecker/datacheckerlistener.cpp
@@ -0,0 +1,33 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include "datacheckerlistener.h"
+
+namespace bt
+{
+
+ DataCheckerListener::DataCheckerListener(bool auto_import) : stopped(false),auto_import(auto_import)
+ {}
+
+
+ DataCheckerListener::~DataCheckerListener()
+ {}
+
+
+}
diff --git a/libktorrent/datachecker/datacheckerlistener.h b/libktorrent/datachecker/datacheckerlistener.h
new file mode 100644
index 0000000..a770bab
--- /dev/null
+++ b/libktorrent/datachecker/datacheckerlistener.h
@@ -0,0 +1,80 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef BTDATACHECKERLISTENER_H
+#define BTDATACHECKERLISTENER_H
+
+#include <util/constants.h>
+
+namespace bt
+{
+
+ /**
+ @author Joris Guisson <joris.guisson@gmail.com>
+ */
+ class DataCheckerListener
+ {
+ public:
+ DataCheckerListener(bool auto_import);
+ virtual ~DataCheckerListener();
+
+ /**
+ * Called when a chunk has been proccessed.
+ * @param num The number processed
+ * @param total The total number of pieces to process
+ */
+ virtual void progress(Uint32 num,Uint32 total) = 0;
+
+ /**
+ * Called when a failed or dowloaded chunk is found.
+ * @param num_failed The number of failed chunks
+ * @param num_downloaded Number of downloaded chunks
+ */
+ virtual void status(Uint32 num_failed,Uint32 num_downloaded) = 0;
+
+ /**
+ * Data check has been finished.
+ */
+ virtual void finished() = 0;
+
+ /**
+ * Test if we need to stop.
+ */
+ bool needToStop() const {return stopped;}
+
+ /// Check if the check has been stopped
+ bool isStopped() const {return stopped;}
+
+ /// Is this an auto_import
+ bool isAutoImport() const {return auto_import;}
+
+ /**
+ * Stop the data check.
+ */
+ void stop() {stopped = true;}
+ private:
+ bool stopped;
+
+ protected:
+ bool auto_import;
+ };
+
+}
+
+#endif
diff --git a/libktorrent/datachecker/datacheckerthread.cpp b/libktorrent/datachecker/datacheckerthread.cpp
new file mode 100644
index 0000000..12a58d7
--- /dev/null
+++ b/libktorrent/datachecker/datacheckerthread.cpp
@@ -0,0 +1,58 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <util/log.h>
+#include <util/error.h>
+#include <torrent/torrent.h>
+#include "datachecker.h"
+#include "datacheckerthread.h"
+
+namespace bt
+{
+
+ DataCheckerThread::DataCheckerThread(DataChecker* dc,
+ const QString & path,
+ const Torrent & tor,
+ const QString & dnddir)
+ : dc(dc),path(path),tor(tor),dnddir(dnddir)
+ {
+ running = true;
+ }
+
+
+ DataCheckerThread::~DataCheckerThread()
+ {
+ delete dc;
+ }
+
+ void DataCheckerThread::run()
+ {
+ try
+ {
+ dc->check(path,tor,dnddir);
+ }
+ catch (bt::Error & e)
+ {
+ error = e.toString();
+ Out(SYS_GEN|LOG_DEBUG) << error << endl;
+ }
+ running = false;
+ }
+
+}
diff --git a/libktorrent/datachecker/datacheckerthread.h b/libktorrent/datachecker/datacheckerthread.h
new file mode 100644
index 0000000..749e3e8
--- /dev/null
+++ b/libktorrent/datachecker/datacheckerthread.h
@@ -0,0 +1,61 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef BTDATACHECKERTHREAD_H
+#define BTDATACHECKERTHREAD_H
+
+#include <qthread.h>
+
+namespace bt
+{
+ class Torrent;
+ class DataChecker;
+
+ /**
+ @author Joris Guisson <joris.guisson@gmail.com>
+
+ Thread which runs the data check.
+ */
+ class DataCheckerThread : public QThread
+ {
+ DataChecker* dc;
+ QString path;
+ const Torrent & tor;
+ QString dnddir;
+ bool running;
+ QString error;
+ public:
+ DataCheckerThread(DataChecker* dc,const QString & path,const Torrent & tor,const QString & dnddir);
+ virtual ~DataCheckerThread();
+
+ virtual void run();
+
+ /// Get the data checker
+ DataChecker* getDataChecker() {return dc;}
+
+ /// Are we still running
+ bool isRunning() const {return running;}
+
+ /// Get the error (if any occured)
+ QString getError() const {return error;}
+ };
+
+}
+
+#endif
diff --git a/libktorrent/datachecker/multidatachecker.cpp b/libktorrent/datachecker/multidatachecker.cpp
new file mode 100644
index 0000000..3c26721
--- /dev/null
+++ b/libktorrent/datachecker/multidatachecker.cpp
@@ -0,0 +1,201 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson & Maggioni Marcello *
+ * joris.guisson@gmail.com *
+ * marcello.maggioni@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+
+#include <klocale.h>
+#include <kapplication.h>
+#include <util/log.h>
+#include <util/file.h>
+#include <util/fileops.h>
+#include <util/error.h>
+#include <util/array.h>
+#include <util/functions.h>
+#include <torrent/dndfile.h>
+#include <torrent/globals.h>
+#include <torrent/torrent.h>
+#include <torrent/torrentfile.h>
+#include "multidatachecker.h"
+
+namespace bt
+{
+
+ MultiDataChecker::MultiDataChecker(): DataChecker(),buf(0)
+ {}
+
+
+ MultiDataChecker::~MultiDataChecker()
+ {
+ delete [] buf;
+ }
+
+ void MultiDataChecker::check(const QString& path, const Torrent& tor,const QString & dnddir)
+ {
+ Uint32 num_chunks = tor.getNumChunks();
+ // initialize the bitsets
+ downloaded = BitSet(num_chunks);
+ failed = BitSet(num_chunks);
+
+ cache = path;
+ if (!cache.endsWith(bt::DirSeparator()))
+ cache += bt::DirSeparator();
+
+ dnd_dir = dnddir;
+ if (!dnddir.endsWith(bt::DirSeparator()))
+ dnd_dir += bt::DirSeparator();
+
+ Uint64 chunk_size = tor.getChunkSize();
+ Uint32 cur_chunk = 0;
+ TimeStamp last_update_time = bt::GetCurrentTime();
+
+ buf = new Uint8[chunk_size];
+
+ for (cur_chunk = 0;cur_chunk < num_chunks;cur_chunk++)
+ {
+ Uint32 cs = (cur_chunk == num_chunks - 1) ? tor.getFileLength() % chunk_size : chunk_size;
+ if (cs == 0)
+ cs = chunk_size;
+ if (!loadChunk(cur_chunk,cs,tor))
+ {
+ downloaded.set(cur_chunk,false);
+ failed.set(cur_chunk,true);
+ continue;
+ }
+
+ bool ok = (SHA1Hash::generate(buf,cs) == tor.getHash(cur_chunk));
+ downloaded.set(cur_chunk,ok);
+ failed.set(cur_chunk,!ok);
+
+ if (listener)
+ {
+ listener->status(failed.numOnBits(),downloaded.numOnBits());
+ listener->progress(cur_chunk,num_chunks);
+ if (listener->needToStop())
+ return;
+ }
+
+ TimeStamp now = bt::GetCurrentTime();
+ if (now - last_update_time > 1000)
+ {
+ Out() << "Checked " << cur_chunk << " chunks" << endl;
+ // KApplication::kApplication()->processEvents();
+ last_update_time = now;
+ }
+ }
+ }
+
+ static Uint32 ReadFullChunk(Uint32 chunk,Uint32 cs,
+ const TorrentFile & tf,
+ const Torrent & tor,
+ Uint8* buf,
+ const QString & cache)
+ {
+ File fptr;
+ if (!fptr.open(cache + tf.getPath(), "rb"))
+ {
+ Out() << QString("Warning : Cannot open %1 : %2").arg(cache +
+ tf.getPath()).arg(fptr.errorString()) << endl;
+ return 0;
+ }
+
+ Uint64 off = tf.fileOffset(chunk,tor.getChunkSize());
+ fptr.seek(File::BEGIN,off);
+ return fptr.read(buf,cs);
+ }
+
+ bool MultiDataChecker::loadChunk(Uint32 ci,Uint32 cs,const Torrent & tor)
+ {
+ QValueList<Uint32> tflist;
+ tor.calcChunkPos(ci,tflist);
+
+ // one file is simple
+ if (tflist.count() == 1)
+ {
+ const TorrentFile & f = tor.getFile(tflist.first());
+ if (!f.doNotDownload())
+ {
+ ReadFullChunk(ci,cs,f,tor,buf,cache);
+ return true;
+ }
+ return false;
+ }
+
+ Uint64 read = 0; // number of bytes read
+ for (Uint32 i = 0;i < tflist.count();i++)
+ {
+ const TorrentFile & f = tor.getFile(tflist[i]);
+
+ // first calculate offset into file
+ // only the first file can have an offset
+ // the following files will start at the beginning
+ Uint64 off = 0;
+ if (i == 0)
+ off = f.fileOffset(ci,tor.getChunkSize());
+
+ Uint32 to_read = 0;
+ // then the amount of data we can read from this file
+ if (i == 0)
+ to_read = f.getLastChunkSize();
+ else if (i == tflist.count() - 1)
+ to_read = cs - read;
+ else
+ to_read = f.getSize();
+
+ // read part of data
+ if (f.doNotDownload())
+ {
+ if (!dnd_dir.isNull() && bt::Exists(dnd_dir + f.getPath() + ".dnd"))
+ {
+ Uint32 ret = 0;
+ DNDFile dfd(dnd_dir + f.getPath() + ".dnd");
+ if (i == 0)
+ ret = dfd.readLastChunk(buf,read,cs);
+ else if (i == tflist.count() - 1)
+ ret = dfd.readFirstChunk(buf,read,cs);
+ else
+ ret = dfd.readFirstChunk(buf,read,cs);
+
+ if (ret > 0 && ret != to_read)
+ Out() << "Warning : MultiDataChecker::load ret != to_read (dnd)" << endl;
+ }
+ }
+ else
+ {
+ if (!bt::Exists(cache + f.getPath()) || bt::FileSize(cache + f.getPath()) < off)
+ return false;
+
+ File fptr;
+ if (!fptr.open(cache + f.getPath(), "rb"))
+ {
+ Out() << QString("Warning : Cannot open %1 : %2").arg(cache +
+ f.getPath()).arg(fptr.errorString()) << endl;
+ return false;
+ }
+ else
+ {
+ fptr.seek(File::BEGIN,off);
+ if (fptr.read(buf+read,to_read) != to_read)
+ Out() << "Warning : MultiDataChecker::load ret != to_read" << endl;
+ }
+ }
+ read += to_read;
+ }
+ return true;
+ }
+}
diff --git a/libktorrent/datachecker/multidatachecker.h b/libktorrent/datachecker/multidatachecker.h
new file mode 100644
index 0000000..d095e99
--- /dev/null
+++ b/libktorrent/datachecker/multidatachecker.h
@@ -0,0 +1,49 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef BTMULTIDATACHECKER_H
+#define BTMULTIDATACHECKER_H
+
+#include "datachecker.h"
+
+namespace bt
+{
+
+ /**
+ @author Joris Guisson
+ */
+ class MultiDataChecker : public DataChecker
+ {
+ public:
+ MultiDataChecker();
+ virtual ~MultiDataChecker();
+
+ virtual void check(const QString& path, const Torrent& tor,const QString & dnddir);
+ private:
+ bool loadChunk(Uint32 ci,Uint32 cs,const Torrent & to);
+
+ private:
+ QString cache;
+ QString dnd_dir;
+ Uint8* buf;
+ };
+
+}
+
+#endif
diff --git a/libktorrent/datachecker/singledatachecker.cpp b/libktorrent/datachecker/singledatachecker.cpp
new file mode 100644
index 0000000..0579338
--- /dev/null
+++ b/libktorrent/datachecker/singledatachecker.cpp
@@ -0,0 +1,103 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <klocale.h>
+#include <kapplication.h>
+#include <util/log.h>
+#include <util/file.h>
+#include <util/error.h>
+#include <util/array.h>
+#include <util/functions.h>
+#include <torrent/globals.h>
+#include <torrent/torrent.h>
+#include "singledatachecker.h"
+
+namespace bt
+{
+
+ SingleDataChecker::SingleDataChecker(): DataChecker()
+ {}
+
+
+ SingleDataChecker::~SingleDataChecker()
+ {}
+
+
+ void SingleDataChecker::check(const QString& path, const Torrent& tor,const QString &)
+ {
+ // open the file
+ Uint32 num_chunks = tor.getNumChunks();
+ Uint32 chunk_size = tor.getChunkSize();
+ File fptr;
+ if (!fptr.open(path,"rb"))
+ {
+ throw Error(i18n("Cannot open file : %1 : %2")
+ .arg(path).arg( fptr.errorString()));
+ }
+
+ // initialize the bitsets
+ downloaded = BitSet(num_chunks);
+ failed = BitSet(num_chunks);
+
+ TimeStamp last_update_time = bt::GetCurrentTime();
+
+ // loop over all chunks
+ Array<Uint8> buf(chunk_size);
+ for (Uint32 i = 0;i < num_chunks;i++)
+ {
+ if (listener)
+ {
+ listener->progress(i,num_chunks);
+ if (listener->needToStop()) // if we need to stop just return
+ return;
+ }
+
+ TimeStamp now = bt::GetCurrentTime();
+ if (now - last_update_time > 1000)
+ {
+ Out(SYS_DIO|LOG_DEBUG) << "Checked " << i << " chunks" << endl;
+ last_update_time = now;
+ }
+
+ if (!fptr.eof())
+ {
+ // read the chunk
+ Uint32 size = i == num_chunks - 1 && tor.getFileLength() % tor.getChunkSize() > 0 ?
+ tor.getFileLength() % tor.getChunkSize() : (Uint32)tor.getChunkSize();
+
+ fptr.seek(File::BEGIN,(Int64)i*tor.getChunkSize());
+ fptr.read(buf,size);
+ // generate and test hash
+ SHA1Hash h = SHA1Hash::generate(buf,size);
+ bool ok = (h == tor.getHash(i));
+ downloaded.set(i,ok);
+ failed.set(i,!ok);
+ }
+ else
+ {
+ // at end of file so set to default values for a failed chunk
+ downloaded.set(i,false);
+ failed.set(i,true);
+ }
+ if (listener)
+ listener->status(failed.numOnBits(),downloaded.numOnBits());
+ }
+ }
+
+}
diff --git a/libktorrent/datachecker/singledatachecker.h b/libktorrent/datachecker/singledatachecker.h
new file mode 100644
index 0000000..20107b3
--- /dev/null
+++ b/libktorrent/datachecker/singledatachecker.h
@@ -0,0 +1,44 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef BTSINGLEDATACHECKER_H
+#define BTSINGLEDATACHECKER_H
+
+#include "datachecker.h"
+
+namespace bt
+{
+
+ /**
+ * @author Joris Guisson
+ *
+ * Data checker for single file torrents.
+ */
+ class SingleDataChecker : public DataChecker
+ {
+ public:
+ SingleDataChecker();
+ virtual ~SingleDataChecker();
+
+ virtual void check(const QString& path, const Torrent& tor,const QString & dnddir);
+ };
+
+}
+
+#endif
diff --git a/libktorrent/expandablewidget.cpp b/libktorrent/expandablewidget.cpp
new file mode 100644
index 0000000..cdac376
--- /dev/null
+++ b/libktorrent/expandablewidget.cpp
@@ -0,0 +1,179 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <qlayout.h>
+#include <qsplitter.h>
+#include "expandablewidget.h"
+
+namespace kt
+{
+
+ ExpandableWidget::ExpandableWidget(QWidget* child,QWidget *parent, const char *name)
+ : QWidget(parent, name)
+ {
+ top_layout = new QHBoxLayout(this);
+ child->reparent(this,QPoint(),true);
+ // make top of stack
+ begin = new StackElement;
+ begin->w = child;
+ top_layout->add(child);
+ }
+
+
+ ExpandableWidget::~ExpandableWidget()
+ {
+ if (begin)
+ {
+ // delete begin->w;
+ delete begin;
+ }
+ }
+
+ void ExpandableWidget::expand(QWidget* w,Position pos)
+ {
+ // create new element
+ StackElement* se = new StackElement;
+ se->w = w;
+ se->pos = pos;
+ se->next = begin;
+
+ // remove old top from layout
+ top_layout->remove(begin->w);
+
+ // create new toplevel splitter
+ Qt::Orientation orientation = (pos == RIGHT || pos == LEFT) ? Qt::Horizontal : Qt::Vertical;
+ QSplitter* s = new QSplitter(orientation,this);;
+ se->s = s;
+
+ // reparent w and the bottom widget to s
+ w->reparent(s,QPoint(),false);
+ if (begin->s)
+ begin->s->reparent(s,QPoint(),false);
+ else
+ begin->w->reparent(s,QPoint(),false);
+
+ // add w and the bottom widget to s
+ if (pos == RIGHT || pos == ABOVE)
+ {
+ s->moveToFirst(w);
+ s->setResizeMode(w,QSplitter::KeepSize);
+ s->moveToLast(begin->s ? begin->s : begin->w);
+ }
+ else
+ {
+ s->moveToFirst(begin->s ? begin->s : begin->w);
+ s->moveToLast(w);
+ s->setResizeMode(w,QSplitter::KeepSize);
+ }
+ // make se new top of stack
+ begin = se;
+
+ // add toplevel splitter to layout
+ top_layout->add(s);
+
+ // show s
+ s->show();
+ }
+
+ void ExpandableWidget::remove(QWidget* w)
+ {
+ // find the correct stackelement
+ StackElement* se = begin;
+ StackElement* prev = 0; // element before se
+ while (se->w != w && se->next)
+ {
+ prev = se;
+ se = se->next;
+ }
+
+ // do not remove last
+ if (!se->next)
+ return;
+
+ if (!prev)
+ {
+ // we need to remove the first
+ top_layout->remove(se->s);
+ // reparent current top to 0
+ se->w->reparent(0,QPoint(),false);
+ se->s->reparent(0,QPoint(),false);
+ // set new top
+ begin = se->next;
+
+
+ if (begin->s)
+ {
+ begin->s->reparent(this,QPoint(),false);
+ top_layout->add(begin->s);
+ begin->s->show();
+ }
+ else
+ {
+ begin->w->reparent(this,QPoint(),false);
+ top_layout->add(begin->w);
+ begin->w->show();
+ }
+
+ se->next = 0;
+ // delete splitter and se
+ delete se->s;
+ delete se;
+ }
+ else
+ {
+ StackElement* next = se->next;
+ // isolate the node
+ se->next = 0;
+ prev->next = next;
+
+ // reparent se to 0
+ se->s->reparent(0,QPoint(),false);
+ se->w->reparent(0,QPoint(),false);
+
+ // reparent se->next to prev
+ if (next->s)
+ next->s->reparent(prev->s,QPoint(),false);
+ else
+ next->w->reparent(prev->s,QPoint(),false);
+
+ // update prev's splitter
+ if (prev->pos == RIGHT || prev->pos == ABOVE)
+ {
+ prev->s->moveToFirst(prev->w);
+ prev->s->setResizeMode(prev->w,QSplitter::KeepSize);
+ prev->s->moveToLast(next->s ? next->s : next->w);
+ prev->s->setResizeMode(next->s ? next->s : next->w,QSplitter::KeepSize);
+ }
+ else
+ {
+ prev->s->moveToFirst(next->s ? next->s : next->w);
+ prev->s->setResizeMode(next->s ? next->s : next->w,QSplitter::KeepSize);
+ prev->s->moveToLast(prev->w);
+ prev->s->setResizeMode(prev->w,QSplitter::KeepSize);
+ }
+
+ // delete se and splitter
+ delete se->s;
+ delete se;
+ prev->next->w->show();
+ prev->s->show();
+ }
+ }
+}
+#include "expandablewidget.moc"
diff --git a/libktorrent/expandablewidget.h b/libktorrent/expandablewidget.h
new file mode 100644
index 0000000..823ce5f
--- /dev/null
+++ b/libktorrent/expandablewidget.h
@@ -0,0 +1,89 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef KTEXPANDABLEWIDGET_H
+#define KTEXPANDABLEWIDGET_H
+
+#include <qwidget.h>
+#include <qptrlist.h>
+#include <interfaces/guiinterface.h>
+
+class QSplitter;
+class QHBoxLayout;
+
+namespace kt
+{
+
+
+
+ /**
+ * @author Joris Guisson
+ * @brief Widget which can be expanded with more widgets
+ *
+ * This is a sort of container widget, which at the minimum has
+ * one child widget. It allows to add more widgets separating the new widget
+ * and everything which was previously in the container by a separator.
+ */
+ class ExpandableWidget : public QWidget
+ {
+ Q_OBJECT
+ public:
+ /**
+ * Constructor, the first child must be provided.
+ * @param child The first child
+ * @param parent The parent
+ * @param name The name
+ */
+ ExpandableWidget(QWidget* child,QWidget *parent = 0, const char *name = 0);
+ virtual ~ExpandableWidget();
+
+
+
+ /**
+ * Expand the widget. This will ensure the proper parent child relations.
+ * @param w The widget
+ * @param pos It's position relative to the current widget
+ */
+ void expand(QWidget* w,Position pos);
+
+ /**
+ * Remove a widget. This will ensure the proper parent child relations.
+ * The widget w will become parentless. Note the first child will never be removed.
+ * @param w The widget
+ */
+ void remove(QWidget* w);
+ private:
+ struct StackElement
+ {
+ QWidget* w;
+ QSplitter* s;
+ Position pos;
+ StackElement* next;
+
+ StackElement() : w(0),s(0),pos(LEFT),next(0) {}
+ ~StackElement() {delete next;}
+ };
+
+ StackElement* begin;
+ QHBoxLayout* top_layout;
+ };
+
+}
+
+#endif
diff --git a/libktorrent/functions.cpp b/libktorrent/functions.cpp
new file mode 100644
index 0000000..3bc4f88
--- /dev/null
+++ b/libktorrent/functions.cpp
@@ -0,0 +1,40 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <qdatetime.h>
+#include <klocale.h>
+#include <kglobal.h>
+#include <kstandarddirs.h>
+#include <util/functions.h>
+#include "functions.h"
+
+using namespace bt;
+
+namespace kt
+{
+ QString DataDir()
+ {
+ QString str = KGlobal::dirs()->saveLocation("data","ktorrent");
+ if (!str.endsWith(bt::DirSeparator()))
+ return str + bt::DirSeparator();
+ else
+ return str;
+ }
+
+}
diff --git a/libktorrent/functions.h b/libktorrent/functions.h
new file mode 100644
index 0000000..02f7870
--- /dev/null
+++ b/libktorrent/functions.h
@@ -0,0 +1,35 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef FUNCTIONS_H
+#define FUNCTIONS_H
+
+#include <qstring.h>
+#include <util/constants.h>
+
+namespace kt
+{
+ /**
+ * Will return the data directory
+ * @return ~/.kde/share/apps/ktorrent/
+ */
+ QString DataDir();
+}
+
+#endif
diff --git a/libktorrent/interfaces/Makefile.am b/libktorrent/interfaces/Makefile.am
new file mode 100644
index 0000000..dca1a4a
--- /dev/null
+++ b/libktorrent/interfaces/Makefile.am
@@ -0,0 +1,15 @@
+INCLUDES = -I$(srcdir)/.. $(all_includes)
+METASOURCES = AUTO
+libinterfaces_la_LDFLAGS = $(all_libraries)
+noinst_LTLIBRARIES = libinterfaces.la
+noinst_HEADERS = plugin.h coreinterface.h guiinterface.h torrentinterface.h \
+ monitorinterface.h chunkdownloadinterface.h peerinterface.h torrentfileinterface.h \
+ filetreeitem.h filetreediritem.h logmonitorinterface.h ipblockinginterface.h \
+ trackerslist.h peersource.h exitoperation.h
+libinterfaces_la_SOURCES = plugin.cpp coreinterface.cpp guiinterface.cpp \
+ prefpageinterface.cpp torrentinterface.cpp monitorinterface.cpp chunkdownloadinterface.cpp \
+ peerinterface.cpp torrentfileinterface.cpp filetreeitem.cpp filetreediritem.cpp \
+ functions.cpp logmonitorinterface.cpp ipblockinginterface.cpp trackerslist.cpp \
+ peersource.cpp exitoperation.cpp
+
+KDE_CXXFLAGS = $(USE_EXCEPTIONS) $(USE_RTTI)
diff --git a/libktorrent/interfaces/chunkdownloadinterface.cpp b/libktorrent/interfaces/chunkdownloadinterface.cpp
new file mode 100644
index 0000000..d991605
--- /dev/null
+++ b/libktorrent/interfaces/chunkdownloadinterface.cpp
@@ -0,0 +1,33 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include "chunkdownloadinterface.h"
+
+namespace kt
+{
+
+ ChunkDownloadInterface::ChunkDownloadInterface()
+ {}
+
+
+ ChunkDownloadInterface::~ChunkDownloadInterface()
+ {}
+
+
+}
diff --git a/libktorrent/interfaces/chunkdownloadinterface.h b/libktorrent/interfaces/chunkdownloadinterface.h
new file mode 100644
index 0000000..161a534
--- /dev/null
+++ b/libktorrent/interfaces/chunkdownloadinterface.h
@@ -0,0 +1,62 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef KTCHUNKDOWNLOADINTERFACE_H
+#define KTCHUNKDOWNLOADINTERFACE_H
+
+#include <qstring.h>
+#include <util/constants.h>
+
+namespace kt
+{
+
+ /**
+ * @author Joris Guisson
+ * @brief Interface for a ChunkDownload
+ *
+ * This class provides the interface for a ChunkDownload object.
+ */
+ class ChunkDownloadInterface
+ {
+ public:
+ ChunkDownloadInterface();
+ virtual ~ChunkDownloadInterface();
+
+ struct Stats
+ {
+ /// The PeerID of the current downloader
+ QString current_peer_id;
+ /// The current download speed
+ bt::Uint32 download_speed;
+ /// The index of the chunk
+ bt::Uint32 chunk_index;
+ /// The number of pieces of the chunk which have been downloaded
+ bt::Uint32 pieces_downloaded;
+ /// The total number of pieces of the chunk
+ bt::Uint32 total_pieces;
+ /// The number of assigned downloaders
+ bt::Uint32 num_downloaders;
+ };
+
+ virtual void getStats(Stats & s) = 0;
+ };
+
+}
+
+#endif
diff --git a/libktorrent/interfaces/coreinterface.cpp b/libktorrent/interfaces/coreinterface.cpp
new file mode 100644
index 0000000..cb350d8
--- /dev/null
+++ b/libktorrent/interfaces/coreinterface.cpp
@@ -0,0 +1,33 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include "coreinterface.h"
+
+namespace kt
+{
+
+ CoreInterface::CoreInterface()
+ {}
+
+
+ CoreInterface::~CoreInterface()
+ {}
+}
+
+#include "coreinterface.moc"
diff --git a/libktorrent/interfaces/coreinterface.h b/libktorrent/interfaces/coreinterface.h
new file mode 100644
index 0000000..613ba8e
--- /dev/null
+++ b/libktorrent/interfaces/coreinterface.h
@@ -0,0 +1,258 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef KTCOREINTERFACE_H
+#define KTCOREINTERFACE_H
+
+#include <kurl.h>
+#include <qobject.h>
+#include <util/constants.h>
+#include <torrent/queuemanager.h>
+
+///Stats struct
+struct CurrentStats
+{
+ bt::Uint32 download_speed;
+ bt::Uint32 upload_speed;
+ bt::Uint64 bytes_downloaded;
+ bt::Uint64 bytes_uploaded;
+};
+
+namespace bt
+{
+ class QueueManager;
+}
+namespace kt
+{
+ class TorrentInterface;
+
+ /**
+ * @author Joris Guisson
+ * @brief Interface for plugins to communicate with the application's core
+ *
+ * This interface provides the plugin with the functionality to modify
+ * the applications core, the core is responsible for managing all
+ * TorrentControl objects.
+ */
+ class CoreInterface : public QObject
+ {
+ Q_OBJECT
+ public:
+ CoreInterface();
+ virtual ~CoreInterface();
+
+ /**
+ * Set the maximum number of simultanious downloads.
+ * @param max The max num (0 == no limit)
+ */
+ virtual void setMaxDownloads(int max) = 0;
+
+ virtual void setMaxSeeds(int max) = 0;
+
+ virtual void setMaxDownloadSpeed(int v) = 0;
+ virtual void setMaxUploadSpeed(int v) = 0;
+
+ /**
+ * Set wether or not we should keep seeding after
+ * a download has finished.
+ * @param ks Keep seeding yes or no
+ */
+ virtual void setKeepSeeding(bool ks) = 0;
+
+ /**
+ * Change the data dir. This involves copying
+ * all data from the old dir to the new.
+ * This can offcourse go horribly wrong, therefore
+ * if it doesn't succeed it returns false
+ * and leaves everything where it supposed to be.
+ * @param new_dir The new directory
+ */
+ virtual bool changeDataDir(const QString & new_dir) = 0;
+
+ /**
+ * Start all, takes into account the maximum number of downloads.
+ * @param type - Weather to start downloads, seeds or both. 1=Downloads, 2=Seeds, 3=All
+ */
+ virtual void startAll(int type) = 0;
+
+ /**
+ * Stop all torrents.
+ * @param type - Weather to start downloads, seeds or both. 1=Downloads, 2=Seeds, 3=All
+ */
+ virtual void stopAll(int type) = 0;
+
+ /**
+ * Start a torrent, takes into account the maximum number of downloads.
+ * @param tc The TorrentControl
+ */
+ virtual void start(TorrentInterface* tc) = 0;
+
+ /**
+ * Stop a torrent, may start another download if it hasn't been started.
+ * @param tc The TorrentControl
+ * @param user true if user stopped the torrent, false otherwise
+ */
+ virtual void stop(TorrentInterface* tc, bool user = false) = 0;
+
+ /**
+ * Enqueue/Dequeue function. Places a torrent in queue.
+ * If the torrent is already in queue this will remove it from queue.
+ * @param tc TorrentControl pointer.
+ */
+ virtual void queue(kt::TorrentInterface* tc) = 0;
+
+ virtual bt::QueueManager* getQueueManager() = 0;
+
+ virtual CurrentStats getStats() = 0;
+
+ /**
+ * Switch the port when no torrents are running.
+ * @param port The new port
+ * @return true if we can, false if there are torrents running
+ */
+ virtual bool changePort(bt::Uint16 port) = 0;
+
+ /// Get the number of torrents running (including seeding torrents).
+ virtual bt::Uint32 getNumTorrentsRunning() const = 0;
+
+ /// Get the number of torrents not running.
+ virtual bt::Uint32 getNumTorrentsNotRunning() const = 0;
+
+ /**
+ * Load a torrent file. Pops up an error dialog
+ * if something goes wrong.
+ * @param file The torrent file
+ * @param savedir Dir to save the data
+ * @param silently Wether or not to do this silently
+ */
+ virtual bool load(const QString & file,const QString & savedir,bool silently) = 0;
+
+ /**
+ * Load a torrent file. Pops up an error dialog
+ * if something goes wrong. Will ask the user for a save location, or use
+ * the default.
+ * @param url The torrent file
+ */
+ virtual void load(const KURL& url) = 0;
+
+ /**
+ * Load a torrent file. Pops up an error dialog
+ * if something goes wrong. Will ask the user for a save location, or use
+ * the default. This will not popup a file selection dialog for multi file torrents.
+ * @param url The torrent file
+ */
+ virtual void loadSilently(const KURL& url) = 0;
+
+ /**
+ * Remove a download.This will delete all temp
+ * data from this TorrentControl And delete the
+ * TorrentControl itself. It can also potentially
+ * start a new download (when one is waiting to be downloaded).
+ * @param tc The torrent
+ * @param data_to Wether or not to delete the file data to
+ */
+ virtual void remove(TorrentInterface* tc,bool data_to) = 0;
+
+ /**
+ * Inserts IP range to be blocked into IPBlocklist
+ * @param ip QString reference to single IP or IP range. For example:
+ * single - 127.0.0.5
+ * range - 127.0.*.*
+ **/
+ virtual void addBlockedIP(QString& ip) = 0;
+
+ /**
+ * Removes IP range from IPBlocklist
+ * @param ip QString reference to single IP or IP range. For example:
+ * single - 127.0.0.5
+ * range - 127.0.*.*
+ **/
+ virtual void removeBlockedIP(QString& ip) = 0;
+
+ /**
+ * Find the next free torX dir.
+ * @return Path to the dir (including the torX part)
+ */
+ virtual QString findNewTorrentDir() const = 0;
+
+ /**
+ * Load an existing torrent, which has already a properly set up torX dir.
+ * @param tor_dir The torX dir
+ */
+ virtual void loadExistingTorrent(const QString & tor_dir) = 0;
+
+ /**
+ * Returns maximum allowed download speed.
+ */
+ virtual int getMaxDownloadSpeed() = 0;
+
+ /**
+ * Returns maximum allowed upload speed.
+ */
+ virtual int getMaxUploadSpeed() = 0;
+
+ /**
+ * Sets global paused state for all torrents (QueueManager) and stopps all torrents.
+ * No torrents will be automatically started/stopped.
+ */
+ virtual void setPausedState(bool pause) = 0;
+
+ /// Get the global share ratio limit
+ virtual float getGlobalMaxShareRatio() const = 0;
+
+ signals:
+ /**
+ * Seeing that when load returns the loading process may not have finished yet,
+ * and some code expects this. We emit this signal to notify that code of it.
+ * @param url The url which has been loaded
+ * @param success Wether or not it succeeded
+ * @param canceled Wether or not it was canceled by the user
+ */
+ void loadingFinished(const KURL & url,bool success,bool canceled);
+
+ /**
+ * A TorrentInterface was added
+ * @param tc
+ */
+ void torrentAdded(kt::TorrentInterface* tc);
+
+
+ /**
+ * A TorrentInterface was removed
+ * @param tc
+ */
+ void torrentRemoved(kt::TorrentInterface* tc);
+
+ /**
+ * A TorrentInterface has finished downloading.
+ * @param tc
+ */
+ void finished(kt::TorrentInterface* tc);
+
+ /**
+ * Torrent download is stopped by error
+ * @param tc TorrentInterface
+ * @param msg Error message
+ */
+ void torrentStoppedByError(kt::TorrentInterface* tc, QString msg);
+ };
+
+}
+
+#endif
diff --git a/libktorrent/interfaces/exitoperation.cpp b/libktorrent/interfaces/exitoperation.cpp
new file mode 100644
index 0000000..8eedb7a
--- /dev/null
+++ b/libktorrent/interfaces/exitoperation.cpp
@@ -0,0 +1,47 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include "exitoperation.h"
+
+namespace kt
+{
+
+ ExitOperation::ExitOperation()
+ {}
+
+
+ ExitOperation::~ExitOperation()
+ {}
+
+ ExitJobOperation::ExitJobOperation(KIO::Job* j)
+ {
+ connect(j,SIGNAL(result(KIO::Job*)),this,SLOT(onResult( KIO::Job* )));
+ }
+
+ ExitJobOperation::~ExitJobOperation()
+ {
+ }
+
+ void ExitJobOperation::onResult(KIO::Job* )
+ {
+ operationFinished(this);
+ }
+
+}
+#include "exitoperation.moc"
diff --git a/libktorrent/interfaces/exitoperation.h b/libktorrent/interfaces/exitoperation.h
new file mode 100644
index 0000000..edaa2fa
--- /dev/null
+++ b/libktorrent/interfaces/exitoperation.h
@@ -0,0 +1,67 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef KTEXITOPERATION_H
+#define KTEXITOPERATION_H
+
+#include <qobject.h>
+#include <kio/job.h>
+
+namespace kt
+{
+
+ /**
+ * @author Joris Guisson <joris.guisson@gmail.com>
+ *
+ * Object to derive from for operations which need to be performed at exit.
+ * The operation should emit the operationFinished signal when they are done.
+ *
+ * ExitOperation's can be used in combination with a WaitJob, to wait for a certain amount of time
+ * to give serveral ExitOperation's the time time to finish up.
+ */
+ class ExitOperation : public QObject
+ {
+ Q_OBJECT
+ public:
+ ExitOperation();
+ virtual ~ExitOperation();
+
+ /// wether or not we can do a deleteLater on the job after it has finished.
+ virtual bool deleteAllowed() const {return true;}
+ signals:
+ void operationFinished(kt::ExitOperation* opt);
+ };
+
+ /**
+ * Exit operation which waits for a KIO::Job
+ */
+ class ExitJobOperation : public ExitOperation
+ {
+ Q_OBJECT
+ public:
+ ExitJobOperation(KIO::Job* j);
+ virtual ~ExitJobOperation();
+
+ virtual bool deleteAllowed() const {return false;}
+ private slots:
+ virtual void onResult(KIO::Job* j);
+ };
+}
+
+#endif
diff --git a/libktorrent/interfaces/filetreediritem.cpp b/libktorrent/interfaces/filetreediritem.cpp
new file mode 100644
index 0000000..b294015
--- /dev/null
+++ b/libktorrent/interfaces/filetreediritem.cpp
@@ -0,0 +1,295 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <kglobal.h>
+#include <klocale.h>
+#include <kiconloader.h>
+#include <kmimetype.h>
+#include <util/functions.h>
+#include <interfaces/functions.h>
+#include <torrent/globals.h>
+#include "filetreediritem.h"
+#include "filetreeitem.h"
+#include "torrentfileinterface.h"
+#include <torrent/torrentfile.h>
+
+using namespace bt;
+
+namespace kt
+{
+
+ FileTreeDirItem::FileTreeDirItem(KListView* klv,const QString & name,FileTreeRootListener* rl)
+ : QCheckListItem(klv,QString::null,QCheckListItem::CheckBox),name(name),root_listener(rl)
+ {
+ parent = 0;
+ size = 0;
+ setPixmap(0,KGlobal::iconLoader()->loadIcon("folder",KIcon::Small));
+ setText(0,name);
+ setText(1,BytesToString(size));
+ setText(2,i18n("Yes"));
+ manual_change = true;
+ setOn(true);
+ manual_change = false;
+ }
+
+ FileTreeDirItem::FileTreeDirItem(FileTreeDirItem* parent,const QString & name)
+ : QCheckListItem(parent,QString::null,QCheckListItem::CheckBox),
+ name(name),parent(parent)
+ {
+ size = 0;
+ setPixmap(0,KGlobal::iconLoader()->loadIcon("folder",KIcon::Small));
+ setText(0,name);
+ setText(1,BytesToString(size));
+ setText(2,i18n("Yes"));
+ manual_change = true;
+ setOn(true);
+ manual_change = false;
+ }
+
+ FileTreeDirItem::~FileTreeDirItem()
+ {
+ }
+
+ void FileTreeDirItem::insert(const QString & path,kt::TorrentFileInterface & file)
+ {
+ size += file.getSize();
+ setText(1,BytesToString(size));
+ int p = path.find(bt::DirSeparator());
+ if (p == -1)
+ {
+ children.insert(path,newFileTreeItem(path,file));
+ }
+ else
+ {
+ QString subdir = path.left(p);
+ FileTreeDirItem* sd = subdirs.find(subdir);
+ if (!sd)
+ {
+ sd = newFileTreeDirItem(subdir);
+ subdirs.insert(subdir,sd);
+ }
+
+ sd->insert(path.mid(p+1),file);
+ }
+ }
+
+ void FileTreeDirItem::setAllChecked(bool on,bool keep_data)
+ {
+ if (!manual_change)
+ {
+ manual_change = true;
+ setOn(on);
+ manual_change = false;
+ }
+ // first set all the child items
+ bt::PtrMap<QString,FileTreeItem>::iterator i = children.begin();
+ while (i != children.end())
+ {
+ i->second->setChecked(on,keep_data);
+ i++;
+ }
+
+ // then recursivly move on to subdirs
+ bt::PtrMap<QString,FileTreeDirItem>::iterator j = subdirs.begin();
+ while (j != subdirs.end())
+ {
+ j->second->setAllChecked(on,keep_data);
+ j++;
+ }
+ }
+
+
+ void FileTreeDirItem::invertChecked()
+ {
+ // first set all the child items
+ bt::PtrMap<QString,FileTreeItem>::iterator i = children.begin();
+ while (i != children.end())
+ {
+ FileTreeItem* item = i->second;
+ item->setChecked(!item->isOn());
+ i++;
+ }
+
+ // then recursivly move on to subdirs
+ bt::PtrMap<QString,FileTreeDirItem>::iterator j = subdirs.begin();
+ while (j != subdirs.end())
+ {
+ j->second->invertChecked();
+ j++;
+ }
+ }
+
+ void FileTreeDirItem::stateChange(bool on)
+ {
+ if (!manual_change)
+ {
+ if (on)
+ {
+ setAllChecked(true);
+ }
+ else
+ {
+ switch (confirmationDialog())
+ {
+ case KEEP_DATA:
+ setAllChecked(false,true);
+ break;
+ case THROW_AWAY_DATA:
+ setAllChecked(false,false);
+ break;
+ case CANCELED:
+ default:
+ manual_change = true;
+ setOn(true);
+ manual_change = false;
+ return;
+ }
+ }
+ if (parent)
+ parent->childStateChange();
+ }
+ setText(2,on ? i18n("Yes") : i18n("No"));
+ }
+
+ Uint64 FileTreeDirItem::bytesToDownload() const
+ {
+ Uint64 tot = 0;
+ // first check all the child items
+ bt::PtrMap<QString,FileTreeItem>::const_iterator i = children.begin();
+ while (i != children.end())
+ {
+ const FileTreeItem* item = i->second;
+ tot += item->bytesToDownload();
+ i++;
+ }
+
+ // then recursivly move on to subdirs
+ bt::PtrMap<QString,FileTreeDirItem>::const_iterator j = subdirs.begin();
+ while (j != subdirs.end())
+ {
+ tot += j->second->bytesToDownload();
+ j++;
+ }
+ return tot;
+ }
+
+ bool FileTreeDirItem::allChildrenOn()
+ {
+ // first check all the child items
+ bt::PtrMap<QString,FileTreeItem>::iterator i = children.begin();
+ while (i != children.end())
+ {
+ FileTreeItem* item = i->second;
+ if (!item->isOn())
+ return false;
+ i++;
+ }
+
+ // then recursivly move on to subdirs
+ bt::PtrMap<QString,FileTreeDirItem>::iterator j = subdirs.begin();
+ while (j != subdirs.end())
+ {
+ if (!j->second->allChildrenOn())
+ return false;
+ j++;
+ }
+ return true;
+ }
+
+ void FileTreeDirItem::childStateChange()
+ {
+ // only set this dir on if all children are on
+ manual_change = true;
+ setOn(allChildrenOn());
+ manual_change = false;
+
+ if (parent)
+ parent->childStateChange();
+ else if (root_listener)
+ root_listener->treeItemChanged();
+
+ }
+
+ int FileTreeDirItem::compare(QListViewItem* i, int col, bool ascending) const
+ {
+ if (col == 1)
+ {
+ FileTreeDirItem* other = dynamic_cast<FileTreeDirItem*>(i);
+ if (!other)
+ return 0;
+ else
+ return (int)(size - other->size);
+ }
+ else
+ {
+ //return QCheckListItem::compare(i, col, ascending);
+ // case insensitive comparison
+ return QString::compare(text(col).lower(),i->text(col).lower());
+ }
+ }
+
+ TorrentFileInterface & FileTreeDirItem::findTorrentFile(QListViewItem* item)
+ {
+ // first check all the child items
+ TorrentFileInterface & nullfile = (TorrentFileInterface &)TorrentFile::null;
+ bt::PtrMap<QString,FileTreeItem>::iterator i = children.begin();
+ while (i != children.end())
+ {
+ FileTreeItem* file = i->second;
+ if (file == (FileTreeItem*)item)
+ return file->getTorrentFile();
+ i++;
+ }
+
+ // then recursivly move on to subdirs
+ bt::PtrMap<QString,FileTreeDirItem>::iterator j = subdirs.begin();
+ while (j != subdirs.end())
+ {
+ TorrentFileInterface & thefile = j->second->findTorrentFile(item);
+ if(!thefile.isNull())
+ return thefile;
+ j++;
+ }
+ return nullfile;
+ }
+
+ FileTreeItem* FileTreeDirItem::newFileTreeItem(const QString & name,TorrentFileInterface & file)
+ {
+ return new FileTreeItem(this,name,file);
+ }
+
+ FileTreeDirItem* FileTreeDirItem::newFileTreeDirItem(const QString & subdir)
+ {
+ return new FileTreeDirItem(this,subdir);
+ }
+
+ bt::ConfirmationResult FileTreeDirItem::confirmationDialog()
+ {
+ return bt::THROW_AWAY_DATA;
+ }
+
+ QString FileTreeDirItem::getPath() const
+ {
+ if (!parent)
+ return bt::DirSeparator();
+ else
+ return parent->getPath() + name + bt::DirSeparator();
+ }
+}
+
diff --git a/libktorrent/interfaces/filetreediritem.h b/libktorrent/interfaces/filetreediritem.h
new file mode 100644
index 0000000..00650f2
--- /dev/null
+++ b/libktorrent/interfaces/filetreediritem.h
@@ -0,0 +1,140 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef KTFILETREEDIRITEM_H
+#define KTFILETREEDIRITEM_H
+
+#include <klistview.h>
+#include <util/constants.h>
+#include <util/ptrmap.h>
+
+namespace kt
+{
+ using namespace bt;
+
+ class FileTreeItem;
+ class TorrentFileInterface;
+ class TorrentInterface;
+
+ class FileTreeRootListener
+ {
+ public:
+ /// An item in the file tree has changed his state
+ virtual void treeItemChanged() = 0;
+ };
+
+ /**
+ * @author Joris Guisson
+ *
+ * Directory item the file tree showing the files in a multifile torrent
+ */
+ class FileTreeDirItem : public QCheckListItem
+ {
+ protected:
+ QString name;
+ Uint64 size;
+ bt::PtrMap<QString,FileTreeItem> children;
+ bt::PtrMap<QString,FileTreeDirItem> subdirs;
+ FileTreeDirItem* parent;
+ bool manual_change;
+ FileTreeRootListener* root_listener;
+ public:
+ FileTreeDirItem(KListView* klv,const QString & name,FileTreeRootListener* rl = 0);
+ FileTreeDirItem(FileTreeDirItem* parent,const QString & name);
+ virtual ~FileTreeDirItem();
+
+ /// Get the path of the directory (if this is the root directory / will be returned)
+ QString getPath() const;
+
+ /**
+ * Recursively insert a TorrentFileInterface.
+ * @param path Path of file
+ * @param file File itself
+ */
+ void insert(const QString & path,kt::TorrentFileInterface & file);
+
+ /**
+ * Recursivly walk the tree to find the TorrentFile which
+ * is shown by a QListViewItem (which should be an FileTreeItem).
+ * If item can't be found or item is an FileTreeDirItem, a reference to
+ * TorrentFile::null will be returned. In which case the isNull() function
+ * of TorrentFile will return true
+ * @param item Pointer to the QListViewItem
+ * @return A reference to the TorrentFile
+ */
+ kt::TorrentFileInterface & findTorrentFile(QListViewItem* item);
+
+ /**
+ * Set all items checked or not.
+ * @param on true everything checked, false everything not checked
+ * @param keep_data In case of unchecking keep the data or not
+ */
+ void setAllChecked(bool on,bool keep_data = false);
+
+ /**
+ * Invert all items, checked items become unchecked and unchecked become checked.
+ */
+ void invertChecked();
+
+ /**
+ * Called by the child to notify the parent it's state has changed.
+ */
+ void childStateChange();
+
+ FileTreeDirItem* getParent() {return parent;}
+
+ /// Recusively get the total number of bytes to download
+ Uint64 bytesToDownload() const;
+
+ protected:
+ /**
+ * Can be overrided by subclasses, so they can use their own
+ * custom FileTreeItem's. Will be called in insert.
+ * @param name Name of the file
+ * @param file The TorrentFileInterface
+ * @return A newly created FileTreeItem
+ */
+ virtual FileTreeItem* newFileTreeItem(const QString & name,
+ TorrentFileInterface & file);
+
+
+ /**
+ * Can be overrided by subclasses, so they can use their own
+ * custom FileTreeDirItem's. Will be called in insert.
+ * @param subdir The name of the subdir
+ * @return A newly created FileTreeDirItem
+ */
+ virtual FileTreeDirItem* newFileTreeDirItem(const QString & subdir);
+
+
+ /**
+ * Subclasses should override this if they want to show a confirmation dialog.
+ * @return What to do (i.e. keep the data, get rid of it or do nothing
+ */
+ virtual bt::ConfirmationResult confirmationDialog();
+
+ private:
+ virtual void stateChange(bool on);
+ virtual int compare(QListViewItem* i, int col, bool ascending) const;
+ bool allChildrenOn();
+ };
+
+}
+
+#endif
diff --git a/libktorrent/interfaces/filetreeitem.cpp b/libktorrent/interfaces/filetreeitem.cpp
new file mode 100644
index 0000000..32f265c
--- /dev/null
+++ b/libktorrent/interfaces/filetreeitem.cpp
@@ -0,0 +1,182 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <kglobal.h>
+#include <klocale.h>
+#include <kiconloader.h>
+#include <kmimetype.h>
+#include <interfaces/functions.h>
+#include <torrent/globals.h>
+#include "filetreeitem.h"
+#include "filetreediritem.h"
+#include "torrentfileinterface.h"
+
+using namespace bt;
+
+namespace kt
+{
+
+ FileTreeItem::FileTreeItem(FileTreeDirItem* item,const QString & name,kt::TorrentFileInterface & file)
+ : QCheckListItem(item,QString::null,QCheckListItem::CheckBox),name(name),file(file)
+ {
+ parent = item;
+ manual_change = false;
+ init();
+ }
+
+ FileTreeItem::~FileTreeItem()
+ {
+ }
+
+ void FileTreeItem::setChecked(bool on,bool keep_data)
+ {
+ manual_change = true;
+ setOn(on);
+ manual_change = false;
+
+ if (!on)
+ {
+ if (keep_data)
+ file.setPriority(ONLY_SEED_PRIORITY);
+ else
+ file.setDoNotDownload(true);
+ }
+ else
+ {
+ if (file.getPriority() == ONLY_SEED_PRIORITY)
+ file.setPriority(NORMAL_PRIORITY);
+ else
+ file.setDoNotDownload(false);
+ }
+
+ updatePriorityText();
+ parent->childStateChange();
+ }
+
+ void FileTreeItem::updatePriorityText()
+ {
+ switch(file.getPriority())
+ {
+ case FIRST_PRIORITY:
+ setText(2,i18n("Yes, First"));
+ break;
+ case LAST_PRIORITY:
+ setText(2,i18n("Yes, Last"));
+ break;
+ case EXCLUDED:
+ case ONLY_SEED_PRIORITY:
+ setText(2,i18n("No"));
+ break;
+ case PREVIEW_PRIORITY:
+ break;
+ default:
+ setText(2,i18n("Yes"));
+ break;
+ }
+ }
+
+ void FileTreeItem::init()
+ {
+ manual_change = true;
+ if (file.doNotDownload() || file.getPriority() == ONLY_SEED_PRIORITY)
+ setOn(false);
+ else
+ setOn(true);
+ manual_change = false;
+ setText(0,name);
+ setText(1,BytesToString(file.getSize()));
+ updatePriorityText();
+ setPixmap(0,KMimeType::findByPath(name)->pixmap(KIcon::Small));
+ }
+
+ void FileTreeItem::stateChange(bool on)
+ {
+ if (manual_change)
+ {
+ updatePriorityText();
+ return;
+ }
+
+ if (!on)
+ {
+ switch (confirmationDialog())
+ {
+ case KEEP_DATA:
+ file.setPriority(ONLY_SEED_PRIORITY);
+ break;
+ case THROW_AWAY_DATA:
+ file.setDoNotDownload(true);
+ break;
+ case CANCELED:
+ default:
+ manual_change = true;
+ setOn(true);
+ manual_change = false;
+ return;
+ }
+ }
+ else
+ {
+ if (file.getPriority() == ONLY_SEED_PRIORITY)
+ file.setPriority(NORMAL_PRIORITY);
+ else
+ file.setDoNotDownload(false);
+
+ }
+
+ updatePriorityText();
+ parent->childStateChange();
+ }
+
+ int FileTreeItem::compare(QListViewItem* i, int col, bool ascending) const
+ {
+ if (col == 1)
+ {
+ FileTreeItem* other = dynamic_cast<FileTreeItem*>(i);
+ if (!other)
+ return 0;
+ else
+ return (int)(file.getSize() - other->file.getSize());
+ }
+ else
+ {
+ // lets sort case insensitive
+ return QString::compare(text(col).lower(),i->text(col).lower());
+ // QCheckListItem::compare(i, col, ascending);
+ }
+ }
+
+
+ ConfirmationResult FileTreeItem::confirmationDialog()
+ {
+ if (file.isPreExistingFile())
+ return KEEP_DATA;
+ else
+ return THROW_AWAY_DATA;
+ }
+
+ Uint64 FileTreeItem::bytesToDownload() const
+ {
+ if (file.doNotDownload())
+ return 0;
+ else
+ return file.getSize();
+ }
+
+}
diff --git a/libktorrent/interfaces/filetreeitem.h b/libktorrent/interfaces/filetreeitem.h
new file mode 100644
index 0000000..6f9f1b1
--- /dev/null
+++ b/libktorrent/interfaces/filetreeitem.h
@@ -0,0 +1,91 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef KTFILETREEITEM_H
+#define KTFILETREEITEM_H
+
+#include <klistview.h>
+#include <util/constants.h>
+
+using namespace bt;
+
+namespace kt
+{
+ class TorrentFileInterface;
+ class TorrentInterface;
+ class FileTreeDirItem;
+
+
+
+ /**
+ * @author Joris Guisson
+ *
+ * File item part of a tree which shows the files in a multifile torrent.
+ * This is derived from QCheckListItem, if the user checks or unchecks the box,
+ * wether or not to download a file will be changed.
+ */
+ class FileTreeItem : public QCheckListItem
+ {
+ protected:
+ QString name;
+ TorrentFileInterface & file;
+ FileTreeDirItem* parent;
+ bool manual_change;
+ public:
+ /**
+ * Constructor, set the parent, name and file
+ * @param item Parent item
+ * @param name Name of file
+ * @param file THe TorrentFileInterface
+ * @return
+ */
+ FileTreeItem(FileTreeDirItem* item,const QString & name,TorrentFileInterface & file);
+ virtual ~FileTreeItem();
+
+ /// Get a reference to the TorrentFileInterface
+ TorrentFileInterface & getTorrentFile() {return file;}
+
+ /**
+ * Set the box checked or not.
+ * @param on Checked or not
+ * @param keep_data In case of unchecking keep the data or not
+ */
+ void setChecked(bool on,bool keep_data = false);
+
+ /// Get the number of bytes to download in this file
+ Uint64 bytesToDownload() const;
+
+
+ private:
+ void init();
+ virtual void stateChange(bool on);
+ void updatePriorityText();
+
+ protected:
+ virtual int compare(QListViewItem* i, int col, bool ascending) const;
+
+ /**
+ * Subclasses should override this if they want to show a confirmation dialog.
+ * @return What to do (i.e. keep the data, get rid of it or do nothing
+ */
+ virtual bt::ConfirmationResult confirmationDialog();
+ };
+}
+
+#endif
diff --git a/libktorrent/interfaces/functions.cpp b/libktorrent/interfaces/functions.cpp
new file mode 100644
index 0000000..2c6286f
--- /dev/null
+++ b/libktorrent/interfaces/functions.cpp
@@ -0,0 +1,62 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <qdatetime.h>
+#include <klocale.h>
+#include <kglobal.h>
+#include "functions.h"
+
+using namespace bt;
+
+namespace kt
+{
+
+
+ QString BytesToString(Uint64 bytes,int precision)
+ {
+ KLocale* loc = KGlobal::locale();
+ if (bytes >= 1024 * 1024 * 1024)
+ return i18n("%1 GB").arg(loc->formatNumber(bytes / TO_GIG,precision < 0 ? 2 : precision));
+ else if (bytes >= 1024*1024)
+ return i18n("%1 MB").arg(loc->formatNumber(bytes / TO_MEG,precision < 0 ? 1 : precision));
+ else if (bytes >= 1024)
+ return i18n("%1 KB").arg(loc->formatNumber(bytes / TO_KB,precision < 0 ? 1 : precision));
+ else
+ return i18n("%1 B").arg(bytes);
+ }
+
+ QString KBytesPerSecToString(double speed,int precision)
+ {
+ KLocale* loc = KGlobal::locale();
+ return i18n("%1 KB/s").arg(loc->formatNumber(speed,precision));
+ }
+
+ QString DurationToString(Uint32 nsecs)
+ {
+ KLocale* loc = KGlobal::locale();
+ QTime t;
+ int ndays = nsecs / 86400;
+ t = t.addSecs(nsecs % 86400);
+ QString s = loc->formatTime(t,true,true);
+ if (ndays > 0)
+ s = i18n("1 day ","%n days ",ndays) + s;
+
+ return s;
+ }
+}
diff --git a/libktorrent/interfaces/functions.h b/libktorrent/interfaces/functions.h
new file mode 100644
index 0000000..1bf7178
--- /dev/null
+++ b/libktorrent/interfaces/functions.h
@@ -0,0 +1,47 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef FUNCTIONS_H
+#define FUNCTIONS_H
+
+#include <qstring.h>
+#include <util/constants.h>
+
+namespace kt
+{
+ const double TO_KB = 1024.0;
+ const double TO_MEG = (1024.0 * 1024.0);
+ const double TO_GIG = (1024.0 * 1024.0 * 1024.0);
+
+ QString BytesToString(bt::Uint64 bytes,int precision = -1);
+ QString KBytesPerSecToString(double speed,int precision = 1);
+ QString DurationToString(bt::Uint32 nsecs);
+
+ template<class T> int CompareVal(T a,T b)
+ {
+ if (a < b)
+ return -1;
+ else if (a > b)
+ return 1;
+ else
+ return 0;
+ }
+}
+
+#endif
diff --git a/libktorrent/interfaces/guiinterface.cpp b/libktorrent/interfaces/guiinterface.cpp
new file mode 100644
index 0000000..8a87d90
--- /dev/null
+++ b/libktorrent/interfaces/guiinterface.cpp
@@ -0,0 +1,54 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include "guiinterface.h"
+#include "torrentinterface.h"
+
+namespace kt
+{
+
+ GUIInterface::GUIInterface()
+ {}
+
+
+ GUIInterface::~GUIInterface()
+ {}
+
+
+ void GUIInterface::notifyViewListeners(TorrentInterface* tc)
+ {
+ QPtrList<ViewListener>::iterator i = listeners.begin();
+ while (i != listeners.end())
+ {
+ ViewListener* vl = *i;
+ vl->currentTorrentChanged(tc);
+ i++;
+ }
+ }
+
+ void GUIInterface::addViewListener(ViewListener* vl)
+ {
+ listeners.append(vl);
+ }
+
+ void GUIInterface::removeViewListener(ViewListener* vl)
+ {
+ listeners.remove(vl);
+ }
+}
diff --git a/libktorrent/interfaces/guiinterface.h b/libktorrent/interfaces/guiinterface.h
new file mode 100644
index 0000000..a263bb6
--- /dev/null
+++ b/libktorrent/interfaces/guiinterface.h
@@ -0,0 +1,218 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef KTGUIINTERFACE_H
+#define KTGUIINTERFACE_H
+
+#include <qptrlist.h>
+
+class QWidget;
+class QIconSet;
+class QString;
+class KToolBar;
+class KProgress;
+
+namespace kt
+{
+ class PrefPageInterface;
+ class Plugin;
+ class TorrentInterface;
+ class GUIInterface;
+
+ enum Position
+ {
+ LEFT, ///< New widgets will be added to the left of the old
+ RIGHT, ///< New widgets will be added to the right of the old
+ ABOVE, ///< New widgets will be added above the old
+ BELOW ///< New widgets will be added below the old
+ };
+
+ /**
+ * Small interface for classes who want to know when
+ * current torrent in the gui changes.
+ */
+ class ViewListener
+ {
+ public:
+ ViewListener() {}
+ virtual ~ViewListener() {}
+
+ virtual void currentTorrentChanged(TorrentInterface* tc) = 0;
+ };
+
+ /**
+ * Plugins wanting to add closeable tabs, should implement this interface.
+ * That way they can be notified of close requests.
+ * Not providing this interface in addTabPage means the tab cannot be closed.
+ */
+ class CloseTabListener
+ {
+ public:
+ /// By default all tabs can be closed, but this can be overridden
+ virtual bool closeAllowed(QWidget* ) {return true;}
+
+ /// THe close button was pressed for this tab, please remove it from the GUI
+ virtual void tabCloseRequest(kt::GUIInterface* gui,QWidget* tab) = 0;
+ };
+
+ /**
+ * @author Joris Guisson
+ * @brief Interface to modify the GUI
+ *
+ * This interface allows plugins and others to modify the GUI.
+ */
+ class GUIInterface
+ {
+ QPtrList<ViewListener> listeners;
+ public:
+ GUIInterface();
+ virtual ~GUIInterface();
+
+
+ /// Add a view listener.
+ void addViewListener(ViewListener* vl);
+
+ /// Remove a view listener
+ void removeViewListener(ViewListener* vl);
+
+ /// Add a progress bar tot the status bar, if one is already present this will fail and return 0
+ virtual KProgress* addProgressBarToStatusBar() = 0;
+
+ /// Remove the progress bar from the status bar
+ virtual void removeProgressBarFromStatusBar(KProgress* p) = 0;
+
+ /**
+ * Add a new tab page to the GUI
+ * @param page The widget
+ * @param icon Icon for the tab
+ * @param caption Text on the tab
+ * @param ctl For closeable tabs this pointer should be set
+ */
+ virtual void addTabPage(QWidget* page,const QIconSet & icon,
+ const QString & caption,CloseTabListener* ctl = 0) = 0;
+
+ /**
+ * Remove a tab page, does nothing if the page
+ * isn't added. Does not delete the widget.
+ * @param page The page
+ */
+ virtual void removeTabPage(QWidget* page) = 0;
+
+ /**
+ * Add a page to the preference dialog.
+ * @param page The page
+ */
+ virtual void addPrefPage(PrefPageInterface* page) = 0;
+
+
+ /**
+ * Remove a page from the preference dialog.
+ * @param page The page
+ */
+ virtual void removePrefPage(PrefPageInterface* page) = 0;
+
+ /**
+ * Change the statusbar message.
+ * @param msg The new message
+ */
+ virtual void changeStatusbar(const QString& msg) = 0;
+
+ /**
+ * Merge the GUI of a plugin.
+ * @param p The Plugin
+ */
+ virtual void mergePluginGui(Plugin* p) = 0;
+
+ /**
+ * Remove the GUI of a plugin.
+ * @param p The Plugin
+ */
+ virtual void removePluginGui(Plugin* p) = 0;
+
+ /**
+ * Embed a widget in the view in the mainview.
+ * The view and the new widget will be separated by a separator.
+ * @param w The widget
+ * @param pos How the widget will be positioned against the already present widgets
+ */
+ virtual void addWidgetInView(QWidget* w,Position pos) = 0;
+
+ /**
+ * Remove a widget added with addWidgetInView.
+ * The widget will be reparented to 0.
+ * @param w The widget
+ */
+ virtual void removeWidgetFromView(QWidget* w) = 0;
+
+ /**
+ * Add a widget below the view.
+ * @param w The widget
+ * @param icon Name of icon to use
+ * @param caption The caption to use
+ */
+ virtual void addWidgetBelowView(QWidget* w,const QString & icon,const QString & caption) = 0;
+
+ /**
+ * Remove a widget, which was added below the view.
+ * @param w The widget
+ */
+ virtual void removeWidgetBelowView(QWidget* w) = 0;
+
+ enum ToolDock
+ {
+ DOCK_LEFT,
+ DOCK_RIGHT,
+ DOCK_BOTTOM
+ };
+
+ /**
+ * Add a tool widget.
+ * @param w The widget
+ * @param icon Name of icon to use
+ * @param caption The caption to use
+ * @param dock Where to dock the widget
+ */
+ virtual void addToolWidget(QWidget* w,const QString & icon,const QString & caption,ToolDock dock) = 0;
+
+ /**
+ * Remove a tool widget.
+ * @param w The widget
+ */
+ virtual void removeToolWidget(QWidget* w) = 0;
+
+ /// Get the current torrent.
+ virtual const TorrentInterface* getCurrentTorrent() const = 0;
+
+ /// Add a toolbar
+ virtual KToolBar* addToolBar(const char* name) = 0;
+
+ /// Remove a toolbar
+ virtual void removeToolBar(KToolBar* tb) = 0;
+
+ protected:
+ /**
+ * Notifies all view listeners of the change in the current downloading TorrentInterface
+ * @param tc Pointer to current TorrentInterface
+ */
+ void notifyViewListeners(TorrentInterface* tc);
+ };
+
+}
+
+#endif
diff --git a/libktorrent/interfaces/ipblockinginterface.cpp b/libktorrent/interfaces/ipblockinginterface.cpp
new file mode 100644
index 0000000..e92e24c
--- /dev/null
+++ b/libktorrent/interfaces/ipblockinginterface.cpp
@@ -0,0 +1,33 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * ivasic@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include "ipblockinginterface.h"
+
+namespace kt
+{
+ IPBlockingInterface::IPBlockingInterface()
+ {}
+
+
+ IPBlockingInterface::~IPBlockingInterface()
+ {}
+}
+
+
diff --git a/libktorrent/interfaces/ipblockinginterface.h b/libktorrent/interfaces/ipblockinginterface.h
new file mode 100644
index 0000000..4054af9
--- /dev/null
+++ b/libktorrent/interfaces/ipblockinginterface.h
@@ -0,0 +1,48 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * ivasic@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+
+#ifndef IPBLOCKINGINTERFACE_H
+#define IPBLOCKINGINTERFACE_H
+
+class QString;
+
+namespace kt
+{
+ /**
+ * @author Ivan Vasic
+ * @brief Interface for IPBlocklist to communicate with IPBlockingPlugin
+ */
+ class IPBlockingInterface
+ {
+ public:
+ IPBlockingInterface();
+ virtual ~IPBlockingInterface();
+
+ /**
+ * This function checks if IP is listed in antip2p filter list.
+ * @return TRUE if IP should be blocked. FALSE otherwise
+ * @arg ip String representation of IP address.
+ */
+ virtual bool isBlockedIP(const QString& ip) = 0;
+
+ };
+}
+#endif
diff --git a/libktorrent/interfaces/logmonitorinterface.cpp b/libktorrent/interfaces/logmonitorinterface.cpp
new file mode 100644
index 0000000..df77a53
--- /dev/null
+++ b/libktorrent/interfaces/logmonitorinterface.cpp
@@ -0,0 +1,33 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include "logmonitorinterface.h"
+
+namespace kt
+{
+
+ LogMonitorInterface::LogMonitorInterface()
+ {}
+
+
+ LogMonitorInterface::~LogMonitorInterface()
+ {}
+
+
+}
diff --git a/libktorrent/interfaces/logmonitorinterface.h b/libktorrent/interfaces/logmonitorinterface.h
new file mode 100644
index 0000000..4fccb0e
--- /dev/null
+++ b/libktorrent/interfaces/logmonitorinterface.h
@@ -0,0 +1,50 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef KTLOGMONITORINTERFACE_H
+#define KTLOGMONITORINTERFACE_H
+
+class QString;
+
+namespace kt
+{
+
+ /**
+ * @author Joris Guisson
+ * @brief Interface for classes who which to receive which log messages are printed
+ *
+ * This class is an interface for all classes which want to know,
+ * what is written to the log.
+ */
+ class LogMonitorInterface
+ {
+ public:
+ LogMonitorInterface();
+ virtual ~LogMonitorInterface();
+
+ /**
+ * A line was written to the log file.
+ * @param line The line
+ */
+ virtual void message(const QString & line, unsigned int arg) = 0;
+ };
+
+}
+
+#endif
diff --git a/libktorrent/interfaces/monitorinterface.cpp b/libktorrent/interfaces/monitorinterface.cpp
new file mode 100644
index 0000000..d3d0c52
--- /dev/null
+++ b/libktorrent/interfaces/monitorinterface.cpp
@@ -0,0 +1,33 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include "monitorinterface.h"
+
+namespace kt
+{
+
+ MonitorInterface::MonitorInterface()
+ {}
+
+
+ MonitorInterface::~MonitorInterface()
+ {}
+
+
+}
diff --git a/libktorrent/interfaces/monitorinterface.h b/libktorrent/interfaces/monitorinterface.h
new file mode 100644
index 0000000..a199800
--- /dev/null
+++ b/libktorrent/interfaces/monitorinterface.h
@@ -0,0 +1,79 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef KTMONITORINTERFACE_H
+#define KTMONITORINTERFACE_H
+
+
+namespace kt
+{
+ class ChunkDownloadInterface;
+ class PeerInterface;
+
+ /**
+ * @author Joris Guisson
+ * @brief Interface for classes who want to monitor a TorrentInterface
+ *
+ * Classes who want to keep track of all peers currently connected for a given
+ * torrent and all chunks who are currently downloading can implement this interface.
+ */
+ class MonitorInterface
+ {
+ public:
+ MonitorInterface();
+ virtual ~MonitorInterface();
+
+ /**
+ * A peer has been added.
+ * @param peer The peer
+ */
+ virtual void peerAdded(kt::PeerInterface* peer) = 0;
+
+ /**
+ * A peer has been removed.
+ * @param peer The peer
+ */
+ virtual void peerRemoved(kt::PeerInterface* peer) = 0;
+
+ /**
+ * The download of a chunk has been started.
+ * @param cd The ChunkDownload
+ */
+ virtual void downloadStarted(kt::ChunkDownloadInterface* cd) = 0;
+
+ /**
+ * The download of a chunk has been stopped.
+ * @param cd The ChunkDownload
+ */
+ virtual void downloadRemoved(kt::ChunkDownloadInterface* cd) = 0;
+
+ /**
+ * The download has been stopped.
+ */
+ virtual void stopped() = 0;
+
+ /**
+ * The download has been deleted.
+ */
+ virtual void destroyed() = 0;
+ };
+
+}
+
+#endif
diff --git a/libktorrent/interfaces/peerinterface.cpp b/libktorrent/interfaces/peerinterface.cpp
new file mode 100644
index 0000000..a4ab246
--- /dev/null
+++ b/libktorrent/interfaces/peerinterface.cpp
@@ -0,0 +1,33 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include "peerinterface.h"
+
+namespace kt
+{
+
+ PeerInterface::PeerInterface()
+ {}
+
+
+ PeerInterface::~PeerInterface()
+ {}
+
+
+}
diff --git a/libktorrent/interfaces/peerinterface.h b/libktorrent/interfaces/peerinterface.h
new file mode 100644
index 0000000..f77d0f8
--- /dev/null
+++ b/libktorrent/interfaces/peerinterface.h
@@ -0,0 +1,91 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef KTPEERINTERFACE_H
+#define KTPEERINTERFACE_H
+
+#include <qstring.h>
+#include <util/constants.h>
+namespace kt
+{
+
+ /**
+ * @author Joris Guisson
+ * @brief Interface for a Peer
+ *
+ * This is the interface for a Peer, it allows other classes to
+ * get statistics about a Peer.
+ */
+ class PeerInterface
+ {
+ public:
+ PeerInterface();
+ virtual ~PeerInterface();
+
+ struct Stats
+ {
+ /// IP address of peer (dotted notation)
+ QString ip_address;
+ /// The client (Azureus, BitComet, ...)
+ QString client;
+ /// Download rate (bytes/s)
+ bt::Uint32 download_rate;
+ /// Upload rate (bytes/s)
+ bt::Uint32 upload_rate;
+ /// Choked or not
+ bool choked;
+ /// Snubbed or not (i.e. we haven't received a piece for a minute)
+ bool snubbed;
+ /// Percentage of file which the peer has
+ float perc_of_file;
+ /// Does this peer support DHT
+ bool dht_support;
+ /// Amount of data uploaded
+ bt::Uint64 bytes_uploaded;
+ /// Amount of data downloaded
+ bt::Uint64 bytes_downloaded;
+ /// Advanced choke algorithm score
+ double aca_score;
+ /// The evil flag is on when the peer has not choked us,
+ /// but has snubbed us and requests have timedout
+ bool evil;
+ /// Flag to indicate if this peer has an upload slot
+ bool has_upload_slot;
+ /// Wether or not this connection is encrypted
+ bool encrypted;
+ /// Number of upload requests queued
+ bt::Uint32 num_up_requests;
+ /// Number of outstanding download requests queued
+ bt::Uint32 num_down_requests;
+ /// Supports the fast extensions
+ bool fast_extensions;
+ /// Is this a peer on the local network
+ bool local;
+ /// Wether or not the peer supports the extension protocol
+ bool extension_protocol;
+ };
+
+ virtual const Stats & getStats() const = 0;
+
+ virtual void kill() = 0;
+ };
+
+}
+
+#endif
diff --git a/libktorrent/interfaces/peersource.cpp b/libktorrent/interfaces/peersource.cpp
new file mode 100644
index 0000000..18368b1
--- /dev/null
+++ b/libktorrent/interfaces/peersource.cpp
@@ -0,0 +1,64 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include "peersource.h"
+
+namespace kt
+{
+
+ PeerSource::PeerSource()
+ {}
+
+
+ PeerSource::~PeerSource()
+ {}
+
+ void PeerSource::completed()
+ {}
+
+ void PeerSource::manualUpdate()
+ {}
+
+ void PeerSource::aboutToBeDestroyed()
+ {}
+
+ void PeerSource::addPeer(const QString & ip,bt::Uint16 port,bool local)
+ {
+ PotentialPeer pp;
+ pp.ip = ip;
+ pp.port = port;
+ pp.local = local;
+ peers.append(pp);
+ }
+
+ bool PeerSource::takePotentialPeer(PotentialPeer & pp)
+ {
+ if (peers.count() > 0)
+ {
+ pp = peers.front();
+ peers.pop_front();
+ return true;
+ }
+ return false;
+ }
+
+
+
+}
+#include "peersource.moc"
diff --git a/libktorrent/interfaces/peersource.h b/libktorrent/interfaces/peersource.h
new file mode 100644
index 0000000..9c2b589
--- /dev/null
+++ b/libktorrent/interfaces/peersource.h
@@ -0,0 +1,118 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef KTPEERSOURCE_H
+#define KTPEERSOURCE_H
+
+#include <qobject.h>
+#include <qvaluelist.h>
+#include <util/constants.h>
+
+namespace bt
+{
+ class WaitJob;
+}
+
+namespace kt
+{
+ struct PotentialPeer
+ {
+ QString ip;
+ bt::Uint16 port;
+ bool local;
+
+ PotentialPeer() : port(0),local(false) {}
+ };
+
+ /**
+ * @author Joris Guisson <joris.guisson@gmail.com>
+ *
+ * This class is the base class for all classes who which to provide potential peers
+ * for torrents. PeerSources should work independently and should emit a signal when they
+ * have peers ready.
+ */
+ class PeerSource : public QObject
+ {
+ Q_OBJECT
+ public:
+ PeerSource();
+ virtual ~PeerSource();
+
+
+
+ /**
+ * Take the first PotentialPeer from the list. The item
+ * is removed from the list.
+ * @param pp PotentialPeer struct to fill
+ * @return true If there was one available, false if not
+ */
+ bool takePotentialPeer(PotentialPeer & pp);
+
+ /**
+ * Add a peer to the list of peers.
+ * @param ip The ip
+ * @param port The port
+ * @param local Wether or not the peer is on the local network
+ */
+ void addPeer(const QString & ip,bt::Uint16 port,bool local = false);
+
+ public slots:
+ /**
+ * Start gathering peers.
+ */
+ virtual void start() = 0;
+
+ /**
+ * Stop gathering peers.
+ */
+ virtual void stop(bt::WaitJob* wjob = 0) = 0;
+
+ /**
+ * The torrent has finished downloading.
+ * This is optional and should be used by HTTP and UDP tracker sources
+ * to notify the tracker.
+ */
+ virtual void completed();
+
+ /**
+ * PeerSources wanting to implement a manual update, should implement this.
+ */
+ virtual void manualUpdate();
+
+ /**
+ * The source is about to be destroyed. Subclasses can override this
+ * to clean up some things.
+ */
+ virtual void aboutToBeDestroyed();
+ signals:
+ /**
+ * This signal should be emitted when a new batch of peers is ready.
+ * @param ps The PeerSource
+ */
+ void peersReady(kt::PeerSource* ps);
+
+
+ private:
+ /// List to keep the potential peers in.
+ QValueList<PotentialPeer> peers;
+ };
+
+}
+
+#endif
diff --git a/libktorrent/interfaces/plugin.cpp b/libktorrent/interfaces/plugin.cpp
new file mode 100644
index 0000000..6354985
--- /dev/null
+++ b/libktorrent/interfaces/plugin.cpp
@@ -0,0 +1,49 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include "plugin.h"
+
+namespace kt
+{
+
+ Plugin::Plugin(QObject *parent, const char* qt_name,const QStringList & /*args*/,
+ const QString & name,const QString & gui_name,const QString & author,
+ const QString & email,const QString & description,
+ const QString & icon)
+ : KParts::Plugin(parent,qt_name),
+ name(name),author(author),email(email),description(description),icon(icon),gui_name(gui_name)
+ {
+ core = 0;
+ gui = 0;
+ loaded = false;
+ }
+
+
+ Plugin::~Plugin()
+ {}
+
+ void Plugin::guiUpdate()
+ {
+ }
+
+ void Plugin::shutdown(bt::WaitJob* )
+ {
+ }
+}
+#include "plugin.moc"
diff --git a/libktorrent/interfaces/plugin.h b/libktorrent/interfaces/plugin.h
new file mode 100644
index 0000000..ac43fbc
--- /dev/null
+++ b/libktorrent/interfaces/plugin.h
@@ -0,0 +1,152 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef KTPLUGIN_H
+#define KTPLUGIN_H
+
+#include <ktversion.h>
+#include <kparts/plugin.h>
+
+namespace bt
+{
+ class WaitJob;
+}
+
+namespace kt
+{
+ class CoreInterface;
+ class GUIInterface;
+
+ /**
+ * @author Joris Guisson
+ * @brief Base class for all plugins
+ *
+ * This is the base class for all plugins. Plugins should implement
+ * the load and unload methods, any changes made in load must be undone in
+ * unload.
+ *
+ * It's also absolutely forbidden to do any complex initialization in the constructor
+ * (setting an int to 0 is ok, creating widgets isn't).
+ * Only the name, author and description may be set in the constructor.
+ */
+ class Plugin : public KParts::Plugin
+ {
+ Q_OBJECT
+ public:
+ /**
+ * Constructor, set the name of the plugin, the name and e-mail of the author and
+ * a short description of the plugin.
+ * @param name Name of plugin
+ * @param gui_name Name to display in GUI (i18n version of name)
+ * @param author Author of plugin
+ * @param mail E-mail address of author
+ * @param description What does the plugin do
+ * @param icon Name of the plugin's icon
+ */
+ Plugin(QObject *parent,const char* qt_name,const QStringList & args,
+ const QString & name,const QString & gui_name,const QString & author,
+ const QString & email,const QString & description,
+ const QString & icon);
+ virtual ~Plugin();
+
+ /**
+ * This gets called, when the plugin gets loaded by KTorrent.
+ * Any changes made here must be later made undone, when unload is
+ * called.
+ * Upon error a bt::Error should be thrown. And the plugin should remain
+ * in an uninitialized state. The Error contains an error message, which will
+ * get show to the user.
+ */
+ virtual void load() = 0;
+
+ /**
+ * Gets called when the plugin gets unloaded.
+ * Should undo anything load did.
+ */
+ virtual void unload() = 0;
+
+ /**
+ * For plugins who need to update something, the same time as the
+ * GUI updates.
+ */
+ virtual void guiUpdate();
+
+ /**
+ * This should be implemented by plugins who need finish of some stuff which might take some time.
+ * These operations must be finished or killed by a timeout before we can proceed with unloading the plugin.
+ * @param job The WaitJob which monitors the plugin
+ */
+ virtual void shutdown(bt::WaitJob* job);
+
+ const QString & getName() const {return name;}
+ const QString & getAuthor() const {return author;}
+ const QString & getEMailAddress() const {return email;}
+ const QString & getDescription() const {return description;}
+ const QString & getIcon() const {return icon;}
+ const QString & getGuiName() const {return gui_name;}
+
+ /// Get a pointer to the CoreInterface
+ CoreInterface* getCore() {return core;}
+
+ /// Get a const pointer to the CoreInterface
+ const CoreInterface* getCore() const {return core;}
+
+ /**
+ * Set the core, used by PluginManager to set the pointer
+ * to the core.
+ * @param c Pointer to the core
+ */
+ void setCore(CoreInterface* c) {core = c;}
+
+ /// Get a pointer to the CoreInterface
+ GUIInterface* getGUI() {return gui;}
+
+ /// Get a const pointer to the CoreInterface
+ const GUIInterface* getGUI() const {return gui;}
+
+ /**
+ * Set the core, used by PluginManager to set the pointer
+ * to the core.
+ * @param c Pointer to the core
+ */
+ void setGUI(GUIInterface* c) {gui = c;}
+
+ /// See if the plugin is loaded
+ bool isLoaded() const {return loaded;}
+
+ /// Check wether the plugin matches the version of KT
+ virtual bool versionCheck(const QString & version) const = 0;
+
+ private:
+ QString name;
+ QString author;
+ QString email;
+ QString description;
+ QString icon;
+ QString gui_name;
+ CoreInterface* core;
+ GUIInterface* gui;
+ bool loaded;
+
+ friend class PluginManager;
+ };
+
+}
+
+#endif
diff --git a/libktorrent/interfaces/prefpageinterface.cpp b/libktorrent/interfaces/prefpageinterface.cpp
new file mode 100644
index 0000000..b905f07
--- /dev/null
+++ b/libktorrent/interfaces/prefpageinterface.cpp
@@ -0,0 +1,32 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include "prefpageinterface.h"
+namespace kt
+{
+ PrefPageInterface::PrefPageInterface(const QString & name,const QString & header,
+ const QPixmap & pix)
+ : pixmap(pix),itemName(name),header(header)
+ {}
+
+
+ PrefPageInterface::~PrefPageInterface()
+ {}
+}
+
diff --git a/libktorrent/interfaces/prefpageinterface.h b/libktorrent/interfaces/prefpageinterface.h
new file mode 100644
index 0000000..7d4d6dc
--- /dev/null
+++ b/libktorrent/interfaces/prefpageinterface.h
@@ -0,0 +1,84 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef PREFPAGEINTERFACE_H
+#define PREFPAGEINTERFACE_H
+
+#include <qpixmap.h>
+
+class QWidget;
+
+namespace kt
+{
+ /**
+ * @author Ivan Vasic
+ * @brief Interface to add configuration dialog page.
+ *
+ * This interface allows plugins and others to add their own pages in Configuration dialog
+ */
+ class PrefPageInterface
+ {
+ public:
+ /**
+ * Constructor, set the name, header and pixmap
+ * @param name
+ * @param header
+ * @param pix
+ */
+ PrefPageInterface(const QString & name,const QString & header,const QPixmap & pix);
+ virtual ~PrefPageInterface();
+
+ const QString& getItemName() { return itemName; }
+ const QString& getHeader() { return header; }
+ const QPixmap& getPixmap() { return pixmap; }
+
+ /**
+ * Apply the changes that have been made in the
+ * pref page. If the settings the user gave isn't valid false should be returned.
+ * This will prevent the dialog from closing.
+ * @return true if the data validates, false otherwise
+ */
+ virtual bool apply() = 0;
+
+ /**
+ * Create the actual widget.
+ * @param parent The parent of the widget
+ */
+ virtual void createWidget(QWidget* parent)=0;
+
+ /**
+ * Update all data on the widget, gets called before
+ * the preference dialog gets shown.
+ */
+ virtual void updateData() = 0;
+
+ /// Delete the widget, gets called when the page gets removed.
+ virtual void deleteWidget() = 0;
+
+ private:
+ ///Used in IconList mode. You should prefer a pixmap with size 32x32 pixels
+ QPixmap pixmap;
+ ///String used in the list or as tab item name.
+ QString itemName;
+ ///Header text use in the list modes. Ignored in Tabbed mode. If empty, the item text is used instead.
+ QString header;
+ };
+}
+#endif
+
diff --git a/libktorrent/interfaces/torrentfileinterface.cpp b/libktorrent/interfaces/torrentfileinterface.cpp
new file mode 100644
index 0000000..4cca138
--- /dev/null
+++ b/libktorrent/interfaces/torrentfileinterface.cpp
@@ -0,0 +1,44 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include "torrentfileinterface.h"
+
+namespace kt
+{
+
+ TorrentFileInterface::TorrentFileInterface(const QString & path,Uint64 size)
+ : path(path),size(size),first_chunk(0),last_chunk(0),num_chunks_downloaded(0),
+ priority(NORMAL_PRIORITY),m_emitDlStatusChanged(true),preview(false)
+ {
+ preexisting = false;
+ }
+
+
+ TorrentFileInterface::~TorrentFileInterface()
+ {}
+
+ float TorrentFileInterface::getDownloadPercentage() const
+ {
+ Uint32 num = last_chunk - first_chunk + 1;
+ return 100.0f * (float)num_chunks_downloaded / num;
+ }
+}
+
+#include "torrentfileinterface.moc"
+
diff --git a/libktorrent/interfaces/torrentfileinterface.h b/libktorrent/interfaces/torrentfileinterface.h
new file mode 100644
index 0000000..430534c
--- /dev/null
+++ b/libktorrent/interfaces/torrentfileinterface.h
@@ -0,0 +1,131 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef KTTORRENTFILEINTERFACE_H
+#define KTTORRENTFILEINTERFACE_H
+
+#include <qobject.h>
+#include <qstring.h>
+#include <util/constants.h>
+
+namespace kt
+{
+ using bt::Uint32;
+ using bt::Uint64;
+ using bt::Priority;
+ using bt::PREVIEW_PRIORITY;
+ using bt::FIRST_PRIORITY;
+ using bt::NORMAL_PRIORITY;
+ using bt::LAST_PRIORITY;
+ using bt::EXCLUDED;
+
+ /**
+ * @author Joris Guisson
+ * @brief Interface for a file in a multifile torrent
+ *
+ * This class is the interface for a file in a multifile torrent.
+ */
+ class TorrentFileInterface : public QObject
+ {
+ Q_OBJECT
+ public:
+ /**
+ * Constructor, set the path and size.
+ * @param path The path
+ * @param size The size
+ */
+ TorrentFileInterface(const QString & path,Uint64 size);
+ virtual ~TorrentFileInterface();
+
+ /// Get the path of the file
+ QString getPath() const {return path;}
+
+ /// Get the size of the file
+ Uint64 getSize() const {return size;}
+
+ /// Get the index of the first chunk in which this file lies
+ Uint32 getFirstChunk() const {return first_chunk;}
+
+ /// Get the last chunk of the file
+ Uint32 getLastChunk() const {return last_chunk;}
+
+ /// See if the TorrentFile is null.
+ bool isNull() const {return path.isNull();}
+
+ /// Set wether we have to not download this file
+ virtual void setDoNotDownload(bool dnd) = 0;
+
+ /// Wether or not we have to not download this file
+ virtual bool doNotDownload() const = 0;
+
+ /// Checks if this file is multimedial
+ virtual bool isMultimedia() const = 0;
+
+ /// Gets the current priority of the torrent
+ virtual Priority getPriority() const {return priority;}
+
+ /// Sets the priority of the torrent
+ virtual void setPriority(Priority newpriority = NORMAL_PRIORITY) = 0;
+
+ /// Wheather to emit signal when dl status changes or not.
+ virtual void setEmitDownloadStatusChanged(bool show) = 0;
+
+ /// Emits signal dlStatusChanged. Use it only with FileSelectDialog!
+ virtual void emitDownloadStatusChanged() = 0;
+
+ /// Did this file exist before the torrent was loaded by KT
+ bool isPreExistingFile() const {return preexisting;}
+
+ /// Set wether this file is preexisting
+ void setPreExisting(bool pe) {preexisting = pe;}
+
+ /// Get the % of the file which is downloaded
+ float getDownloadPercentage() const;
+
+ /// See if preview is available
+ bool isPreviewAvailable() const {return preview;}
+
+ signals:
+ /**
+ * Emitted when the download percentage has been changed.
+ * @param p The new percentage
+ */
+ void downloadPercentageChanged(float p);
+
+ /**
+ * Emitted when the preview becomes available or not.
+ * @param available
+ */
+ void previewAvailable(bool available);
+
+ protected:
+ QString path;
+ Uint64 size;
+ Uint32 first_chunk;
+ Uint32 last_chunk;
+ Uint32 num_chunks_downloaded;
+ Priority priority;
+ bool preexisting;
+ bool m_emitDlStatusChanged;
+ bool preview;
+ };
+
+}
+
+#endif
diff --git a/libktorrent/interfaces/torrentinterface.cpp b/libktorrent/interfaces/torrentinterface.cpp
new file mode 100644
index 0000000..763bd55
--- /dev/null
+++ b/libktorrent/interfaces/torrentinterface.cpp
@@ -0,0 +1,43 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include "torrentinterface.h"
+
+namespace kt
+{
+
+ float ShareRatio(const TorrentStats & stats)
+ {
+ if (stats.bytes_downloaded == 0)
+ return 0.0f;
+ else
+ return (float) stats.bytes_uploaded / (stats.bytes_downloaded /*+ stats.imported_bytes*/);
+ }
+
+
+ TorrentInterface::TorrentInterface()
+ {}
+
+
+ TorrentInterface::~TorrentInterface()
+ {}
+
+}
+
+#include "torrentinterface.moc"
diff --git a/libktorrent/interfaces/torrentinterface.h b/libktorrent/interfaces/torrentinterface.h
new file mode 100644
index 0000000..95d5766
--- /dev/null
+++ b/libktorrent/interfaces/torrentinterface.h
@@ -0,0 +1,509 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef KTTORRENTINTERFACE_H
+#define KTTORRENTINTERFACE_H
+
+#include <qobject.h>
+#include <util/constants.h>
+#include <interfaces/trackerslist.h>
+
+#include <kurl.h>
+
+namespace bt
+{
+ class BitSet;
+ class DataCheckerListener;
+ class SHA1Hash;
+ class WaitJob;
+ class PeerID;
+}
+
+namespace kt
+{
+ using bt::Uint32;
+ using bt::Uint64;
+
+ class MonitorInterface;
+ class TorrentFileInterface;
+ class PeerSource;
+
+ enum TorrentStatus
+ {
+ NOT_STARTED,
+ SEEDING_COMPLETE,
+ DOWNLOAD_COMPLETE,
+ SEEDING,
+ DOWNLOADING,
+ STALLED,
+ STOPPED,
+ ALLOCATING_DISKSPACE,
+ ERROR,
+ QUEUED,
+ CHECKING_DATA,
+ NO_SPACE_LEFT
+ };
+
+ enum TorrentStartResponse
+ {
+ START_OK,
+ USER_CANCELED,
+ NOT_ENOUGH_DISKSPACE,
+ MAX_SHARE_RATIO_REACHED,
+ BUSY_WITH_DATA_CHECK,
+ QM_LIMITS_REACHED // Max seeds or downloads reached
+ };
+
+ enum AutoStopReason
+ {
+ MAX_RATIO_REACHED,
+ MAX_SEED_TIME_REACHED
+ };
+
+ struct TorrentStats
+ {
+ /// The number of bytes imported (igore these for average speed)
+ Uint64 imported_bytes;
+ /// Total number of bytes downloaded.
+ Uint64 bytes_downloaded;
+ /// Total number of bytes uploaded.
+ Uint64 bytes_uploaded;
+ /// The number of bytes left (gets sent to the tracker)
+ Uint64 bytes_left;
+ /// The number of bytes left to download (bytes_left - excluded bytes)
+ Uint64 bytes_left_to_download;
+ /// total number of bytes in torrent
+ Uint64 total_bytes;
+ /// The total number of bytes which need to be downloaded
+ Uint64 total_bytes_to_download;
+ /// The download rate in bytes per sec
+ Uint32 download_rate;
+ /// The upload rate in bytes per sec
+ Uint32 upload_rate;
+ /// The number of peers we are connected to
+ Uint32 num_peers;
+ /// The number of chunks we are currently downloading
+ Uint32 num_chunks_downloading;
+ /// The total number of chunks
+ Uint32 total_chunks;
+ /// The number of chunks which have been downloaded
+ Uint32 num_chunks_downloaded;
+ /// Get the number of chunks which have been excluded
+ Uint32 num_chunks_excluded;
+ /// Get the number of chunks left
+ Uint32 num_chunks_left;
+ /// Size of each chunk
+ Uint32 chunk_size;
+ /// Total seeders in swarm
+ Uint32 seeders_total;
+ /// Num seeders connected to
+ Uint32 seeders_connected_to;
+ /// Total leechers in swarm
+ Uint32 leechers_total;
+ /// Num leechers connected to
+ Uint32 leechers_connected_to;
+ /// Status of the download
+ TorrentStatus status;
+ /// The status of the tracker
+ QString trackerstatus;
+ /// The number of bytes downloaded in this session
+ Uint64 session_bytes_downloaded;
+ /// The number of bytes uploaded in this session
+ Uint64 session_bytes_uploaded;
+ /// The number of bytes downloaded since the last started event, this gets sent to the tracker
+ Uint64 trk_bytes_downloaded;
+ /// The number of bytes upload since the last started event, this gets sent to the tracker
+ Uint64 trk_bytes_uploaded;
+ /// Name of the torrent
+ QString torrent_name;
+ /// Path of the dir or file where the data will get saved
+ QString output_path;
+ /// See if we are running
+ bool running;
+ /// See if the torrent has been started
+ bool started;
+ /// See if we are allowed to startup this torrent automatically.
+ bool autostart;
+ /// See if we have a multi file torrent
+ bool multi_file_torrent;
+ /// See if the torrent is stopped by error
+ bool stopped_by_error;
+ /// See if the download is completed
+ bool completed;
+ /// See if this torrent is controlled by user
+ bool user_controlled;
+ /// Maximum share ratio
+ float max_share_ratio;
+ /// Maximum seed time
+ float max_seed_time;
+ /// Private torrent (i.e. no use of DHT)
+ bool priv_torrent;
+ /// Number of corrupted chunks found since the last check
+ Uint32 num_corrupted_chunks;
+ };
+
+
+ struct DHTNode
+ {
+ QString ip;
+ bt::Uint16 port;
+ };
+
+ enum TorrentFeature
+ {
+ DHT_FEATURE,
+ UT_PEX_FEATURE // µTorrent peer exchange
+ };
+
+
+ /**
+ * @author Joris Guisson
+ * @brief Interface for an object which controls one torrent
+ *
+ * This class is the interface for an object which controls the
+ * up- and download of one torrent.
+ */
+ class TorrentInterface : public QObject
+ {
+ Q_OBJECT
+ public:
+ TorrentInterface();
+ virtual ~TorrentInterface();
+
+
+ /**
+ * Update the object, should be called periodically.
+ */
+ virtual void update() = 0;
+
+ /**
+ * Start the download of the torrent.
+ */
+ virtual void start() = 0;
+
+ /**
+ * Stop the download, closes all connections.
+ * @param user wether or not the user did this explicitly
+ * @param wjob WaitJob, used when KT is shutting down,
+ * so that we can wait for all stopped events to reach the tracker
+ */
+ virtual void stop(bool user,bt::WaitJob* wjob = 0) = 0;
+
+ /**
+ * Update the tracker, this should normally handled internally.
+ * We leave it public so that the user can do a manual announce.
+ */
+ virtual void updateTracker() = 0;
+
+ /// Get the torrent's statistics
+ const TorrentStats & getStats() const {return stats;}
+
+ /**
+ * Checks if torrent is multimedial and chunks needed for preview are downloaded
+ * @param start_chunk The index of starting chunk to check
+ * @param end_chunk The index of the last chunk to check
+ * In case of single torrent file defaults can be used (0,1)
+ **/
+ virtual bool readyForPreview(int start_chunk = 0, int end_chunk = 1) = 0;
+
+ /**
+ * Get the torX directory of this torrent. Temporary stuff like the index
+ * file get stored there.
+ */
+ virtual QString getTorDir() const = 0;
+
+ /// Get the data directory of this torrent
+ virtual QString getDataDir() const = 0;
+
+ /// Get a short error message
+ virtual QString getShortErrorMessage() const = 0;
+
+ /**
+ * Get the download running time of this torrent in seconds
+ * @return Uint32 - time in seconds
+ */
+ virtual Uint32 getRunningTimeDL() const = 0;
+
+ /**
+ * Get the upload running time of this torrent in seconds
+ * @return Uint32 - time in seconds
+ */
+ virtual Uint32 getRunningTimeUL() const = 0;
+
+ /**
+ * Change to a new data dir. If this fails
+ * we will fall back on the old directory.
+ * @param new_dir The new directory
+ * @return true upon succes
+ */
+ virtual bool changeDataDir(const QString & new_dir) = 0;
+
+ /**
+ * Change torrents output directory. If this fails we will fall back on the old directory.
+ * @param new_dir The new directory
+ * @param moveFiles Wheather to actually move the files or just change the directory without moving them.
+ * @return true upon success.
+ */
+ virtual bool changeOutputDir(const QString& new_dir, bool moveFiles = true) = 0;
+
+ /**
+ * Roll back the previous changeDataDir call.
+ * Does nothing if there was no previous changeDataDir call.
+ */
+ virtual void rollback() = 0;
+
+ /**
+ * Get a BitSet of the status of all Chunks
+ */
+ virtual const bt::BitSet & downloadedChunksBitSet() const = 0;
+
+ /**
+ * Get a BitSet of the availability of all Chunks
+ */
+ virtual const bt::BitSet & availableChunksBitSet() const = 0;
+
+ /**
+ * Get a BitSet of the excluded Chunks
+ */
+ virtual const bt::BitSet & excludedChunksBitSet() const = 0;
+
+ /**
+ * Get a bitset of only seed chunks
+ */
+ virtual const bt::BitSet & onlySeedChunksBitSet() const = 0;
+
+ /// Set the monitor
+ virtual void setMonitor(MonitorInterface* tmo) = 0;
+
+ /// Get the time to the next tracker update in seconds.
+ virtual Uint32 getTimeToNextTrackerUpdate() const = 0;
+
+ /// Get the number of files in a multifile torrent (0 if we do not have a multifile torrent)
+ virtual Uint32 getNumFiles() const = 0;
+
+ /**
+ * Get the index'th file of a multifile torrent
+ * @param index The index
+ * @return The TorrentFileInterface (isNull() will be true in case of error)
+ */
+ virtual TorrentFileInterface & getTorrentFile(Uint32 index) = 0;
+
+ ///Get a pointer to TrackersList object
+ virtual TrackersList* getTrackersList() = 0;
+
+ ///Get a pointer to TrackersList object
+ virtual const TrackersList* getTrackersList() const = 0;
+
+ ///Get the torrent queue number. Zero if not in queue
+ virtual int getPriority() const = 0;
+
+ ///Set the torrent queue number.
+ virtual void setPriority(int p) = 0;
+
+ /// Set the max share ratio
+ virtual void setMaxShareRatio(float ratio) = 0;
+
+ /// Get the max share ratio
+ virtual float getMaxShareRatio() const = 0;
+
+ /// Set the max seed time in hours (0 is no limit)
+ virtual void setMaxSeedTime(float hours) = 0;
+
+ /// Get the max seed time
+ virtual float getMaxSeedTime() const = 0;
+
+ /// Make a string of the current status
+ virtual QString statusToString() const = 0;
+
+ ///Is manual announce allowed?
+ virtual bool announceAllowed() = 0;
+
+
+ /**
+ * Returns estimated time left for finishing download. Returned value is in seconds.
+ * Uses TimeEstimator class to calculate this value.
+ */
+ virtual Uint32 getETA() = 0;
+
+ /**
+ * Verify the correctness of all data.
+ * @param lst The listener
+ * @param auto_import Wether or not this is an initial import
+ */
+ virtual void startDataCheck(bt::DataCheckerListener* lst,bool auto_import) = 0;
+
+ /**
+ * Data check has been finished, this should be called.
+ */
+ virtual void afterDataCheck() = 0;
+
+ /**
+ * Are we doing a data check on this torrent.
+ * @param finished This will be set to true if the data check is finished
+ */
+ virtual bool isCheckingData(bool & finished) const = 0;
+
+ /**
+ * Test all files and see if they are not missing.
+ * If so put them in a list
+ */
+ virtual bool hasMissingFiles(QStringList & sl) = 0;
+
+ /**
+ * Recreate missing files.
+ */
+ virtual void recreateMissingFiles() = 0;
+
+ /**
+ * Mark missing files as do not download.
+ */
+ virtual void dndMissingFiles() = 0;
+
+
+ /// Get the number of initial DHT nodes
+ virtual Uint32 getNumDHTNodes() const = 0;
+
+ /// Get a DHT node
+ virtual const DHTNode & getDHTNode(Uint32 i) const = 0;
+
+ /** Delete the data files of the torrent,
+ * they will be lost permanently
+ */
+ virtual void deleteDataFiles() = 0;
+
+ ///Checks if a seeding torrent has reached its maximum share ratio
+ virtual bool overMaxRatio() = 0;
+
+ /// Checks if a seeding torrent has reached it's max seed time
+ virtual bool overMaxSeedTime() = 0;
+
+ /// Handle an error
+ virtual void handleError(const QString & err) = 0;
+
+ /// Get the info_hash.
+ virtual const bt::SHA1Hash & getInfoHash() const = 0;
+
+ /**
+ * Add a new PeerSource
+ * @param ps
+ */
+ virtual void addPeerSource(PeerSource* ps) = 0;
+
+ /**
+ * Remove a nPeerSource
+ * @param ps
+ */
+ virtual void removePeerSource(PeerSource* ps) = 0;
+
+ /// Is a feature enabled
+ virtual bool isFeatureEnabled(TorrentFeature tf) = 0;
+
+ /// Disable or enable a feature
+ virtual void setFeatureEnabled(TorrentFeature tf,bool on) = 0;
+
+ /// Get our PeerID
+ virtual const bt::PeerID & getOwnPeerID() const = 0;
+
+ /// Set the traffic limits for this torrent
+ virtual void setTrafficLimits(Uint32 up,Uint32 down) = 0;
+
+ /// Get the traffic limits
+ virtual void getTrafficLimits(Uint32 & up,Uint32 & down) = 0;
+
+ /// Check if there is enough diskspace available for this torrent
+ virtual bool checkDiskSpace(bool emit_sig = true) = 0;
+
+ /// Are we in the process of moving files
+ virtual bool isMovingFiles() const = 0;
+ signals:
+ /**
+ * Emited when we have finished downloading.
+ * @param me The object who emitted the signal
+ */
+ void finished(kt::TorrentInterface* me);
+
+ /**
+ * Emited when a Torrent download is stopped by error
+ * @param me The object who emitted the signal
+ * @param msg Error message
+ */
+ void stoppedByError(kt::TorrentInterface* me, QString msg);
+
+ /**
+ * Emited when maximum share ratio for this torrent is changed
+ * @param me The object which emitted the signal.
+ */
+ void maxRatioChanged(kt::TorrentInterface* me);
+
+ /**
+ * Emited then torrent is stopped from seeding by KTorrent.
+ * Happens when torrent has reached maximum share ratio and maybe we'll add something more...
+ * @param me The object which emitted the signal.
+ */
+ void seedingAutoStopped(kt::TorrentInterface* me,kt::AutoStopReason reason);
+
+ /**
+ * Emitted just before the torrent is started, this should be used to do some
+ * checks on the files in the cache.
+ * @param me The torrent which emitted the signal
+ * @param ret The return value
+ */
+ void aboutToBeStarted(kt::TorrentInterface* me,bool & ret);
+
+ /**
+ * Emitted when missing files have been marked as dnd.
+ * The intention of this signal is to update the GUI.
+ * @param me The torrent which emitted the signal
+ */
+ void missingFilesMarkedDND(kt::TorrentInterface* me);
+
+ /**
+ * A corrupted chunk has been found during upload.
+ * @param me The torrent which emitted the signal
+ */
+ void corruptedDataFound(kt::TorrentInterface* me);
+
+ /**
+ * Disk is running out of space.
+ * @param me The torrent which emitted the signal
+ * @param toStop should this torrent be stopped or not
+ */
+ void diskSpaceLow(kt::TorrentInterface* me, bool toStop);
+
+ /**
+ * Torrent has been stopped
+ * @param me The torrent which emitted the signal
+ */
+ void torrentStopped(kt::TorrentInterface* me);
+
+ protected:
+ TorrentStats stats;
+ };
+
+
+ /**
+ * Calculates the share ratio of a torrent.
+ * @param stats The stats of the torrent
+ * @return The share ratio
+ */
+ float ShareRatio(const TorrentStats & stats);
+
+}
+
+#endif
diff --git a/libktorrent/interfaces/trackerslist.cpp b/libktorrent/interfaces/trackerslist.cpp
new file mode 100644
index 0000000..c119625
--- /dev/null
+++ b/libktorrent/interfaces/trackerslist.cpp
@@ -0,0 +1,51 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <torrent/torrent.h>
+#include "trackerslist.h"
+
+namespace kt
+{
+
+ TrackersList::TrackersList()
+ {
+ }
+
+
+ TrackersList::~TrackersList()
+ {
+ }
+
+ void TrackersList::merge(const bt::TrackerTier* first)
+ {
+ int tier = 1;
+ while (first)
+ {
+ KURL::List::const_iterator i = first->urls.begin();
+ while (i != first->urls.end())
+ {
+ addTracker(*i,true,tier);
+ i++;
+ }
+ tier++;
+ first = first->next;
+ }
+ }
+
+}
diff --git a/libktorrent/interfaces/trackerslist.h b/libktorrent/interfaces/trackerslist.h
new file mode 100644
index 0000000..55dc05e
--- /dev/null
+++ b/libktorrent/interfaces/trackerslist.h
@@ -0,0 +1,89 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Ivan Vasic *
+ * ivasic@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef KTTRACKERSLIST_H
+#define KTTRACKERSLIST_H
+
+#include <kurl.h>
+
+namespace bt
+{
+ struct TrackerTier;
+}
+
+namespace kt
+{
+ /**
+ * @author Ivan Vasić <ivasic@gmail.com>
+ *
+ * This interface is used to provide access to AnnounceList object which holds a list of available trackers for a torrent.
+ */
+ class TrackersList
+ {
+ public:
+ TrackersList();
+ virtual ~TrackersList();
+
+ /**
+ * Get the current tracker URL.
+ */
+ virtual KURL getTrackerURL() const = 0;
+
+ /**
+ * Gets a list of available trackers.
+ */
+ virtual KURL::List getTrackerURLs() = 0;
+
+ /**
+ * Adds a tracker URL to the list.
+ * @param url The URL
+ * @param custom Is it a custom tracker
+ * @param tier Which tier (or priority) the tracker has, tier 1 are
+ * the main trackers, tier 2 are backups ...
+ */
+ virtual void addTracker(KURL url, bool custom = true,int tier = 1) = 0;
+
+ /**
+ * Removes the tracker from the list.
+ * @param url - Tracker url.
+ */
+ virtual bool removeTracker(KURL url) = 0;
+
+ /**
+ * Sets the current tracker and does the announce.
+ * @param url - Tracker url.
+ */
+ virtual void setTracker(KURL url) = 0;
+
+ /**
+ * Restores the default tracker and does the announce.
+ */
+ virtual void restoreDefault() = 0;
+
+ /**
+ * Merge an other tracker list.
+ * @param first The first TrackerTier
+ */
+ void merge(const bt::TrackerTier* first);
+
+ };
+
+}
+
+#endif
diff --git a/libktorrent/kademlia/Makefile.am b/libktorrent/kademlia/Makefile.am
new file mode 100644
index 0000000..1b567b2
--- /dev/null
+++ b/libktorrent/kademlia/Makefile.am
@@ -0,0 +1,12 @@
+INCLUDES = -I$(srcdir)/.. $(all_includes)
+METASOURCES = AUTO
+libkademlia_la_LDFLAGS = $(all_libraries)
+noinst_LTLIBRARIES = libkademlia.la
+noinst_HEADERS = key.h node.h kbucket.h rpccall.h rpcserver.h database.h dht.h \
+ rpcmsg.h kclosestnodessearch.h nodelookup.h task.h pack.h \
+ taskmanager.h announcetask.h dhttrackerbackend.h dhtbase.h
+libkademlia_la_SOURCES = key.cpp node.cpp kbucket.cpp rpccall.cpp rpcserver.cpp \
+ database.cpp dht.cpp rpcmsg.cpp kclosestnodessearch.cpp nodelookup.cpp task.cpp \
+ pack.cpp taskmanager.cpp announcetask.cpp \
+ dhttrackerbackend.cpp dhtbase.cpp
+KDE_CXXFLAGS = $(USE_EXCEPTIONS) $(USE_RTTI)
diff --git a/libktorrent/kademlia/announcetask.cpp b/libktorrent/kademlia/announcetask.cpp
new file mode 100644
index 0000000..b7350a2
--- /dev/null
+++ b/libktorrent/kademlia/announcetask.cpp
@@ -0,0 +1,154 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <util/log.h>
+#include <torrent/globals.h>
+#include "announcetask.h"
+#include "node.h"
+#include "pack.h"
+
+using namespace bt;
+
+namespace dht
+{
+
+ AnnounceTask::AnnounceTask(Database* db,RPCServer* rpc, Node* node,const dht::Key & info_hash,bt::Uint16 port)
+ : Task(rpc, node),info_hash(info_hash),port(port),db(db)
+ {}
+
+
+ AnnounceTask::~AnnounceTask()
+ {}
+
+
+ void AnnounceTask::callFinished(RPCCall* c, MsgBase* rsp)
+ {
+ // Out() << "AnnounceTask::callFinished" << endl;
+ // if we do not have a get peers response, return
+ // announce_peer's response are just empty anyway
+ if (c->getMsgMethod() != dht::GET_PEERS)
+ return;
+
+ // it is either a GetPeersNodesRsp or a GetPeersValuesRsp
+ GetPeersRsp* gpr = dynamic_cast<GetPeersRsp*>(rsp);
+ if (!gpr)
+ return;
+
+ if (gpr->containsNodes())
+ {
+ const QByteArray & n = gpr->getData();
+ Uint32 nval = n.size() / 26;
+ for (Uint32 i = 0;i < nval;i++)
+ {
+ // add node to todo list
+ KBucketEntry e = UnpackBucketEntry(n,i*26);
+ if (!todo.contains(e) && !visited.contains(e) &&
+ todo.count() < 100)
+ {
+ todo.append(e);
+ }
+ }
+ }
+ else
+ {
+ // store the items in the database
+ const DBItemList & items = gpr->getItemList();
+ for (DBItemList::const_iterator i = items.begin();i != items.end();i++)
+ {
+ db->store(info_hash,*i);
+ // also add the items to the returned_items list
+ returned_items.append(*i);
+ }
+
+ // add the peer who responded to the answered list, so we can do an announce
+ KBucketEntry e(rsp->getOrigin(),rsp->getID());
+ if (!answered.contains(KBucketEntryAndToken(e,gpr->getToken())) && !answered_visited.contains(e))
+ {
+ answered.append(KBucketEntryAndToken(e,gpr->getToken()));
+ }
+
+ emitDataReady();
+ }
+ }
+
+ void AnnounceTask::callTimeout(RPCCall* )
+ {
+ //Out() << "AnnounceTask::callTimeout " << endl;
+ }
+
+ void AnnounceTask::update()
+ {
+/* Out() << "AnnounceTask::update " << endl;
+ Out() << "todo " << todo.count() << " ; answered " << answered.count() << endl;
+ Out() << "visited " << visited.count() << " ; answered_visited " << answered_visited.count() << endl;
+ */
+ while (!answered.empty() && canDoRequest())
+ {
+ KBucketEntryAndToken & e = answered.first();
+ if (!answered_visited.contains(e))
+ {
+ AnnounceReq* anr = new AnnounceReq(node->getOurID(),info_hash,port,e.getToken());
+ anr->setOrigin(e.getAddress());
+ rpcCall(anr);
+ answered_visited.append(e);
+ }
+ answered.pop_front();
+ }
+
+ // go over the todo list and send get_peers requests
+ // until we have nothing left
+ while (!todo.empty() && canDoRequest())
+ {
+ KBucketEntry e = todo.first();
+ // onLy send a findNode if we haven't allrready visited the node
+ if (!visited.contains(e))
+ {
+ // send a findNode to the node
+ GetPeersReq* gpr = new GetPeersReq(node->getOurID(),info_hash);
+ gpr->setOrigin(e.getAddress());
+ rpcCall(gpr);
+ visited.append(e);
+ }
+ // remove the entry from the todo list
+ todo.pop_front();
+ }
+
+ if (todo.empty() && answered.empty() && getNumOutstandingRequests() == 0 && !isFinished())
+ {
+ Out(SYS_DHT|LOG_NOTICE) << "DHT: AnnounceTask done" << endl;
+ done();
+ }
+ else if (answered_visited.count() >= dht::K)
+ {
+ // if K announces have occurred stop
+ Out(SYS_DHT|LOG_NOTICE) << "DHT: AnnounceTask done" << endl;
+ done();
+ }
+ }
+
+ bool AnnounceTask::takeItem(DBItem & item)
+ {
+ if (returned_items.empty())
+ return false;
+
+ item = returned_items.first();
+ returned_items.pop_front();
+ return true;
+ }
+}
diff --git a/libktorrent/kademlia/announcetask.h b/libktorrent/kademlia/announcetask.h
new file mode 100644
index 0000000..d6bfa7c
--- /dev/null
+++ b/libktorrent/kademlia/announcetask.h
@@ -0,0 +1,74 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef DHTANNOUNCETASK_H
+#define DHTANNOUNCETASK_H
+
+#include <task.h>
+#include "kbucket.h"
+
+namespace dht
+{
+ class Database;
+
+ class KBucketEntryAndToken : public KBucketEntry
+ {
+ Key token;
+ public:
+ KBucketEntryAndToken() {}
+ KBucketEntryAndToken(const KBucketEntry & e,const Key & token)
+ : KBucketEntry(e),token(token) {}
+ virtual ~KBucketEntryAndToken() {}
+
+ const Key & getToken() const {return token;}
+ };
+
+ /**
+ @author Joris Guisson <joris.guisson@gmail.com>
+ */
+ class AnnounceTask : public Task
+ {
+ public:
+ AnnounceTask(Database* db,RPCServer* rpc, Node* node,const dht::Key & info_hash,bt::Uint16 port);
+ virtual ~AnnounceTask();
+
+ virtual void callFinished(RPCCall* c, MsgBase* rsp);
+ virtual void callTimeout(RPCCall* c);
+ virtual void update();
+
+ /**
+ * Take one item from the returned values.
+ * Returns false if there is no item to take.
+ * @param item The item
+ * @return false if no item to take, true else
+ */
+ bool takeItem(DBItem & item);
+ private:
+ dht::Key info_hash;
+ bt::Uint16 port;
+ QValueList<KBucketEntryAndToken> answered; // nodes which have answered with values
+ QValueList<KBucketEntry> answered_visited; // nodes which have answered with values which have been visited
+ Database* db;
+ DBItemList returned_items;
+
+ };
+
+}
+
+#endif
diff --git a/libktorrent/kademlia/database.cpp b/libktorrent/kademlia/database.cpp
new file mode 100644
index 0000000..447975f
--- /dev/null
+++ b/libktorrent/kademlia/database.cpp
@@ -0,0 +1,186 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <util/functions.h>
+#include <util/log.h>
+#include <torrent/globals.h>
+#include "database.h"
+
+using namespace bt;
+
+namespace dht
+{
+ DBItem::DBItem()
+ {
+ memset(item,0,9);
+ time_stamp = bt::GetCurrentTime();
+ }
+
+ DBItem::DBItem(const bt::Uint8* ip_port)
+ {
+ memcpy(item,ip_port,6);
+ time_stamp = bt::GetCurrentTime();
+ }
+
+ DBItem::DBItem(const DBItem & it)
+ {
+ memcpy(item,it.item,6);
+ time_stamp = it.time_stamp;
+ }
+
+ DBItem::~DBItem()
+ {}
+
+ bool DBItem::expired(bt::TimeStamp now) const
+ {
+ return (now - time_stamp >= MAX_ITEM_AGE);
+ }
+
+ DBItem & DBItem::operator = (const DBItem & it)
+ {
+ memcpy(item,it.item,6);
+ time_stamp = it.time_stamp;
+ return *this;
+ }
+
+ ///////////////////////////////////////////////
+
+ Database::Database()
+ {
+ items.setAutoDelete(true);
+ }
+
+
+ Database::~Database()
+ {}
+
+ void Database::store(const dht::Key & key,const DBItem & dbi)
+ {
+ DBItemList* dbl = items.find(key);
+ if (!dbl)
+ {
+ dbl = new DBItemList();
+ items.insert(key,dbl);
+ }
+ dbl->append(dbi);
+ }
+
+ void Database::sample(const dht::Key & key,DBItemList & tdbl,bt::Uint32 max_entries)
+ {
+ DBItemList* dbl = items.find(key);
+ if (!dbl)
+ return;
+
+ if (dbl->count() < max_entries)
+ {
+ DBItemList::iterator i = dbl->begin();
+ while (i != dbl->end())
+ {
+ tdbl.append(*i);
+ i++;
+ }
+ }
+ else
+ {
+ Uint32 num_added = 0;
+ DBItemList::iterator i = dbl->begin();
+ while (i != dbl->end() && num_added < max_entries)
+ {
+ tdbl.append(*i);
+ num_added++;
+ i++;
+ }
+ }
+ }
+
+ void Database::expire(bt::TimeStamp now)
+ {
+ bt::PtrMap<dht::Key,DBItemList>::iterator itr = items.begin();
+ while (itr != items.end())
+ {
+ DBItemList* dbl = itr->second;
+ // newer keys are inserted at the back
+ // so we can stop when we hit the first key which is not expired
+ while (dbl->count() > 0 && dbl->first().expired(now))
+ {
+ dbl->pop_front();
+ }
+ itr++;
+ }
+ }
+
+ dht::Key Database::genToken(Uint32 ip,Uint16 port)
+ {
+ Uint8 tdata[14];
+ TimeStamp now = bt::GetCurrentTime();
+ // generate a hash of the ip port and the current time
+ // should prevent anybody from crapping things up
+ bt::WriteUint32(tdata,0,ip);
+ bt::WriteUint16(tdata,4,port);
+ bt::WriteUint64(tdata,6,now);
+
+ dht::Key token = SHA1Hash::generate(tdata,14);
+ // keep track of the token, tokens will expire after a while
+ tokens.insert(token,now);
+ return token;
+ }
+
+ bool Database::checkToken(const dht::Key & token,Uint32 ip,Uint16 port)
+ {
+ // the token must be in the map
+ if (!tokens.contains(token))
+ {
+ Out(SYS_DHT|LOG_DEBUG) << "Unknown token" << endl;
+ return false;
+ }
+
+ // in the map so now get the timestamp and regenerate the token
+ // using the IP and port of the sender
+ TimeStamp ts = tokens[token];
+ Uint8 tdata[14];
+ bt::WriteUint32(tdata,0,ip);
+ bt::WriteUint16(tdata,4,port);
+ bt::WriteUint64(tdata,6,ts);
+ dht::Key ct = SHA1Hash::generate(tdata,14);
+ // compare the generated token to the one received
+ if (token != ct) // not good, this peer didn't went through the proper channels
+ {
+ Out(SYS_DHT|LOG_DEBUG) << "Invalid token" << endl;
+ return false;
+ }
+ // expire the token
+ tokens.erase(token);
+ return true;
+ }
+
+ bool Database::contains(const dht::Key & key) const
+ {
+ return items.find(key) != 0;
+ }
+
+ void Database::insert(const dht::Key & key)
+ {
+ DBItemList* dbl = items.find(key);
+ if (!dbl)
+ {
+ dbl = new DBItemList();
+ items.insert(key,dbl);
+ }
+ }
+}
diff --git a/libktorrent/kademlia/database.h b/libktorrent/kademlia/database.h
new file mode 100644
index 0000000..94e6b3f
--- /dev/null
+++ b/libktorrent/kademlia/database.h
@@ -0,0 +1,129 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef DHTDATABASE_H
+#define DHTDATABASE_H
+
+#include <qmap.h>
+#include <qvaluelist.h>
+#include <util/ptrmap.h>
+#include <util/constants.h>
+#include <util/array.h>
+#include "key.h"
+
+
+namespace dht
+{
+ /// Each item may only exist for 30 minutes
+ const bt::Uint32 MAX_ITEM_AGE = 30 * 60 * 1000;
+
+ /**
+ * @author Joris Guisson
+ *
+ * Item in the database, will keep track of an IP and port combination.
+ * As well as the time it was inserted.
+ */
+ class DBItem
+ {
+ bt::Uint8 item[6];
+ bt::TimeStamp time_stamp;
+ public:
+ DBItem();
+ DBItem(const bt::Uint8* ip_port);
+ DBItem(const DBItem & item);
+ virtual ~DBItem();
+
+ /// See if the item is expired
+ bool expired(bt::TimeStamp now) const;
+
+ /// Get the data of an item
+ const bt::Uint8* getData() const {return item;}
+
+ DBItem & operator = (const DBItem & item);
+ };
+
+ typedef QValueList<DBItem> DBItemList;
+
+ /**
+ * @author Joris Guisson
+ *
+ * Class where all the key value paires get stored.
+ */
+ class Database
+ {
+ bt::PtrMap<dht::Key,DBItemList> items;
+ QMap<dht::Key,bt::TimeStamp> tokens;
+ public:
+ Database();
+ virtual ~Database();
+
+ /**
+ * Store an entry in the database
+ * @param key The key
+ * @param dbi The DBItem to store
+ */
+ void store(const dht::Key & key,const DBItem & dbi);
+
+ /**
+ * Get max_entries items from the database, which have
+ * the same key, items are taken randomly from the list.
+ * If the key is not present no items will be returned, if
+ * there are fewer then max_entries items for the key, all
+ * entries will be returned
+ * @param key The key to search for
+ * @param dbl The list to store the items in
+ * @param max_entries The maximum number entries
+ */
+ void sample(const dht::Key & key,DBItemList & dbl,bt::Uint32 max_entries);
+
+ /**
+ * Expire all items older then 30 minutes
+ * @param now The time it is now
+ * (we pass this along so we only have to calculate it once)
+ */
+ void expire(bt::TimeStamp now);
+
+ /**
+ * Generate a write token, which will give peers write access to
+ * the DB.
+ * @param ip The IP of the peer
+ * @param port The port of the peer
+ * @return A Key
+ */
+ dht::Key genToken(bt::Uint32 ip,bt::Uint16 port);
+
+ /**
+ * Check if a received token is OK.
+ * @param token The token received
+ * @param ip The ip of the sender
+ * @param port The port of the sender
+ * @return true if the token was given to this peer, false other wise
+ */
+ bool checkToken(const dht::Key & token,bt::Uint32 ip,bt::Uint16 port);
+
+ /// Test wether or not the DB contains a key
+ bool contains(const dht::Key & key) const;
+
+ /// Insert an empty item (only if it isn't already in the DB)
+ void insert(const dht::Key & key);
+ };
+
+}
+
+#endif
diff --git a/libktorrent/kademlia/dht.cpp b/libktorrent/kademlia/dht.cpp
new file mode 100644
index 0000000..1d00ab8
--- /dev/null
+++ b/libktorrent/kademlia/dht.cpp
@@ -0,0 +1,378 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <qmap.h>
+#include <kresolver.h>
+#include <util/log.h>
+#include <util/array.h>
+#include <util/functions.h>
+#include <torrent/bnode.h>
+#include <torrent/globals.h>
+#include <ksocketaddress.h>
+#include "announcetask.h"
+#include "dht.h"
+#include "node.h"
+#include "rpcserver.h"
+#include "rpcmsg.h"
+#include "kclosestnodessearch.h"
+#include "database.h"
+#include "taskmanager.h"
+#include "nodelookup.h"
+
+
+using namespace bt;
+using namespace KNetwork;
+
+namespace dht
+{
+
+
+
+ DHT::DHT() : node(0),srv(0),db(0),tman(0)
+ {
+ connect(&update_timer,SIGNAL(timeout()),this,SLOT(update()));
+ }
+
+
+ DHT::~DHT()
+ {
+ if (running)
+ stop();
+ }
+
+ void DHT::start(const QString & table,const QString & key_file,bt::Uint16 port)
+ {
+ if (running)
+ return;
+
+ if (port == 0)
+ port = 6881;
+
+ table_file = table;
+ this->port = port;
+ Out(SYS_DHT|LOG_NOTICE) << "DHT: Starting on port " << port << endl;
+ srv = new RPCServer(this,port);
+ node = new Node(srv,key_file);
+ db = new Database();
+ tman = new TaskManager();
+ expire_timer.update();
+ running = true;
+ srv->start();
+ node->loadTable(table);
+ update_timer.start(1000);
+ started();
+ }
+
+
+ void DHT::stop()
+ {
+ if (!running)
+ return;
+
+ update_timer.stop();
+ Out(SYS_DHT|LOG_NOTICE) << "DHT: Stopping " << endl;
+ srv->stop();
+ node->saveTable(table_file);
+ running = false;
+ stopped();
+ delete tman; tman = 0;
+ delete db; db = 0;
+ delete node; node = 0;
+ delete srv; srv = 0;
+ }
+
+ void DHT::ping(PingReq* r)
+ {
+ if (!running)
+ return;
+
+ // ignore requests we get from ourself
+ if (r->getID() == node->getOurID())
+ return;
+
+ Out(SYS_DHT|LOG_NOTICE) << "DHT: Sending ping response" << endl;
+ PingRsp rsp(r->getMTID(),node->getOurID());
+ rsp.setOrigin(r->getOrigin());
+ srv->sendMsg(&rsp);
+ node->recieved(this,r);
+ }
+
+
+
+ void DHT::findNode(FindNodeReq* r)
+ {
+ if (!running)
+ return;
+
+ // ignore requests we get from ourself
+ if (r->getID() == node->getOurID())
+ return;
+
+ Out(SYS_DHT|LOG_DEBUG) << "DHT: got findNode request" << endl;
+ node->recieved(this,r);
+ // find the K closest nodes and pack them
+ KClosestNodesSearch kns(r->getTarget(),K);
+
+ node->findKClosestNodes(kns);
+
+ Uint32 rs = kns.requiredSpace();
+ // create the data
+ QByteArray nodes(rs);
+ // pack the found nodes in a byte array
+ if (rs > 0)
+ kns.pack(nodes);
+
+ FindNodeRsp fnr(r->getMTID(),node->getOurID(),nodes);
+ fnr.setOrigin(r->getOrigin());
+ srv->sendMsg(&fnr);
+ }
+
+
+ void DHT::announce(AnnounceReq* r)
+ {
+ if (!running)
+ return;
+
+ // ignore requests we get from ourself
+ if (r->getID() == node->getOurID())
+ return;
+
+ Out(SYS_DHT|LOG_DEBUG) << "DHT: got announce request" << endl;
+ node->recieved(this,r);
+ // first check if the token is OK
+ dht::Key token = r->getToken();
+ if (!db->checkToken(token,r->getOrigin().ipAddress().IPv4Addr(),r->getOrigin().port()))
+ return;
+
+ // everything OK, so store the value
+ Uint8 tdata[6];
+ bt::WriteUint32(tdata,0,r->getOrigin().ipAddress().IPv4Addr());
+ bt::WriteUint16(tdata,4,r->getPort());
+ db->store(r->getInfoHash(),DBItem(tdata));
+ // send a proper response to indicate everything is OK
+ AnnounceRsp rsp(r->getMTID(),node->getOurID());
+ rsp.setOrigin(r->getOrigin());
+ srv->sendMsg(&rsp);
+ }
+
+
+
+ void DHT::getPeers(GetPeersReq* r)
+ {
+ if (!running)
+ return;
+
+ // ignore requests we get from ourself
+ if (r->getID() == node->getOurID())
+ return;
+
+ Out(SYS_DHT|LOG_DEBUG) << "DHT: got getPeers request" << endl;
+ node->recieved(this,r);
+ DBItemList dbl;
+ db->sample(r->getInfoHash(),dbl,50);
+
+ // generate a token
+ dht::Key token = db->genToken(r->getOrigin().ipAddress().IPv4Addr(),r->getOrigin().port());
+
+ if (dbl.count() == 0)
+ {
+ // if data is null do the same as when we have a findNode request
+ // find the K closest nodes and pack them
+ KClosestNodesSearch kns(r->getInfoHash(),K);
+ node->findKClosestNodes(kns);
+ Uint32 rs = kns.requiredSpace();
+ // create the data
+ QByteArray nodes(rs);
+ // pack the found nodes in a byte array
+ if (rs > 0)
+ kns.pack(nodes);
+
+ GetPeersRsp fnr(r->getMTID(),node->getOurID(),nodes,token);
+ fnr.setOrigin(r->getOrigin());
+ srv->sendMsg(&fnr);
+ }
+ else
+ {
+ // send a get peers response
+ GetPeersRsp fvr(r->getMTID(),node->getOurID(),dbl,token);
+ fvr.setOrigin(r->getOrigin());
+ srv->sendMsg(&fvr);
+ }
+ }
+
+ void DHT::response(MsgBase* r)
+ {
+ if (!running)
+ return;
+
+ node->recieved(this,r);
+ }
+
+ void DHT::error(ErrMsg* )
+ {}
+
+
+ void DHT::portRecieved(const QString & ip,bt::Uint16 port)
+ {
+ if (!running)
+ return;
+
+ Out(SYS_DHT|LOG_DEBUG) << "Sending ping request to " << ip << ":" << port << endl;
+ PingReq* r = new PingReq(node->getOurID());
+ r->setOrigin(KInetSocketAddress(ip,port));
+ srv->doCall(r);
+ }
+
+ bool DHT::canStartTask() const
+ {
+ // we can start a task if we have less then 7 runnning and
+ // there are at least 16 RPC slots available
+ if (tman->getNumTasks() >= 7)
+ return false;
+ else if (256 - srv->getNumActiveRPCCalls() <= 16)
+ return false;
+
+ return true;
+ }
+
+ AnnounceTask* DHT::announce(const bt::SHA1Hash & info_hash,bt::Uint16 port)
+ {
+ if (!running)
+ return 0;
+
+ KClosestNodesSearch kns(info_hash,K);
+ node->findKClosestNodes(kns);
+ if (kns.getNumEntries() > 0)
+ {
+ Out(SYS_DHT|LOG_NOTICE) << "DHT: Doing announce " << endl;
+ AnnounceTask* at = new AnnounceTask(db,srv,node,info_hash,port);
+ at->start(kns,!canStartTask());
+ tman->addTask(at);
+ if (!db->contains(info_hash))
+ db->insert(info_hash);
+ return at;
+ }
+
+ return 0;
+ }
+
+ NodeLookup* DHT::refreshBucket(const dht::Key & id,KBucket & bucket)
+ {
+ if (!running)
+ return 0;
+
+ KClosestNodesSearch kns(id,K);
+ bucket.findKClosestNodes(kns);
+ bucket.updateRefreshTimer();
+ if (kns.getNumEntries() > 0)
+ {
+ Out(SYS_DHT|LOG_DEBUG) << "DHT: refreshing bucket " << endl;
+ NodeLookup* nl = new NodeLookup(id,srv,node);
+ nl->start(kns,!canStartTask());
+ tman->addTask(nl);
+ return nl;
+ }
+
+ return 0;
+ }
+
+ NodeLookup* DHT::findNode(const dht::Key & id)
+ {
+ if (!running)
+ return 0;
+
+ KClosestNodesSearch kns(id,K);
+ node->findKClosestNodes(kns);
+ if (kns.getNumEntries() > 0)
+ {
+ Out(SYS_DHT|LOG_DEBUG) << "DHT: finding node " << endl;
+ NodeLookup* at = new NodeLookup(id,srv,node);
+ at->start(kns,!canStartTask());
+ tman->addTask(at);
+ return at;
+ }
+
+ return 0;
+ }
+
+ void DHT::update()
+ {
+ if (!running)
+ return;
+
+ if (expire_timer.getElapsedSinceUpdate() > 5*60*1000)
+ {
+ db->expire(bt::GetCurrentTime());
+ expire_timer.update();
+ }
+
+ node->refreshBuckets(this);
+ tman->removeFinishedTasks(this);
+ stats.num_tasks = tman->getNumTasks() + tman->getNumQueuedTasks();
+ stats.num_peers = node->getNumEntriesInRoutingTable();
+ }
+
+ void DHT::timeout(const MsgBase* r)
+ {
+ node->onTimeout(r);
+ }
+
+ void DHT::addDHTNode(const QString & host,Uint16 hport)
+ {
+ if (!running)
+ return;
+
+ KResolverResults res = KResolver::resolve(host,QString::number(hport));
+ if (res.count() > 0)
+ {
+ srv->ping(node->getOurID(),res.front().address());
+ }
+ }
+
+ QMap<QString, int> DHT::getClosestGoodNodes(int maxNodes)
+ {
+ QMap<QString, int> map;
+
+ if(!node)
+ return map;
+
+ int max = 0;
+ KClosestNodesSearch kns(node->getOurID(), maxNodes*2);
+ node->findKClosestNodes(kns);
+
+ KClosestNodesSearch::Itr it;
+ for(it = kns.begin(); it != kns.end(); ++it)
+ {
+ KBucketEntry e = it->second;
+
+ if(!e.isGood())
+ continue;
+
+ KInetSocketAddress a = e.getAddress();
+
+ map.insert(a.ipAddress().toString(), a.port());
+ if(++max >= maxNodes)
+ break;
+ }
+
+ return map;
+ }
+}
+
+#include "dht.moc"
diff --git a/libktorrent/kademlia/dht.h b/libktorrent/kademlia/dht.h
new file mode 100644
index 0000000..8642836
--- /dev/null
+++ b/libktorrent/kademlia/dht.h
@@ -0,0 +1,136 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef DHTDHT_H
+#define DHTDHT_H
+
+#include <qtimer.h>
+#include <qstring.h>
+#include <qmap.h>
+#include <util/constants.h>
+#include <util/timer.h>
+#include "key.h"
+#include "dhtbase.h"
+
+namespace bt
+{
+ class SHA1Hash;
+}
+
+namespace KNetwork
+{
+ class KInetSocketAddress;
+}
+
+namespace dht
+{
+ class Node;
+ class RPCServer;
+ class PingReq;
+ class FindNodeReq;
+ class FindValueReq;
+ class StoreValueReq;
+ class GetPeersReq;
+ class MsgBase;
+ class ErrMsg;
+ class MsgBase;
+ class AnnounceReq;
+ class Database;
+ class TaskManager;
+ class Task;
+ class AnnounceTask;
+ class NodeLookup;
+ class KBucket;
+
+ /**
+ @author Joris Guisson <joris.guisson@gmail.com>
+ */
+ class DHT : public DHTBase
+ {
+ Q_OBJECT
+ public:
+ DHT();
+ virtual ~DHT();
+
+ void ping(PingReq* r);
+ void findNode(FindNodeReq* r);
+ void response(MsgBase* r);
+ void getPeers(GetPeersReq* r);
+ void announce(AnnounceReq* r);
+ void error(ErrMsg* r);
+ void timeout(const MsgBase* r);
+
+ /**
+ * A Peer has received a PORT message, and uses this function to alert the DHT of it.
+ * @param ip The IP of the peer
+ * @param port The port in the PORT message
+ */
+ void portRecieved(const QString & ip,bt::Uint16 port);
+
+ /**
+ * Do an announce on the DHT network
+ * @param info_hash The info_hash
+ * @param port The port
+ * @return The task which handles this
+ */
+ AnnounceTask* announce(const bt::SHA1Hash & info_hash,bt::Uint16 port);
+
+ /**
+ * Refresh a bucket using a find node task.
+ * @param id The id
+ * @param bucket The bucket to refresh
+ */
+ NodeLookup* refreshBucket(const dht::Key & id,KBucket & bucket);
+
+ /**
+ * Do a NodeLookup.
+ * @param id The id of the key to search
+ */
+ NodeLookup* findNode(const dht::Key & id);
+
+ /// See if it is possible to start a task
+ bool canStartTask() const;
+
+ void start(const QString & table,const QString & key_file,bt::Uint16 port);
+ void stop();
+ void addDHTNode(const QString & host,bt::Uint16 hport);
+
+ /**
+ * Returns maxNodes number of <IP address, port> nodes
+ * that are closest to ourselves and are good.
+ * @param maxNodes maximum nr of nodes in QMap to return.
+ */
+ QMap<QString, int> getClosestGoodNodes(int maxNodes);
+
+ private slots:
+ void update();
+
+ private:
+ Node* node;
+ RPCServer* srv;
+ Database* db;
+ TaskManager* tman;
+ bt::Timer expire_timer;
+ QString table_file;
+ QTimer update_timer;
+ };
+
+}
+
+#endif
diff --git a/libktorrent/kademlia/dhtbase.cpp b/libktorrent/kademlia/dhtbase.cpp
new file mode 100644
index 0000000..b0ff582
--- /dev/null
+++ b/libktorrent/kademlia/dhtbase.cpp
@@ -0,0 +1,37 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include "dhtbase.h"
+
+namespace dht
+{
+
+ DHTBase::DHTBase() : running(false),port(0)
+ {
+ stats.num_peers = 0;
+ stats.num_tasks = 0;
+ }
+
+
+ DHTBase::~DHTBase()
+ {}
+}
+
+#include "dhtbase.moc"
+
diff --git a/libktorrent/kademlia/dhtbase.h b/libktorrent/kademlia/dhtbase.h
new file mode 100644
index 0000000..dfa880a
--- /dev/null
+++ b/libktorrent/kademlia/dhtbase.h
@@ -0,0 +1,129 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef DHTDHTBASE_H
+#define DHTDHTBASE_H
+
+#include <qobject.h>
+#include <util/constants.h>
+
+class QString;
+
+namespace bt
+{
+ class SHA1Hash;
+}
+
+namespace dht
+{
+ class AnnounceTask;
+
+ struct Stats
+ {
+ /// number of peers in the routing table
+ bt::Uint32 num_peers;
+ /// Number of running tasks
+ bt::Uint32 num_tasks;
+ };
+
+ /**
+ * @author Joris Guisson <joris.guisson@gmail.com>
+ *
+ * Interface for DHT class, this is to keep other things separate from the inner workings
+ * of the DHT.
+ */
+ class DHTBase : public QObject
+ {
+ Q_OBJECT
+ public:
+ DHTBase();
+ virtual ~DHTBase();
+
+
+ /**
+ * Start the DHT
+ * @param table File where the save table is located
+ * @param key_file The file where the key is stored
+ * @param port The port to use
+ */
+ virtual void start(const QString & table,const QString & key_file,bt::Uint16 port) = 0;
+
+ /**
+ * Stop the DHT
+ */
+ virtual void stop() = 0;
+
+ /**
+ * Update the DHT
+ */
+ virtual void update() = 0;
+
+ /**
+ * A Peer has received a PORT message, and uses this function to alert the DHT of it.
+ * @param ip The IP of the peer
+ * @param port The port in the PORT message
+ */
+ virtual void portRecieved(const QString & ip,bt::Uint16 port) = 0;
+
+ /**
+ * Do an announce on the DHT network
+ * @param info_hash The info_hash
+ * @param port The port
+ * @return The task which handles this
+ */
+ virtual AnnounceTask* announce(const bt::SHA1Hash & info_hash,bt::Uint16 port) = 0;
+
+ /**
+ * See if the DHT is running.
+ */
+ bool isRunning() const {return running;}
+
+ /// Get the DHT port
+ bt::Uint16 getPort() const {return port;}
+
+ /// Get statistics about the DHT
+ const dht::Stats & getStats() const {return stats;}
+
+ /**
+ * Add a DHT node. This node shall be pinged immediately.
+ * @param host The hostname or ip
+ * @param hport The port of the host
+ */
+ virtual void addDHTNode(const QString & host,bt::Uint16 hport) = 0;
+
+ /**
+ * Returns maxNodes number of <IP address, port> nodes
+ * that are closest to ourselves and are good.
+ * @param maxNodes maximum nr of nodes in QMap to return.
+ */
+ virtual QMap<QString, int> getClosestGoodNodes(int maxNodes) = 0;
+
+ signals:
+ void started();
+ void stopped();
+
+ protected:
+ bool running;
+ bt::Uint16 port;
+ dht::Stats stats;
+ };
+
+}
+
+#endif
diff --git a/libktorrent/kademlia/dhttrackerbackend.cpp b/libktorrent/kademlia/dhttrackerbackend.cpp
new file mode 100644
index 0000000..c90e6f7
--- /dev/null
+++ b/libktorrent/kademlia/dhttrackerbackend.cpp
@@ -0,0 +1,154 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <kurl.h>
+#include <qhostaddress.h>
+#include <util/log.h>
+#include <util/functions.h>
+#include <torrent/globals.h>
+#include <torrent/server.h>
+#include <torrent/peermanager.h>
+#include <interfaces/torrentinterface.h>
+#include "dhttrackerbackend.h"
+#include "dht.h"
+#include "announcetask.h"
+
+using namespace bt;
+
+namespace dht
+{
+
+ DHTTrackerBackend::DHTTrackerBackend(DHTBase & dh_table,kt::TorrentInterface* tor)
+ : dh_table(dh_table),curr_task(0),tor(tor)
+ {
+ connect(&timer,SIGNAL(timeout()),this,SLOT(onTimeout()));
+ connect(&dh_table,SIGNAL(started()),this,SLOT(manualUpdate()));
+ connect(&dh_table,SIGNAL(stopped()),this,SLOT(dhtStopped()));
+ started = false;
+ }
+
+
+ DHTTrackerBackend::~DHTTrackerBackend()
+ {
+ if (curr_task)
+ curr_task->kill();
+ }
+
+ void DHTTrackerBackend::start()
+ {
+ started = true;
+ if (dh_table.isRunning())
+ doRequest();
+ }
+
+ void DHTTrackerBackend::dhtStopped()
+ {
+ stop(0);
+ curr_task = 0;
+ }
+
+ void DHTTrackerBackend::stop(bt::WaitJob*)
+ {
+ started = false;
+ if (curr_task)
+ {
+ curr_task->kill();
+ timer.stop();
+ }
+ }
+
+ void DHTTrackerBackend::manualUpdate()
+ {
+ if (dh_table.isRunning() && started)
+ doRequest();
+ }
+
+
+ bool DHTTrackerBackend::doRequest()
+ {
+ if (!dh_table.isRunning())
+ return false;
+
+ if (curr_task)
+ return true;
+
+ const SHA1Hash & info_hash = tor->getInfoHash();
+ Uint16 port = bt::Globals::instance().getServer().getPortInUse();
+ curr_task = dh_table.announce(info_hash,port);
+ if (curr_task)
+ {
+ for (Uint32 i = 0;i < tor->getNumDHTNodes();i++)
+ {
+ const kt::DHTNode & n = tor->getDHTNode(i);
+ curr_task->addDHTNode(n.ip,n.port);
+ }
+ connect(curr_task,SIGNAL(dataReady( Task* )),this,SLOT(onDataReady( Task* )));
+ connect(curr_task,SIGNAL(finished( Task* )),this,SLOT(onFinished( Task* )));
+
+ return true;
+ }
+
+ return false;
+ }
+
+ void DHTTrackerBackend::onFinished(Task* t)
+ {
+ if (curr_task == t)
+ {
+ onDataReady(curr_task);
+ curr_task = 0;
+ // do another announce in 5 minutes or so
+ timer.start(5 * 60 * 1000,true);
+ }
+ }
+
+ void DHTTrackerBackend::onDataReady(Task* t)
+ {
+ if (curr_task == t)
+ {
+ Uint32 cnt = 0;
+ DBItem item;
+ while (curr_task->takeItem(item))
+ {
+ Uint16 port = bt::ReadUint16(item.getData(),4);
+ QString ip = QHostAddress(ReadUint32(item.getData(),0)).toString();
+
+ addPeer(ip,port);
+ cnt++;
+ }
+
+ if (cnt)
+ {
+ Out(SYS_DHT|LOG_NOTICE) <<
+ QString("DHT: Got %1 potential peers for torrent %2")
+ .arg(cnt).arg(tor->getStats().torrent_name) << endl;
+ peersReady(this);
+ }
+ }
+ }
+
+ void DHTTrackerBackend::onTimeout()
+ {
+ if (dh_table.isRunning() && started)
+ doRequest();
+ }
+
+}
+
+#include "dhttrackerbackend.moc"
diff --git a/libktorrent/kademlia/dhttrackerbackend.h b/libktorrent/kademlia/dhttrackerbackend.h
new file mode 100644
index 0000000..355aab9
--- /dev/null
+++ b/libktorrent/kademlia/dhttrackerbackend.h
@@ -0,0 +1,75 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef DHTDHTTRACKERBACKEND_H
+#define DHTDHTTRACKERBACKEND_H
+
+#include <qtimer.h>
+#include <interfaces/peersource.h>
+#include "task.h"
+
+namespace kt
+{
+ class TorrentInterface;
+}
+
+namespace bt
+{
+ class WaitJob;
+}
+
+
+namespace dht
+{
+ class DHTBase;
+ class AnnounceTask;
+
+
+ /**
+ @author Joris Guisson <joris.guisson@gmail.com>
+ */
+ class DHTTrackerBackend : public kt::PeerSource
+ {
+ Q_OBJECT
+ public:
+ DHTTrackerBackend(DHTBase & dh_table,kt::TorrentInterface* tor);
+ virtual ~DHTTrackerBackend();
+
+ virtual void start();
+ virtual void stop(bt::WaitJob* wjob = 0);
+ virtual void manualUpdate();
+
+ private slots:
+ void onTimeout();
+ bool doRequest();
+ void onDataReady(Task* t);
+ void onFinished(Task* t);
+ void dhtStopped();
+
+ private:
+ DHTBase & dh_table;
+ AnnounceTask* curr_task;
+ kt::TorrentInterface* tor;
+ QTimer timer;
+ bool started;
+ };
+
+}
+
+#endif
diff --git a/libktorrent/kademlia/kbucket.cpp b/libktorrent/kademlia/kbucket.cpp
new file mode 100644
index 0000000..fb60d1b
--- /dev/null
+++ b/libktorrent/kademlia/kbucket.cpp
@@ -0,0 +1,355 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <ksocketaddress.h>
+#include <util/file.h>
+#include <util/log.h>
+#include <util/functions.h>
+#include <netinet/in.h>
+#include "kbucket.h"
+#include "kclosestnodessearch.h"
+#include "rpcserver.h"
+#include "node.h"
+
+using namespace KNetwork;
+using namespace bt;
+
+namespace dht
+{
+ KBucketEntry::KBucketEntry()
+ {
+ last_responded = bt::GetCurrentTime();
+ failed_queries = 0;
+ questionable_pings = 0;
+ }
+
+ KBucketEntry::KBucketEntry(const KInetSocketAddress & addr,const Key & id)
+ : addr(addr),node_id(id)
+ {
+ last_responded = bt::GetCurrentTime();
+ failed_queries = 0;
+ questionable_pings = 0;
+ }
+
+ KBucketEntry::KBucketEntry(const KBucketEntry & other)
+ : addr(other.addr),node_id(other.node_id),
+ last_responded(other.last_responded),failed_queries(other.failed_queries),questionable_pings(other.questionable_pings)
+ {}
+
+
+ KBucketEntry::~KBucketEntry()
+ {}
+
+ KBucketEntry & KBucketEntry::operator = (const KBucketEntry & other)
+ {
+ addr = other.addr;
+ node_id = other.node_id;
+ last_responded = other.last_responded;
+ failed_queries = other.failed_queries;
+ questionable_pings = other.questionable_pings;
+ return *this;
+ }
+
+ bool KBucketEntry::operator == (const KBucketEntry & entry) const
+ {
+ return addr == entry.addr && node_id == entry.node_id;
+ }
+
+ bool KBucketEntry::isGood() const
+ {
+ if (bt::GetCurrentTime() - last_responded > 15 * 60 * 1000)
+ return false;
+ else
+ return true;
+ }
+
+ bool KBucketEntry::isQuestionable() const
+ {
+ if (bt::GetCurrentTime() - last_responded > 15 * 60 * 1000)
+ return true;
+ else
+ return false;
+ }
+
+
+ bool KBucketEntry::isBad() const
+ {
+ if (isGood())
+ return false;
+
+ return failed_queries > 2 || questionable_pings > 2;
+ }
+
+ void KBucketEntry::hasResponded()
+ {
+ last_responded = bt::GetCurrentTime();
+ failed_queries = 0; // reset failed queries
+ questionable_pings = 0;
+ }
+
+
+ //////////////////////////////////////////////////////////
+
+ KBucket::KBucket(Uint32 idx,RPCServer* srv,Node* node)
+ : idx(idx),srv(srv),node(node)
+ {
+ last_modified = bt::GetCurrentTime();
+ refresh_task = 0;
+ }
+
+
+ KBucket::~KBucket()
+ {}
+
+ void KBucket::insert(const KBucketEntry & entry)
+ {
+ QValueList<KBucketEntry>::iterator i = entries.find(entry);
+
+ // If in the list, move it to the end
+ if (i != entries.end())
+ {
+ KBucketEntry & e = *i;
+ e.hasResponded();
+ last_modified = bt::GetCurrentTime();
+ entries.remove(i);
+ entries.append(entry);
+ return;
+ }
+
+ // insert if not already in the list and we still have room
+ if (i == entries.end() && entries.count() < dht::K)
+ {
+ entries.append(entry);
+ last_modified = bt::GetCurrentTime();
+ }
+ else if (!replaceBadEntry(entry))
+ {
+ // ping questionable nodes when replacing a bad one fails
+ pingQuestionable(entry);
+ }
+ }
+
+ void KBucket::onResponse(RPCCall* c,MsgBase* rsp)
+ {
+ last_modified = bt::GetCurrentTime();
+
+ if (!pending_entries_busy_pinging.contains(c))
+ return;
+
+ KBucketEntry entry = pending_entries_busy_pinging[c];
+ pending_entries_busy_pinging.erase(c); // call is done so erase it
+
+ // we have a response so try to find the next bad or questionable node
+ // if we do not have room see if we can get rid of some bad peers
+ if (!replaceBadEntry(entry)) // if no bad peers ping a questionable one
+ pingQuestionable(entry);
+
+ }
+
+
+
+ void KBucket::onTimeout(RPCCall* c)
+ {
+ if (!pending_entries_busy_pinging.contains(c))
+ return;
+
+ KBucketEntry entry = pending_entries_busy_pinging[c];
+
+ // replace the entry which timed out
+ QValueList<KBucketEntry>::iterator i;
+ for (i = entries.begin();i != entries.end();i++)
+ {
+ KBucketEntry & e = *i;
+ if (e.getAddress() == c->getRequest()->getOrigin())
+ {
+ last_modified = bt::GetCurrentTime();
+ entries.remove(i);
+ entries.append(entry);
+ break;
+ }
+ }
+ pending_entries_busy_pinging.erase(c); // call is done so erase it
+ // see if we can do another pending entry
+ if (pending_entries_busy_pinging.count() < 2 && pending_entries.count() > 0)
+ {
+ KBucketEntry pe = pending_entries.front();
+ pending_entries.pop_front();
+ if (!replaceBadEntry(pe)) // if no bad peers ping a questionable one
+ pingQuestionable(pe);
+ }
+ }
+
+ void KBucket::pingQuestionable(const KBucketEntry & replacement_entry)
+ {
+ if (pending_entries_busy_pinging.count() >= 2)
+ {
+ pending_entries.append(replacement_entry); // lets not have to many pending_entries calls going on
+ return;
+ }
+
+ QValueList<KBucketEntry>::iterator i;
+ // we haven't found any bad ones so try the questionable ones
+ for (i = entries.begin();i != entries.end();i++)
+ {
+ KBucketEntry & e = *i;
+ if (e.isQuestionable())
+ {
+ Out(SYS_DHT|LOG_DEBUG) << "Pinging questionable node : " << e.getAddress().toString() << endl;
+ PingReq* p = new PingReq(node->getOurID());
+ p->setDestination(e.getAddress());
+ RPCCall* c = srv->doCall(p);
+ if (c)
+ {
+ e.onPingQuestionable();
+ c->addListener(this);
+ // add the pending entry
+ pending_entries_busy_pinging.insert(c,replacement_entry);
+ return;
+ }
+ }
+ }
+ }
+
+ bool KBucket::replaceBadEntry(const KBucketEntry & entry)
+ {
+ QValueList<KBucketEntry>::iterator i;
+ for (i = entries.begin();i != entries.end();i++)
+ {
+ KBucketEntry & e = *i;
+ if (e.isBad())
+ {
+ // bad one get rid of it
+ last_modified = bt::GetCurrentTime();
+ entries.remove(i);
+ entries.append(entry);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ bool KBucket::contains(const KBucketEntry & entry) const
+ {
+ return entries.contains(entry);
+ }
+
+ void KBucket::findKClosestNodes(KClosestNodesSearch & kns)
+ {
+ QValueList<KBucketEntry>::iterator i = entries.begin();
+ while (i != entries.end())
+ {
+ kns.tryInsert(*i);
+ i++;
+ }
+ }
+
+ bool KBucket::onTimeout(const KInetSocketAddress & addr)
+ {
+ QValueList<KBucketEntry>::iterator i;
+
+ for (i = entries.begin();i != entries.end();i++)
+ {
+ KBucketEntry & e = *i;
+ if (e.getAddress() == addr)
+ {
+ e.requestTimeout();
+ return true;
+ }
+ }
+ return false;
+ }
+
+ bool KBucket::needsToBeRefreshed() const
+ {
+ bt::TimeStamp now = bt::GetCurrentTime();
+ if (last_modified > now)
+ {
+ last_modified = now;
+ return false;
+ }
+
+ return !refresh_task && entries.count() > 0 && (now - last_modified > BUCKET_REFRESH_INTERVAL);
+ }
+
+ void KBucket::updateRefreshTimer()
+ {
+ last_modified = bt::GetCurrentTime();
+ }
+
+
+
+ void KBucket::save(bt::File & fptr)
+ {
+ BucketHeader hdr;
+ hdr.magic = BUCKET_MAGIC_NUMBER;
+ hdr.index = idx;
+ hdr.num_entries = entries.count();
+
+ fptr.write(&hdr,sizeof(BucketHeader));
+ QValueList<KBucketEntry>::iterator i;
+ for (i = entries.begin();i != entries.end();i++)
+ {
+ KBucketEntry & e = *i;
+ const KIpAddress & ip = e.getAddress().ipAddress();
+ Uint8 tmp[26];
+ bt::WriteUint32(tmp,0,ip.IPv4Addr());
+ bt::WriteUint16(tmp,4,e.getAddress().port());
+ memcpy(tmp+6,e.getID().getData(),20);
+ fptr.write(tmp,26);
+ }
+ }
+
+ void KBucket::load(bt::File & fptr,const BucketHeader & hdr)
+ {
+ if (hdr.num_entries > K)
+ return;
+
+ for (Uint32 i = 0;i < hdr.num_entries;i++)
+ {
+ Uint8 tmp[26];
+ if (fptr.read(tmp,26) != 26)
+ return;
+
+ entries.append(KBucketEntry(
+ KInetSocketAddress(
+ KIpAddress(bt::ReadUint32(tmp,0)),
+ bt::ReadUint16(tmp,4)),
+ dht::Key(tmp+6)));
+ }
+ }
+
+ void KBucket::onFinished(Task* t)
+ {
+ if (t == refresh_task)
+ refresh_task = 0;
+ }
+
+ void KBucket::setRefreshTask(Task* t)
+ {
+ refresh_task = t;
+ if (refresh_task)
+ {
+ connect(refresh_task,SIGNAL(finished( Task* )),
+ this,SLOT(onFinished( Task* )));
+ }
+ }
+
+}
+
+#include "kbucket.moc"
diff --git a/libktorrent/kademlia/kbucket.h b/libktorrent/kademlia/kbucket.h
new file mode 100644
index 0000000..139ce10
--- /dev/null
+++ b/libktorrent/kademlia/kbucket.h
@@ -0,0 +1,212 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef DHTKBUCKET_H
+#define DHTKBUCKET_H
+
+#include <qvaluelist.h>
+#include <util/constants.h>
+#include <ksocketaddress.h>
+#include "key.h"
+#include "rpccall.h"
+#include "task.h"
+
+using bt::Uint32;
+using bt::Uint16;
+using bt::Uint8;
+using KNetwork::KInetSocketAddress;
+
+namespace bt
+{
+ class File;
+}
+
+namespace dht
+{
+ class RPCServer;
+ class KClosestNodesSearch;
+ class Node;
+ class Task;
+
+ const Uint32 K = 8;
+ const Uint32 BUCKET_MAGIC_NUMBER = 0xB0C4B0C4;
+ const Uint32 BUCKET_REFRESH_INTERVAL = 15 * 60 * 1000;
+// const Uint32 BUCKET_REFRESH_INTERVAL = 120 * 1000;
+
+ struct BucketHeader
+ {
+ Uint32 magic;
+ Uint32 index;
+ Uint32 num_entries;
+ };
+
+ /**
+ * @author Joris Guisson
+ *
+ * Entry in a KBucket, it basically contains an ip_address of a node,
+ * the udp port of the node and a node_id.
+ */
+ class KBucketEntry
+ {
+ KInetSocketAddress addr;
+ Key node_id;
+ bt::TimeStamp last_responded;
+ Uint32 failed_queries;
+ Uint32 questionable_pings;
+ public:
+ /**
+ * Constructor, sets everything to 0.
+ * @return
+ */
+ KBucketEntry();
+
+ /**
+ * Constructor, set the ip, port and key
+ * @param addr socket address
+ * @param id ID of node
+ */
+ KBucketEntry(const KInetSocketAddress & addr,const Key & id);
+
+ /**
+ * Copy constructor.
+ * @param other KBucketEntry to copy
+ * @return
+ */
+ KBucketEntry(const KBucketEntry & other);
+
+ /// Destructor
+ virtual ~KBucketEntry();
+
+ /**
+ * Assignment operator.
+ * @param other Node to copy
+ * @return this KBucketEntry
+ */
+ KBucketEntry & operator = (const KBucketEntry & other);
+
+ /// Equality operator
+ bool operator == (const KBucketEntry & entry) const;
+
+ /// Get the socket address of the node
+ const KInetSocketAddress & getAddress() const {return addr;}
+
+ /// Get it's ID
+ const Key & getID() const {return node_id;}
+
+ /// Is this node a good node
+ bool isGood() const;
+
+ /// Is this node questionable (haven't heard from it in the last 15 minutes)
+ bool isQuestionable() const;
+
+ /// Is it a bad node. (Hasn't responded to a query
+ bool isBad() const;
+
+ /// Signal the entry that the peer has responded
+ void hasResponded();
+
+ /// A request timed out
+ void requestTimeout() {failed_queries++;}
+
+ /// The entry has been pinged because it is questionable
+ void onPingQuestionable() {questionable_pings++;}
+
+ /// The null entry
+ static KBucketEntry null;
+ };
+
+
+ /**
+ * @author Joris Guisson
+ *
+ * A KBucket is just a list of KBucketEntry objects.
+ * The list is sorted by time last seen :
+ * The first element is the least recently seen, the last
+ * the most recently seen.
+ */
+ class KBucket : public RPCCallListener
+ {
+ Q_OBJECT
+
+ Uint32 idx;
+ QValueList<KBucketEntry> entries,pending_entries;
+ RPCServer* srv;
+ Node* node;
+ QMap<RPCCall*,KBucketEntry> pending_entries_busy_pinging;
+ mutable bt::TimeStamp last_modified;
+ Task* refresh_task;
+ public:
+ KBucket(Uint32 idx,RPCServer* srv,Node* node);
+ virtual ~KBucket();
+
+ /**
+ * Inserts an entry into the bucket.
+ * @param entry The entry to insert
+ */
+ void insert(const KBucketEntry & entry);
+
+ /// Get the least recently seen node
+ const KBucketEntry & leastRecentlySeen() const {return entries[0];}
+
+ /// Get the number of entries
+ Uint32 getNumEntries() const {return entries.count();}
+
+ /// See if this bucket contains an entry
+ bool contains(const KBucketEntry & entry) const;
+
+ /**
+ * Find the K closest entries to a key and store them in the KClosestNodesSearch
+ * object.
+ * @param kns The object to storre the search results
+ */
+ void findKClosestNodes(KClosestNodesSearch & kns);
+
+ /**
+ * A peer failed to respond
+ * @param addr Address of the peer
+ */
+ bool onTimeout(const KInetSocketAddress & addr);
+
+ /// Check if the bucket needs to be refreshed
+ bool needsToBeRefreshed() const;
+
+ /// save the bucket to a file
+ void save(bt::File & fptr);
+
+ /// Load the bucket from a file
+ void load(bt::File & fptr,const BucketHeader & hdr);
+
+ /// Update the refresh timer of the bucket
+ void updateRefreshTimer();
+
+ /// Set the refresh task
+ void setRefreshTask(Task* t);
+
+ private:
+ virtual void onResponse(RPCCall* c,MsgBase* rsp);
+ virtual void onTimeout(RPCCall* c);
+ void pingQuestionable(const KBucketEntry & replacement_entry);
+ bool replaceBadEntry(const KBucketEntry & entry);
+
+ private slots:
+ void onFinished(Task* t);
+ };
+}
+
+#endif
diff --git a/libktorrent/kademlia/kclosestnodessearch.cpp b/libktorrent/kademlia/kclosestnodessearch.cpp
new file mode 100644
index 0000000..4a97c7f
--- /dev/null
+++ b/libktorrent/kademlia/kclosestnodessearch.cpp
@@ -0,0 +1,84 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <util/functions.h>
+#include "kclosestnodessearch.h"
+#include "pack.h"
+
+using namespace bt;
+using namespace KNetwork;
+
+namespace dht
+{
+ typedef std::map<dht::Key,KBucketEntry>::iterator KNSitr;
+
+ KClosestNodesSearch::KClosestNodesSearch(const dht::Key & key,Uint32 max_entries)
+ : key(key),max_entries(max_entries)
+ {}
+
+
+ KClosestNodesSearch::~KClosestNodesSearch()
+ {}
+
+
+ void KClosestNodesSearch::tryInsert(const KBucketEntry & e)
+ {
+ // calculate distance between key and e
+ dht::Key d = dht::Key::distance(key,e.getID());
+
+ if (emap.size() < max_entries)
+ {
+ // room in the map so just insert
+ emap.insert(std::make_pair(d,e));
+ }
+ else
+ {
+ // now find the max distance
+ // seeing that the last element of the map has also
+ // the biggest distance to key (std::map is sorted on the distance)
+ // we just take the last
+ const dht::Key & max = emap.rbegin()->first;
+ if (d < max)
+ {
+ // insert if d is smaller then max
+ emap.insert(std::make_pair(d,e));
+ // erase the old max value
+ emap.erase(max);
+ }
+ }
+
+ }
+
+ void KClosestNodesSearch::pack(QByteArray & ba)
+ {
+ // make sure we do not writ to much
+ Uint32 max_items = ba.size() / 26;
+ Uint32 j = 0;
+
+ KNSitr i = emap.begin();
+ while (i != emap.end() && j < max_items)
+ {
+ PackBucketEntry(i->second,ba,j*26);
+ i++;
+ j++;
+ i++;
+ }
+ }
+
+}
diff --git a/libktorrent/kademlia/kclosestnodessearch.h b/libktorrent/kademlia/kclosestnodessearch.h
new file mode 100644
index 0000000..e006a25
--- /dev/null
+++ b/libktorrent/kademlia/kclosestnodessearch.h
@@ -0,0 +1,90 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef DHTKCLOSESTNODESSEARCH_H
+#define DHTKCLOSESTNODESSEARCH_H
+
+#include <map>
+#include "key.h"
+#include "kbucket.h"
+
+namespace dht
+{
+
+ /**
+ * @author Joris Guisson <joris.guisson@gmail.com>
+ *
+ * Class used to store the search results during a K closests nodes search
+ * Note: we use a std::map because of lack of functionality in QMap
+ */
+ class KClosestNodesSearch
+ {
+ dht::Key key;
+ std::map<dht::Key,KBucketEntry> emap;
+ Uint32 max_entries;
+ public:
+ /**
+ * Constructor sets the key to compare with
+ * @param key The key to compare with
+ * @param max_entries The maximum number of entries can be in the map
+ * @return
+ */
+ KClosestNodesSearch(const dht::Key & key,Uint32 max_entries);
+ virtual ~KClosestNodesSearch();
+
+ typedef std::map<dht::Key,KBucketEntry>::iterator Itr;
+ typedef std::map<dht::Key,KBucketEntry>::const_iterator CItr;
+
+ Itr begin() {return emap.begin();}
+ Itr end() {return emap.end();}
+
+ CItr begin() const {return emap.begin();}
+ CItr end() const {return emap.end();}
+
+ /// Get the target key of the search3
+ const dht::Key & getSearchTarget() const {return key;}
+
+ /// Get the number of entries.
+ bt::Uint32 getNumEntries() const {return emap.size();}
+
+ /**
+ * Try to insert an entry.
+ * @param e The entry
+ */
+ void tryInsert(const KBucketEntry & e);
+
+ /**
+ * Gets the required space in bytes to pack the nodes.
+ * This should be used to determin the size of the buffer
+ * passed to pack.
+ * @return 26 * number of entries
+ */
+ Uint32 requiredSpace() const {return emap.size()* 26;}
+
+ /**
+ * Pack the search results in a buffer, the buffer should have
+ * enough space to store requiredSpace() bytes.
+ * @param ba The buffer
+ */
+ void pack(QByteArray & ba);
+ };
+
+}
+
+#endif
diff --git a/libktorrent/kademlia/key.cpp b/libktorrent/kademlia/key.cpp
new file mode 100644
index 0000000..6e62ff6
--- /dev/null
+++ b/libktorrent/kademlia/key.cpp
@@ -0,0 +1,110 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <time.h>
+#include <stdlib.h>
+#include <qcstring.h>
+#include <util/constants.h>
+#include "key.h"
+
+using namespace bt;
+
+namespace dht
+{
+
+ Key::Key()
+ {}
+
+ Key::Key(const bt::SHA1Hash & k) : bt::SHA1Hash(k)
+ {
+ }
+
+ Key::Key(const Uint8* d) : bt::SHA1Hash(d)
+ {
+ }
+
+ Key::Key(const QByteArray & ba)
+ {
+ for (Uint32 i = 0;i < 20 && i < ba.size();i++)
+ hash[i] = ba[i];
+ }
+
+ Key::~Key()
+ {}
+
+ bool Key::operator == (const Key & other) const
+ {
+ return bt::SHA1Hash::operator ==(other);
+ }
+
+ bool Key::operator != (const Key & other) const
+ {
+ return !operator == (other);
+ }
+
+ bool Key::operator < (const Key & other) const
+ {
+ for (int i = 0;i < 20;i++)
+ {
+ if (hash[i] < other.hash[i])
+ return true;
+ else if (hash[i] > other.hash[i])
+ return false;
+ }
+ return false;
+ }
+
+ bool Key::operator <= (const Key & other) const
+ {
+ return operator < (other) || operator == (other);
+ }
+
+ bool Key::operator > (const Key & other) const
+ {
+ for (int i = 0;i < 20;i++)
+ {
+ if (hash[i] < other.hash[i])
+ return false;
+ else if (hash[i] > other.hash[i])
+ return true;
+ }
+ return false;
+ }
+
+ bool Key::operator >= (const Key & other) const
+ {
+ return operator > (other) || operator == (other);
+ }
+
+ Key Key::distance(const Key & a,const Key & b)
+ {
+ return a ^ b;
+ }
+
+ Key Key::random()
+ {
+ srand(time(0));
+ Key k;
+ for (int i = 0;i < 20;i++)
+ {
+ k.hash[i] = (Uint8)rand() % 0xFF;
+ }
+ return k;
+ }
+}
diff --git a/libktorrent/kademlia/key.h b/libktorrent/kademlia/key.h
new file mode 100644
index 0000000..e818dc1
--- /dev/null
+++ b/libktorrent/kademlia/key.h
@@ -0,0 +1,129 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef DHTKEY_H
+#define DHTKEY_H
+
+#include <qcstring.h>
+#include <util/sha1hash.h>
+
+
+
+namespace dht
+{
+
+ /**
+ * @author Joris Guisson
+ * @brief Key in the distributed hash table
+ *
+ * Key's in the distributed hash table are just SHA-1 hashes.
+ * Key provides all necesarry operators to be used as a value.
+ */
+ class Key : public bt::SHA1Hash
+ {
+ public:
+ /**
+ * Constructor, sets key to 0.
+ */
+ Key();
+
+ /**
+ * Copy constructor. Seeing that Key doesn't add any data
+ * we just pass a SHA1Hash, Key's are automatically covered by this
+ * @param k Hash to copy
+ */
+ Key(const bt::SHA1Hash & k);
+
+ /**
+ * Make a key out of a bytearray
+ * @param ba The QByteArray
+ */
+ Key(const QByteArray & ba);
+
+ /**
+ * Make a key out of a 20 byte array.
+ * @param d The array
+ */
+ Key(const bt::Uint8* d);
+
+ /// Destructor.
+ virtual ~Key();
+
+ /**
+ * Create a random key.
+ * @return A random Key
+ */
+ static Key random();
+
+ /**
+ * Equality operator.
+ * @param other The key to compare
+ * @return true if this key is equal to other
+ */
+ bool operator == (const Key & other) const;
+
+ /**
+ * Inequality operator.
+ * @param other The key to compare
+ * @return true if this key is not equal to other
+ */
+ bool operator != (const Key & other) const;
+
+ /**
+ * Smaller then operator.
+ * @param other The key to compare
+ * @return rue if this key is smaller then other
+ */
+ bool operator < (const Key & other) const;
+
+
+ /**
+ * Smaller then or equal operator.
+ * @param other The key to compare
+ * @return rue if this key is smaller then or equal to other
+ */
+ bool operator <= (const Key & other) const;
+
+
+ /**
+ * Greater then operator.
+ * @param other The key to compare
+ * @return rue if this key is greater then other
+ */
+ bool operator > (const Key & other) const;
+
+ /**
+ * Greater then or equal operator.
+ * @param other The key to compare
+ * @return rue if this key is greater then or equal to other
+ */
+ bool operator >= (const Key & other) const;
+
+ /**
+ * The distance of two keys is the keys xor together.
+ * @param a The first key
+ * @param b The second key
+ * @return a xor b
+ */
+ static Key distance(const Key & a,const Key & b);
+ };
+
+}
+
+#endif
diff --git a/libktorrent/kademlia/node.cpp b/libktorrent/kademlia/node.cpp
new file mode 100644
index 0000000..96c39a4
--- /dev/null
+++ b/libktorrent/kademlia/node.cpp
@@ -0,0 +1,287 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+
+#include <util/log.h>
+#include <util/file.h>
+#include <util/fileops.h>
+#include <util/functions.h>
+#include <torrent/globals.h>
+#include "node.h"
+#include "rpcmsg.h"
+#include "key.h"
+#include "rpccall.h"
+#include "rpcserver.h"
+#include "kclosestnodessearch.h"
+#include "dht.h"
+#include "nodelookup.h"
+
+using namespace bt;
+using namespace KNetwork;
+
+namespace dht
+{
+ static void SaveKey(const dht::Key & key,const QString & key_file)
+ {
+ bt::File fptr;
+ if (!fptr.open(key_file,"wb"))
+ {
+ Out(SYS_DHT|LOG_IMPORTANT) << "DHT: Cannot open file " << key_file << " : " << fptr.errorString() << endl;
+ return;
+ }
+
+ fptr.write(key.getData(),20);
+ fptr.close();
+ }
+
+ static dht::Key LoadKey(const QString & key_file,bool & new_key)
+ {
+ bt::File fptr;
+ if (!fptr.open(key_file,"rb"))
+ {
+ Out(SYS_DHT|LOG_IMPORTANT) << "DHT: Cannot open file " << key_file << " : " << fptr.errorString() << endl;
+ dht::Key r = dht::Key::random();
+ SaveKey(r,key_file);
+ new_key = true;
+ return r;
+ }
+
+ Uint8 data[20];
+ if (fptr.read(data,20) != 20)
+ {
+ dht::Key r = dht::Key::random();
+ SaveKey(r,key_file);
+ new_key = true;
+ return r;
+ }
+
+ new_key = false;
+ return dht::Key(data);
+ }
+
+ Node::Node(RPCServer* srv,const QString & key_file) : srv(srv)
+ {
+ num_receives = 0;
+ num_entries = 0;
+ delete_table = false;
+ our_id = LoadKey(key_file,delete_table);
+ for (int i = 0;i < 160;i++)
+ bucket[i] = 0;
+ }
+
+
+ Node::~Node()
+ {
+ for (int i = 0;i < 160;i++)
+ {
+ KBucket* b = bucket[i];
+ if (b)
+ delete b;
+ }
+ }
+
+ Uint8 Node::findBucket(const dht::Key & id)
+ {
+ // XOR our id and the sender's ID
+ dht::Key d = dht::Key::distance(id,our_id);
+ // now use the first on bit to determin which bucket it should go in
+
+ Uint8 bit_on = 0xFF;
+ for (Uint32 i = 0;i < 20;i++)
+ {
+ // get the byte
+ Uint8 b = *(d.getData() + i);
+ // no bit on in this byte so continue
+ if (b == 0x00)
+ continue;
+
+ for (Uint8 j = 0;j < 8;j++)
+ {
+ if (b & (0x80 >> j))
+ {
+ // we have found the bit
+ bit_on = (19 - i)*8 + (7 - j);
+ return bit_on;
+ }
+ }
+ }
+ return bit_on;
+ }
+
+ void Node::recieved(DHT* dh_table,const MsgBase* msg)
+ {
+ Uint8 bit_on = findBucket(msg->getID());
+
+ // return if bit_on is not good
+ if (bit_on >= 160)
+ return;
+
+ // make the bucket if it doesn't exist
+ if (!bucket[bit_on])
+ bucket[bit_on] = new KBucket(bit_on,srv,this);
+
+ // insert it into the bucket
+ KBucket* kb = bucket[bit_on];
+ kb->insert(KBucketEntry(msg->getOrigin(),msg->getID()));
+ num_receives++;
+ if (num_receives == 3)
+ {
+ // do a node lookup upon our own id
+ // when we insert the first entry in the table
+ dh_table->findNode(our_id);
+ }
+
+ num_entries = 0;
+ for (Uint32 i = 0;i < 160;i++)
+ if (bucket[i])
+ num_entries += bucket[i]->getNumEntries();
+ }
+
+ void Node::findKClosestNodes(KClosestNodesSearch & kns)
+ {
+ // go over all buckets until
+ for (Uint32 i = 0;i < 160;i++)
+ {
+ if (bucket[i])
+ {
+ bucket[i]->findKClosestNodes(kns);
+ }
+ }
+ }
+
+ void Node::onTimeout(const MsgBase* msg)
+ {
+ for (Uint32 i = 0;i < 160;i++)
+ {
+ if (bucket[i] && bucket[i]->onTimeout(msg->getDestination()))
+ {
+ return;
+ }
+ }
+ }
+
+ /// Generate a random key which lies in a certain bucket
+ Key RandomKeyInBucket(Uint32 b,const Key & our_id)
+ {
+ // first generate a random one
+ Key r = dht::Key::random();
+ Uint8* data = (Uint8*)r.getData();
+
+ // before we hit bit b, everything needs to be equal to our_id
+ Uint8 nb = b / 8;
+ for (Uint8 i = 0;i < nb;i++)
+ data[i] = *(our_id.getData() + i);
+
+
+ // copy all bits of ob, until we hit the bit which needs to be different
+ Uint8 ob = *(our_id.getData() + nb);
+ for (Uint8 j = 0;j < b % 8;j++)
+ {
+ if ((0x80 >> j) & ob)
+ data[nb] |= (0x80 >> j);
+ else
+ data[nb] &= ~(0x80 >> j);
+ }
+
+ // if the bit b is on turn it off else turn it on
+ if ((0x80 >> (b % 8)) & ob)
+ data[nb] &= ~(0x80 >> (b % 8));
+ else
+ data[nb] |= (0x80 >> (b % 8));
+
+ return Key(data);
+ }
+
+ void Node::refreshBuckets(DHT* dh_table)
+ {
+ for (Uint32 i = 0;i < 160;i++)
+ {
+ KBucket* b = bucket[i];
+ if (b && b->needsToBeRefreshed())
+ {
+ // the key needs to be the refreshed
+ NodeLookup* nl = dh_table->refreshBucket(RandomKeyInBucket(i,our_id),*b);
+ if (nl)
+ b->setRefreshTask(nl);
+ }
+ }
+ }
+
+
+ void Node::saveTable(const QString & file)
+ {
+ bt::File fptr;
+ if (!fptr.open(file,"wb"))
+ {
+ Out(SYS_DHT|LOG_IMPORTANT) << "DHT: Cannot open file " << file << " : " << fptr.errorString() << endl;
+ return;
+ }
+
+ for (Uint32 i = 0;i < 160;i++)
+ {
+ KBucket* b = bucket[i];
+ if (b)
+ {
+ b->save(fptr);
+ }
+ }
+ }
+
+ void Node::loadTable(const QString & file)
+ {
+ if (delete_table)
+ {
+ delete_table = false;
+ bt::Delete(file,true);
+ Out(SYS_DHT|LOG_IMPORTANT) << "DHT: new key, so removing table" << endl;
+ return;
+ }
+
+ bt::File fptr;
+ if (!fptr.open(file,"rb"))
+ {
+ Out(SYS_DHT|LOG_IMPORTANT) << "DHT: Cannot open file " << file << " : " << fptr.errorString() << endl;
+ return;
+ }
+
+ num_entries = 0;
+ while (!fptr.eof())
+ {
+ BucketHeader hdr;
+ if (fptr.read(&hdr,sizeof(BucketHeader)) != sizeof(BucketHeader))
+ return;
+
+ if (hdr.magic != dht::BUCKET_MAGIC_NUMBER || hdr.num_entries > dht::K || hdr.index > 160)
+ return;
+
+ if (hdr.num_entries == 0)
+ continue;
+
+ Out(SYS_DHT|LOG_NOTICE) << "DHT: Loading bucket " << hdr.index << endl;
+ if (bucket[hdr.index])
+ delete bucket[hdr.index];
+
+ bucket[hdr.index] = new KBucket(hdr.index,srv,this);
+ bucket[hdr.index]->load(fptr,hdr);
+ num_entries += bucket[hdr.index]->getNumEntries();
+ }
+ }
+}
+
+#include "node.moc"
diff --git a/libktorrent/kademlia/node.h b/libktorrent/kademlia/node.h
new file mode 100644
index 0000000..56f41f1
--- /dev/null
+++ b/libktorrent/kademlia/node.h
@@ -0,0 +1,103 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef DHTNODE_H
+#define DHTNODE_H
+
+#include <qobject.h>
+#include "key.h"
+#include "kbucket.h"
+
+
+using bt::Uint8;
+
+namespace dht
+{
+ class DHT;
+ class MsgBase;
+ class RPCServer;
+ class KClosestNodesSearch;
+
+ /**
+ * @author Joris Guisson
+ *
+ * A Node represents us in the kademlia network. It contains
+ * our id and 160 KBucket's.
+ * A KBucketEntry is in node i, when the difference between our id and
+ * the KBucketEntry's id is between 2 to the power i and 2 to the power i+1.
+ */
+ class Node : public QObject
+ {
+ Q_OBJECT
+ public:
+ Node(RPCServer* srv,const QString & key_file);
+ virtual ~Node();
+
+ /**
+ * An RPC message was received, the node must now update
+ * the right bucket.
+ * @param dh_table The DHT
+ * @param msg The message
+ * @param srv The RPCServer to send a ping if necessary
+ */
+ void recieved(DHT* dh_table,const MsgBase* msg);
+
+ /// Get our own ID
+ const dht::Key & getOurID() const {return our_id;}
+
+ /**
+ * Find the K closest entries to a key and store them in the KClosestNodesSearch
+ * object.
+ * @param kns The object to storre the search results
+ */
+ void findKClosestNodes(KClosestNodesSearch & kns);
+
+ /**
+ * Increase the failed queries count of the bucket entry we sent the message to
+ */
+ void onTimeout(const MsgBase* msg);
+
+ /// Check if a buckets needs to be refreshed, and refresh if necesarry
+ void refreshBuckets(DHT* dh_table);
+
+ /// Save the routing table to a file
+ void saveTable(const QString & file);
+
+ /// Load the routing table from a file
+ void loadTable(const QString & file);
+
+ /// Get the number of entries in the routing table
+ Uint32 getNumEntriesInRoutingTable() const {return num_entries;}
+ private:
+ Uint8 findBucket(const dht::Key & id);
+
+
+
+ private:
+ dht::Key our_id;
+ KBucket* bucket[160];
+ RPCServer* srv;
+ Uint32 num_receives;
+ Uint32 num_entries;
+ bool delete_table;
+ };
+
+}
+
+#endif
diff --git a/libktorrent/kademlia/nodelookup.cpp b/libktorrent/kademlia/nodelookup.cpp
new file mode 100644
index 0000000..9fa616c
--- /dev/null
+++ b/libktorrent/kademlia/nodelookup.cpp
@@ -0,0 +1,98 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <util/log.h>
+#include <torrent/globals.h>
+#include "nodelookup.h"
+#include "rpcmsg.h"
+#include "node.h"
+#include "pack.h"
+
+using namespace bt;
+
+namespace dht
+{
+
+ NodeLookup::NodeLookup(const dht::Key & key,RPCServer* rpc,Node* node)
+ : Task(rpc,node),node_id(key),num_nodes_rsp(0)
+ {
+ }
+
+
+ NodeLookup::~NodeLookup()
+ {}
+
+
+ void NodeLookup::callFinished(RPCCall* ,MsgBase* rsp)
+ {
+ // Out() << "NodeLookup::callFinished" << endl;
+ if (isFinished())
+ return;
+
+ // check the response and see if it is a good one
+ if (rsp->getMethod() == dht::FIND_NODE && rsp->getType() == dht::RSP_MSG)
+ {
+ FindNodeRsp* fnr = (FindNodeRsp*)rsp;
+ const QByteArray & nodes = fnr->getNodes();
+ Uint32 nnodes = nodes.size() / 26;
+ for (Uint32 j = 0;j < nnodes;j++)
+ {
+ // unpack an entry and add it to the todo list
+ KBucketEntry e = UnpackBucketEntry(nodes,j*26);
+ // lets not talk to ourself
+ if (e.getID() != node->getOurID() && !todo.contains(e) && !visited.contains(e))
+ todo.append(e);
+ }
+ num_nodes_rsp++;
+ }
+ }
+
+ void NodeLookup::callTimeout(RPCCall*)
+ {
+ // Out() << "NodeLookup::callTimeout" << endl;
+ }
+
+ void NodeLookup::update()
+ {
+ // Out() << "NodeLookup::update" << endl;
+ // Out() << "todo = " << todo.count() << " ; visited = " << visited.count() << endl;
+ // go over the todo list and send find node calls
+ // until we have nothing left
+ while (!todo.empty() && canDoRequest())
+ {
+ KBucketEntry e = todo.first();
+ // only send a findNode if we haven't allrready visited the node
+ if (!visited.contains(e))
+ {
+ // send a findNode to the node
+ FindNodeReq* fnr = new FindNodeReq(node->getOurID(),node_id);
+ fnr->setOrigin(e.getAddress());
+ rpcCall(fnr);
+ visited.append(e);
+ }
+ // remove the entry from the todo list
+ todo.pop_front();
+ }
+
+ if (todo.empty() && getNumOutstandingRequests() == 0 && !isFinished())
+ done();
+ else if (num_nodes_rsp > 50)
+ done(); // quit after 50 nodes responses
+ }
+}
diff --git a/libktorrent/kademlia/nodelookup.h b/libktorrent/kademlia/nodelookup.h
new file mode 100644
index 0000000..ff19e92
--- /dev/null
+++ b/libktorrent/kademlia/nodelookup.h
@@ -0,0 +1,52 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef DHTNODELOOKUP_H
+#define DHTNODELOOKUP_H
+
+#include "key.h"
+#include "task.h"
+
+namespace dht
+{
+ class Node;
+ class RPCServer;
+
+ /**
+ * @author Joris Guisson <joris.guisson@gmail.com>
+ *
+ * Task to do a node lookup.
+ */
+ class NodeLookup : public Task
+ {
+ public:
+ NodeLookup(const dht::Key & node_id,RPCServer* rpc,Node* node);
+ virtual ~NodeLookup();
+
+ virtual void update();
+ virtual void callFinished(RPCCall* c, MsgBase* rsp);
+ virtual void callTimeout(RPCCall* c);
+ private:
+ dht::Key node_id;
+ bt::Uint32 num_nodes_rsp;
+ };
+
+}
+
+#endif
diff --git a/libktorrent/kademlia/pack.cpp b/libktorrent/kademlia/pack.cpp
new file mode 100644
index 0000000..a5acafb
--- /dev/null
+++ b/libktorrent/kademlia/pack.cpp
@@ -0,0 +1,62 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <util/error.h>
+#include <util/functions.h>
+#include "pack.h"
+
+using namespace bt;
+using namespace KNetwork;
+
+namespace dht
+{
+
+ void PackBucketEntry(const KBucketEntry & e,QByteArray & ba,Uint32 off)
+ {
+ // first check size
+ if (off + 26 > ba.size())
+ throw bt::Error("Not enough room in buffer");
+
+ Uint8* data = (Uint8*)ba.data();
+ Uint8* ptr = data + off;
+
+ const KInetSocketAddress & addr = e.getAddress();
+ // copy ID, IP address and port into the buffer
+ memcpy(ptr,e.getID().getData(),20);
+ bt::WriteUint32(ptr,20,addr.ipAddress().IPv4Addr());
+ bt::WriteUint16(ptr,24,addr.port());
+ }
+
+ KBucketEntry UnpackBucketEntry(const QByteArray & ba,Uint32 off)
+ {
+ if (off + 26 > ba.size())
+ throw bt::Error("Not enough room in buffer");
+
+ const Uint8* data = (Uint8*)ba.data();
+ const Uint8* ptr = data + off;
+
+ // get the port, ip and key);
+ Uint16 port = bt::ReadUint16(ptr,24);
+ Uint8 key[20];
+ memcpy(key,ptr,20);
+
+ return KBucketEntry(KInetSocketAddress(KIpAddress(ptr+20,4),port),dht::Key(key));
+ }
+
+}
diff --git a/libktorrent/kademlia/pack.h b/libktorrent/kademlia/pack.h
new file mode 100644
index 0000000..dab1523
--- /dev/null
+++ b/libktorrent/kademlia/pack.h
@@ -0,0 +1,48 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef DHTPACK_H
+#define DHTPACK_H
+
+#include "kbucket.h"
+
+namespace dht
+{
+
+ /**
+ * Pack a KBucketEntry into a byte array.
+ * If the array is not large enough, an error will be thrown
+ * @param e The entry
+ * @param ba The byte array
+ * @param off The offset into the array
+ */
+ void PackBucketEntry(const KBucketEntry & e,QByteArray & ba,Uint32 off);
+
+ /**
+ * Unpack a KBucketEntry from a byte array.
+ * If a full entry cannot be read an error will be thrown.
+ * @param ba The byte array
+ * @param off The offset
+ * @return The entry
+ */
+ KBucketEntry UnpackBucketEntry(const QByteArray & ba,Uint32 off);
+
+}
+
+#endif
diff --git a/libktorrent/kademlia/rpccall.cpp b/libktorrent/kademlia/rpccall.cpp
new file mode 100644
index 0000000..b86e8f7
--- /dev/null
+++ b/libktorrent/kademlia/rpccall.cpp
@@ -0,0 +1,79 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include "dht.h"
+#include "rpcmsg.h"
+#include "rpccall.h"
+#include "rpcserver.h"
+
+namespace dht
+{
+ RPCCallListener::RPCCallListener()
+ {}
+
+ RPCCallListener::~RPCCallListener()
+ {
+ }
+
+ RPCCall::RPCCall(RPCServer* rpc,MsgBase* msg,bool queued) : msg(msg),rpc(rpc),queued(queued)
+ {
+ connect(&timer,SIGNAL(timeout()),this,SLOT(onTimeout()));
+ if (!queued)
+ timer.start(30*1000,true);
+ }
+
+
+ RPCCall::~RPCCall()
+ {
+ delete msg;
+ }
+
+ void RPCCall::start()
+ {
+ queued = false;
+ timer.start(30*1000,true);
+ }
+
+ void RPCCall::onTimeout()
+ {
+ onCallTimeout(this);
+ rpc->timedOut(msg->getMTID());
+ }
+
+ void RPCCall::response(MsgBase* rsp)
+ {
+ onCallResponse(this,rsp);
+ }
+
+ Method RPCCall::getMsgMethod() const
+ {
+ if (msg)
+ return msg->getMethod();
+ else
+ return dht::NONE;
+ }
+
+ void RPCCall::addListener(RPCCallListener* cl)
+ {
+ connect(this,SIGNAL(onCallResponse( RPCCall*, MsgBase* )),cl,SLOT(onResponse( RPCCall*, MsgBase* )));
+ connect(this,SIGNAL(onCallTimeout( RPCCall* )),cl,SLOT(onTimeout( RPCCall* )));
+ }
+
+}
+#include "rpccall.moc"
diff --git a/libktorrent/kademlia/rpccall.h b/libktorrent/kademlia/rpccall.h
new file mode 100644
index 0000000..6e54933
--- /dev/null
+++ b/libktorrent/kademlia/rpccall.h
@@ -0,0 +1,110 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef DHTRPCCALL_H
+#define DHTRPCCALL_H
+
+#include <qtimer.h>
+#include "key.h"
+#include "rpcmsg.h"
+
+namespace dht
+{
+ class RPCServer;
+ class RPCCall;
+
+ /**
+ * Class which objects should derive from, if they want to know the result of a call.
+ */
+ class RPCCallListener : public QObject
+ {
+ Q_OBJECT
+ public:
+ RPCCallListener();
+ virtual ~RPCCallListener();
+
+ public slots:
+ /**
+ * A response was received.
+ * @param c The call
+ * @param rsp The response
+ */
+ virtual void onResponse(RPCCall* c,MsgBase* rsp) = 0;
+
+ /**
+ * The call has timed out.
+ * @param c The call
+ */
+ virtual void onTimeout(RPCCall* c) = 0;
+
+ };
+
+ /**
+ * @author Joris Guisson
+ */
+ class RPCCall : public QObject
+ {
+ Q_OBJECT
+ public:
+ RPCCall(RPCServer* rpc,MsgBase* msg,bool queued);
+ virtual ~RPCCall();
+
+ /**
+ * Called when a queued call gets started. Starts the timeout timer.
+ */
+ void start();
+
+ /**
+ * Called by the server if a response is received.
+ * @param rsp
+ */
+ void response(MsgBase* rsp);
+
+ /**
+ * Add a listener for this call
+ * @param cl The listener
+ */
+ void addListener(RPCCallListener* cl);
+
+ /// Get the message type
+ Method getMsgMethod() const;
+
+ /// Get the request sent
+ const MsgBase* getRequest() const {return msg;}
+
+ /// Get the request sent
+ MsgBase* getRequest() {return msg;}
+
+ private slots:
+ void onTimeout();
+
+ signals:
+ void onCallResponse(RPCCall* c,MsgBase* rsp);
+ void onCallTimeout(RPCCall* c);
+
+ private:
+ MsgBase* msg;
+ QTimer timer;
+ RPCServer* rpc;
+ bool queued;
+ };
+
+}
+
+#endif
diff --git a/libktorrent/kademlia/rpcmsg.cpp b/libktorrent/kademlia/rpcmsg.cpp
new file mode 100644
index 0000000..97364e1
--- /dev/null
+++ b/libktorrent/kademlia/rpcmsg.cpp
@@ -0,0 +1,596 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <util/log.h>
+#include <torrent/bnode.h>
+#include <torrent/globals.h>
+#include <torrent/bencoder.h>
+#include "rpcmsg.h"
+#include "rpccall.h"
+#include "rpcserver.h"
+#include "dht.h"
+
+using namespace bt;
+
+namespace dht
+{
+ const QString TID = "t";
+ const QString REQ = "q";
+ const QString RSP = "r";
+ const QString TYP = "y";
+ const QString ARG = "a";
+ // ERR apparently is defined as a macro on solaris in some header file,
+ // which causes things not to compile on it, so we have changed it to ERR_DHT
+ const QString ERR_DHT = "e";
+
+
+ MsgBase* MakeMsg(bt::BDictNode* dict);
+
+
+ MsgBase* ParseReq(bt::BDictNode* dict)
+ {
+ BValueNode* vn = dict->getValue(REQ);
+ BDictNode* args = dict->getDict(ARG);
+ if (!vn || !args)
+ return 0;
+
+ if (!args->getValue("id"))
+ return 0;
+
+ if (!dict->getValue(TID))
+ return 0;
+
+ Key id = Key(args->getValue("id")->data().toByteArray());
+ QByteArray mtid_d = dict->getValue(TID)->data().toByteArray();
+ if (mtid_d.size() == 0)
+ return 0;
+ Uint8 mtid = (Uint8)mtid_d.at(0);
+ MsgBase* msg = 0;
+
+ QString str = vn->data().toString();
+ if (str == "ping")
+ {
+ msg = new PingReq(id);
+ }
+ else if (str == "find_node")
+ {
+ if (args->getValue("target"))
+ msg = new FindNodeReq(id,Key(args->getValue("target")->data().toByteArray()));
+ }
+ else if (str == "get_peers")
+ {
+ if (args->getValue("info_hash"))
+ msg = new GetPeersReq(id,Key(args->getValue("info_hash")->data().toByteArray()));
+ }
+ else if (str == "announce_peer")
+ {
+ if (args->getValue("info_hash") && args->getValue("port") && args->getValue("token"))
+ {
+ msg = new AnnounceReq(id,
+ Key(args->getValue("info_hash")->data().toByteArray()),
+ args->getValue("port")->data().toInt(),
+ Key(args->getValue("token")->data().toByteArray()));
+ }
+ }
+
+ if (msg)
+ msg->setMTID(mtid);
+
+ return msg;
+ }
+
+ MsgBase* ParseRsp(bt::BDictNode* dict,dht::Method req_method,Uint8 mtid)
+ {
+ BDictNode* args = dict->getDict(RSP);
+ if (!args || !args->getValue("id"))
+ return 0;
+
+ Key id = Key(args->getValue("id")->data().toByteArray());
+
+ switch (req_method)
+ {
+ case PING :
+ return new PingRsp(mtid,id);
+ case FIND_NODE :
+ if (!args->getValue("nodes"))
+ return 0;
+ else
+ return new FindNodeRsp(mtid,id,args->getValue("nodes")->data().toByteArray());
+ case GET_PEERS :
+ if (args->getValue("token"))
+ {
+ Key token = args->getValue("token")->data().toByteArray();
+ QByteArray data;
+ BListNode* vals = args->getList("values");
+ DBItemList dbl;
+ if (vals)
+ {
+ for (Uint32 i = 0;i < vals->getNumChildren();i++)
+ {
+ BValueNode* vn = dynamic_cast<BValueNode*>(vals->getChild(i));
+ if (!vn)
+ continue;
+ dbl.append(DBItem((Uint8*)vn->data().toByteArray().data()));
+ }
+ return new GetPeersRsp(mtid,id,dbl,token);
+ }
+ else if (args->getValue("nodes"))
+ {
+ data = args->getValue("nodes")->data().toByteArray();
+ return new GetPeersRsp(mtid,id,data,token);
+ }
+ else
+ {
+ Out(SYS_DHT|LOG_DEBUG) << "No nodes or values in get_peers response" << endl;
+ return 0;
+ }
+ }
+ else
+ {
+ Out(SYS_DHT|LOG_DEBUG) << "No token in get_peers response" << endl;
+ }
+ case ANNOUNCE_PEER :
+ return new AnnounceRsp(mtid,id);
+ default:
+ return 0;
+ }
+ return 0;
+ }
+
+ MsgBase* ParseRsp(bt::BDictNode* dict,RPCServer* srv)
+ {
+ BDictNode* args = dict->getDict(RSP);
+ if (!args || !dict->getValue(TID))
+ {
+ Out(SYS_DHT|LOG_DEBUG) << "ParseRsp : args || !args->getValue(id) || !dict->getValue(TID)" << endl;
+ return 0;
+ }
+
+
+ QByteArray ba = dict->getValue(TID)->data().toByteArray();
+ // check for empty byte arrays should prevent 144416
+ if (ba.size() == 0)
+ return 0;
+
+ Uint8 mtid = (Uint8)ba.at(0);
+ // find the call
+ const RPCCall* c = srv->findCall(mtid);
+ if (!c)
+ {
+ Out(SYS_DHT|LOG_DEBUG) << "Cannot find RPC call" << endl;
+ return 0;
+ }
+
+ return ParseRsp(dict,c->getMsgMethod(),mtid);
+ }
+
+ MsgBase* ParseErr(bt::BDictNode* dict)
+ {
+ BValueNode* vn = dict->getValue(RSP);
+ BDictNode* args = dict->getDict(ARG);
+ if (!vn || !args || !args->getValue("id") || !dict->getValue(TID))
+ return 0;
+
+ Key id = Key(args->getValue("id")->data().toByteArray());
+ QString mt_id = dict->getValue(TID)->data().toString();
+ if (mt_id.length() == 0)
+ return 0;
+
+ Uint8 mtid = (char)mt_id.at(0).latin1();
+ QString str = vn->data().toString();
+
+ return new ErrMsg(mtid,id,str);
+ }
+
+
+ MsgBase* MakeRPCMsg(bt::BDictNode* dict,RPCServer* srv)
+ {
+ BValueNode* vn = dict->getValue(TYP);
+ if (!vn)
+ return 0;
+
+ if (vn->data().toString() == REQ)
+ {
+ return ParseReq(dict);
+ }
+ else if (vn->data().toString() == RSP)
+ {
+ return ParseRsp(dict,srv);
+ }
+ else if (vn->data().toString() == ERR_DHT)
+ {
+ return ParseErr(dict);
+ }
+
+ return 0;
+ }
+
+ MsgBase* MakeRPCMsgTest(bt::BDictNode* dict,dht::Method req_method)
+ {
+ BValueNode* vn = dict->getValue(TYP);
+ if (!vn)
+ return 0;
+
+ if (vn->data().toString() == REQ)
+ {
+ return ParseReq(dict);
+ }
+ else if (vn->data().toString() == RSP)
+ {
+ return ParseRsp(dict,req_method,0);
+ }
+ else if (vn->data().toString() == ERR_DHT)
+ {
+ return ParseErr(dict);
+ }
+
+ return 0;
+ }
+
+ MsgBase::MsgBase(Uint8 mtid,Method m,Type type,const Key & id)
+ : mtid(mtid),method(m),type(type),id(id)
+ {}
+
+ MsgBase::~MsgBase()
+ {}
+
+ ////////////////////////////////
+
+ PingReq::PingReq(const Key & id) : MsgBase(0xFF,PING,REQ_MSG,id)
+ {
+ }
+
+ PingReq::~PingReq()
+ {}
+
+ void PingReq::apply(DHT* dh_table)
+ {
+ dh_table->ping(this);
+ }
+
+ void PingReq::print()
+ {
+ Out(SYS_DHT|LOG_DEBUG) << QString("REQ: %1 %2 : ping").arg(mtid).arg(id.toString()) << endl;
+ }
+
+ void PingReq::encode(QByteArray & arr)
+ {
+ BEncoder enc(new BEncoderBufferOutput(arr));
+ enc.beginDict();
+ {
+ enc.write(ARG); enc.beginDict();
+ {
+ enc.write("id"); enc.write(id.getData(),20);
+ }
+ enc.end();
+ enc.write(REQ); enc.write("ping");
+ enc.write(TID); enc.write(&mtid,1);
+ enc.write(TYP); enc.write(REQ);
+ }
+ enc.end();
+ }
+
+ ////////////////////////////////
+
+ FindNodeReq::FindNodeReq(const Key & id,const Key & target)
+ : MsgBase(0xFF,FIND_NODE,REQ_MSG,id),target(target)
+ {}
+
+ FindNodeReq::~FindNodeReq()
+ {}
+
+ void FindNodeReq::apply(DHT* dh_table)
+ {
+ dh_table->findNode(this);
+ }
+
+ void FindNodeReq::print()
+ {
+ Out(SYS_DHT|LOG_NOTICE) << QString("REQ: %1 %2 : find_node %3")
+ .arg(mtid).arg(id.toString()).arg(target.toString()) << endl;
+ }
+
+ void FindNodeReq::encode(QByteArray & arr)
+ {
+ BEncoder enc(new BEncoderBufferOutput(arr));
+ enc.beginDict();
+ {
+ enc.write(ARG); enc.beginDict();
+ {
+ enc.write("id"); enc.write(id.getData(),20);
+ enc.write("target"); enc.write(target.getData(),20);
+ }
+ enc.end();
+ enc.write(REQ); enc.write("find_node");
+ enc.write(TID); enc.write(&mtid,1);
+ enc.write(TYP); enc.write(REQ);
+ }
+ enc.end();
+ }
+
+ ////////////////////////////////
+
+ ////////////////////////////////
+ GetPeersReq::GetPeersReq(const Key & id,const Key & info_hash)
+ : MsgBase(0xFF,GET_PEERS,REQ_MSG,id),info_hash(info_hash)
+ {}
+
+ GetPeersReq::~GetPeersReq()
+ {}
+
+ void GetPeersReq::apply(DHT* dh_table)
+ {
+ dh_table->getPeers(this);
+ }
+
+ void GetPeersReq::print()
+ {
+ Out(SYS_DHT|LOG_DEBUG) << QString("REQ: %1 %2 : get_peers %3")
+ .arg(mtid).arg(id.toString()).arg(info_hash.toString()) << endl;
+ }
+
+ void GetPeersReq::encode(QByteArray & arr)
+ {
+ BEncoder enc(new BEncoderBufferOutput(arr));
+ enc.beginDict();
+ {
+ enc.write(ARG); enc.beginDict();
+ {
+ enc.write("id"); enc.write(id.getData(),20);
+ enc.write("info_hash"); enc.write(info_hash.getData(),20);
+ }
+ enc.end();
+ enc.write(REQ); enc.write("get_peers");
+ enc.write(TID); enc.write(&mtid,1);
+ enc.write(TYP); enc.write(REQ);
+ }
+ enc.end();
+ }
+
+ ////////////////////////////////
+
+ AnnounceReq::AnnounceReq(const Key & id,const Key & info_hash,Uint16 port,const Key & token)
+ : GetPeersReq(id,info_hash),port(port),token(token)
+ {
+ method = dht::ANNOUNCE_PEER;
+ }
+
+ AnnounceReq::~AnnounceReq() {}
+
+ void AnnounceReq::apply(DHT* dh_table)
+ {
+ dh_table->announce(this);
+ }
+
+ void AnnounceReq::print()
+ {
+ Out(SYS_DHT|LOG_DEBUG) << QString("REQ: %1 %2 : announce_peer %3 %4 %5")
+ .arg(mtid).arg(id.toString()).arg(info_hash.toString())
+ .arg(port).arg(token.toString()) << endl;
+ }
+
+ void AnnounceReq::encode(QByteArray & arr)
+ {
+ BEncoder enc(new BEncoderBufferOutput(arr));
+ enc.beginDict();
+ {
+ enc.write(ARG); enc.beginDict();
+ {
+ enc.write("id"); enc.write(id.getData(),20);
+ enc.write("info_hash"); enc.write(info_hash.getData(),20);
+ enc.write("port"); enc.write((Uint32)port);
+ enc.write("token"); enc.write(token.getData(),20);
+ }
+ enc.end();
+ enc.write(REQ); enc.write("announce_peer");
+ enc.write(TID); enc.write(&mtid,1);
+ enc.write(TYP); enc.write(REQ);
+ }
+ enc.end();
+ }
+
+ ////////////////////////////////
+
+ PingRsp::PingRsp(Uint8 mtid,const Key & id)
+ : MsgBase(mtid,PING,RSP_MSG,id)
+ {}
+
+ PingRsp::~PingRsp() {}
+
+ void PingRsp::apply(DHT* dh_table)
+ {
+ dh_table->response(this);
+ }
+
+ void PingRsp::print()
+ {
+ Out(SYS_DHT|LOG_DEBUG) << QString("RSP: %1 %2 : ping")
+ .arg(mtid).arg(id.toString()) << endl;
+ }
+
+ void PingRsp::encode(QByteArray & arr)
+ {
+ BEncoder enc(new BEncoderBufferOutput(arr));
+ enc.beginDict();
+ {
+ enc.write(RSP); enc.beginDict();
+ {
+ enc.write("id"); enc.write(id.getData(),20);
+ }
+ enc.end();
+ enc.write(TID); enc.write(&mtid,1);
+ enc.write(TYP); enc.write(RSP);
+ }
+ enc.end();
+ }
+
+ ////////////////////////////////
+
+ FindNodeRsp::FindNodeRsp(Uint8 mtid,const Key & id,const QByteArray & nodes)
+ : MsgBase(mtid,FIND_NODE,RSP_MSG,id),nodes(nodes)
+ {}
+
+ FindNodeRsp::~FindNodeRsp() {}
+
+ void FindNodeRsp::apply(DHT* dh_table)
+ {
+ dh_table->response(this);
+ }
+
+ void FindNodeRsp::print()
+ {
+ Out(SYS_DHT|LOG_DEBUG) << QString("RSP: %1 %2 : find_node")
+ .arg(mtid).arg(id.toString()) << endl;
+ }
+
+ void FindNodeRsp::encode(QByteArray & arr)
+ {
+ BEncoder enc(new BEncoderBufferOutput(arr));
+ enc.beginDict();
+ {
+ enc.write(RSP); enc.beginDict();
+ {
+ enc.write("id"); enc.write(id.getData(),20);
+ enc.write("nodes"); enc.write(nodes);
+ }
+ enc.end();
+ enc.write(TID); enc.write(&mtid,1);
+ enc.write(TYP); enc.write(RSP);
+ }
+ enc.end();
+ }
+
+ ////////////////////////////////
+
+ GetPeersRsp::GetPeersRsp(Uint8 mtid,const Key & id,const QByteArray & data,const Key & token)
+ : MsgBase(mtid,dht::GET_PEERS,dht::RSP_MSG,id),token(token),data(data)
+ {
+ this->data.detach();
+ }
+
+ GetPeersRsp::GetPeersRsp(Uint8 mtid,const Key & id,const DBItemList & values,const Key & token)
+ : MsgBase(mtid,dht::GET_PEERS,dht::RSP_MSG,id),token(token),items(values)
+ {}
+
+ GetPeersRsp::~GetPeersRsp()
+ {}
+
+ void GetPeersRsp::apply(DHT* dh_table)
+ {
+ dh_table->response(this);
+ }
+ void GetPeersRsp::print()
+ {
+ Out() << QString("RSP: %1 %2 : get_peers(%3)")
+ .arg(mtid).arg(id.toString()).arg(data.size() > 0 ? "nodes" : "values") << endl;
+ }
+
+ void GetPeersRsp::encode(QByteArray & arr)
+ {
+ BEncoder enc(new BEncoderBufferOutput(arr));
+ enc.beginDict();
+ {
+ enc.write(RSP); enc.beginDict();
+ {
+ enc.write("id"); enc.write(id.getData(),20);
+ if (data.size() > 0)
+ {
+ enc.write("nodes"); enc.write(data);
+ enc.write("token"); enc.write(token.getData(),20);
+ }
+ else
+ {
+ enc.write("token"); enc.write(token.getData(),20);
+ enc.write("values"); enc.beginList();
+ DBItemList::iterator i = items.begin();
+ while (i != items.end())
+ {
+ const DBItem & item = *i;
+ enc.write(item.getData(),6);
+ i++;
+ }
+ enc.end();
+ }
+ }
+ enc.end();
+ enc.write(TID); enc.write(&mtid,1);
+ enc.write(TYP); enc.write(RSP);
+ }
+ enc.end();
+ }
+
+
+ ////////////////////////////////
+ ////////////////////////////////
+
+ AnnounceRsp::AnnounceRsp(Uint8 mtid,const Key & id) : MsgBase(mtid,ANNOUNCE_PEER,RSP_MSG,id)
+ {}
+
+ AnnounceRsp::~AnnounceRsp(){}
+
+ void AnnounceRsp::apply(DHT* dh_table)
+ {
+ dh_table->response(this);
+ }
+
+ void AnnounceRsp::print()
+ {
+ Out() << QString("RSP: %1 %2 : announce_peer")
+ .arg(mtid).arg(id.toString()) << endl;
+ }
+
+ void AnnounceRsp::encode(QByteArray & arr)
+ {
+ BEncoder enc(new BEncoderBufferOutput(arr));
+ enc.beginDict();
+ {
+ enc.write(RSP); enc.beginDict();
+ {
+ enc.write("id"); enc.write(id.getData(),20);
+ }
+ enc.end();
+ enc.write(TID); enc.write(&mtid,1);
+ enc.write(TYP); enc.write(RSP);
+ }
+ enc.end();
+ }
+
+
+ ////////////////////////////////
+
+ ErrMsg::ErrMsg(Uint8 mtid,const Key & id,const QString & msg)
+ : MsgBase(mtid,NONE,ERR_MSG,id),msg(msg)
+ {}
+
+ ErrMsg::~ErrMsg()
+ {}
+
+ void ErrMsg::apply(DHT* dh_table)
+ {
+ dh_table->error(this);
+ }
+
+ void ErrMsg::print()
+ {
+ Out(SYS_DHT|LOG_NOTICE) << "ERR: " << mtid << " " << msg << endl;
+ }
+
+ void ErrMsg::encode(QByteArray & )
+ {}
+}
diff --git a/libktorrent/kademlia/rpcmsg.h b/libktorrent/kademlia/rpcmsg.h
new file mode 100644
index 0000000..4863ae2
--- /dev/null
+++ b/libktorrent/kademlia/rpcmsg.h
@@ -0,0 +1,269 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef DHTRPCMSG_H
+#define DHTRPCMSG_H
+
+#include <ksocketaddress.h>
+#include <util/constants.h>
+#include "key.h"
+#include "database.h"
+
+namespace bt
+{
+ class BDictNode;
+}
+
+using bt::Uint8;
+using bt::Uint32;
+
+namespace dht
+{
+ class DHT;
+ class RPCServer;
+
+ enum Type
+ {
+ REQ_MSG,
+ RSP_MSG,
+ ERR_MSG,
+ INVALID
+ };
+
+ enum Method
+ {
+ PING,
+ FIND_NODE,
+ GET_PEERS,
+ ANNOUNCE_PEER,
+ NONE
+ };
+
+
+
+ /**
+ * Base class for all RPC messages.
+ */
+ class MsgBase
+ {
+ public:
+ MsgBase(Uint8 mtid,Method m,Type type,const Key & id);
+ virtual ~MsgBase();
+
+
+ /**
+ * When this message arrives this function will be called upon the DHT.
+ * The message should then call the appropriate DHT function (double dispatch)
+ * @param dh_table Pointer to DHT
+ */
+ virtual void apply(DHT* dh_table) = 0;
+
+ /**
+ * Print the message for debugging purposes.
+ */
+ virtual void print() = 0;
+
+ /**
+ * BEncode the message.
+ * @param arr Data array
+ */
+ virtual void encode(QByteArray & arr) = 0;
+
+ /// Set the origin (i.e. where the message came from)
+ void setOrigin(const KNetwork::KSocketAddress & o) {origin = o;}
+
+ /// Get the origin
+ const KNetwork::KInetSocketAddress & getOrigin() const {return origin;}
+
+ /// Set the origin (i.e. where the message came from)
+ void setDestination(const KNetwork::KSocketAddress & o) {origin = o;}
+
+ /// Get the origin
+ const KNetwork::KInetSocketAddress & getDestination() const {return origin;}
+
+ /// Get the MTID
+ Uint8 getMTID() const {return mtid;}
+
+ /// Set the MTID
+ void setMTID(Uint8 m) {mtid = m;}
+
+ /// Get the id of the sender
+ const Key & getID() const {return id;}
+
+ /// Get the type of the message
+ Type getType() const {return type;}
+
+ /// Get the message it's method
+ Method getMethod() const {return method;}
+
+ protected:
+ Uint8 mtid;
+ Method method;
+ Type type;
+ Key id;
+ KNetwork::KInetSocketAddress origin;
+ };
+
+ /**
+ * Creates a message out of a BDictNode.
+ * @param dict The BDictNode
+ * @param srv The RPCServer
+ * @return A newly created message or 0 upon error
+ */
+ MsgBase* MakeRPCMsg(bt::BDictNode* dict,RPCServer* srv);
+
+ MsgBase* MakeRPCMsgTest(bt::BDictNode* dict,dht::Method req_method);
+
+ class ErrMsg : public MsgBase
+ {
+ public:
+ ErrMsg(Uint8 mtid,const Key & id,const QString & msg);
+ virtual ~ErrMsg();
+
+ virtual void apply(DHT* dh_table);
+ virtual void print();
+ virtual void encode(QByteArray & arr);
+ private:
+ QString msg;
+ };
+
+ class PingReq : public MsgBase
+ {
+ public:
+ PingReq(const Key & id);
+ virtual ~PingReq();
+
+ virtual void apply(DHT* dh_table);
+ virtual void print();
+ virtual void encode(QByteArray & arr);
+ };
+
+ class FindNodeReq : public MsgBase
+ {
+ public:
+ FindNodeReq(const Key & id,const Key & target);
+ virtual ~FindNodeReq();
+
+ virtual void apply(DHT* dh_table);
+ virtual void print();
+ virtual void encode(QByteArray & arr);
+
+ const Key & getTarget() const {return target;}
+
+ private:
+ Key target;
+ };
+
+ class GetPeersReq : public MsgBase
+ {
+ public:
+ GetPeersReq(const Key & id,const Key & info_hash);
+ virtual ~GetPeersReq();
+
+ const Key & getInfoHash() const {return info_hash;}
+ virtual void apply(DHT* dh_table);
+ virtual void print();
+ virtual void encode(QByteArray & arr);
+ protected:
+ Key info_hash;
+ };
+
+ class AnnounceReq : public GetPeersReq
+ {
+ public:
+ AnnounceReq(const Key & id,const Key & info_hash,bt::Uint16 port,const Key & token);
+ virtual ~AnnounceReq();
+
+ virtual void apply(DHT* dh_table);
+ virtual void print();
+ virtual void encode(QByteArray & arr);
+
+ const Key & getToken() const {return token;}
+ bt::Uint16 getPort() const {return port;}
+ private:
+ bt::Uint16 port;
+ Key token;
+ };
+
+ class PingRsp : public MsgBase
+ {
+ public:
+ PingRsp(Uint8 mtid,const Key & id);
+ virtual ~PingRsp();
+
+ virtual void apply(DHT* dh_table);
+ virtual void print();
+ virtual void encode(QByteArray & arr);
+ };
+
+
+
+ class FindNodeRsp : public MsgBase
+ {
+ public:
+ FindNodeRsp(Uint8 mtid,const Key & id,const QByteArray & nodes);
+ virtual ~FindNodeRsp();
+
+ virtual void apply(DHT* dh_table);
+ virtual void print();
+ virtual void encode(QByteArray & arr);
+
+ const QByteArray & getNodes() const {return nodes;}
+ protected:
+ QByteArray nodes;
+ };
+
+ class GetPeersRsp : public MsgBase
+ {
+ public:
+ GetPeersRsp(Uint8 mtid,const Key & id,const QByteArray & data,const Key & token);
+ GetPeersRsp(Uint8 mtid,const Key & id,const DBItemList & values,const Key & token);
+ virtual ~GetPeersRsp();
+
+ virtual void apply(DHT* dh_table);
+ virtual void print();
+ virtual void encode(QByteArray & arr);
+
+ const QByteArray & getData() const {return data;}
+ const DBItemList & getItemList() const {return items;}
+ const Key & getToken() const {return token;}
+ bool containsNodes() const {return data.size() > 0;}
+ bool containsValues() const {return data.size() == 0;}
+ private:
+ Key token;
+ QByteArray data;
+ DBItemList items;
+ };
+
+
+ class AnnounceRsp : public MsgBase
+ {
+ public:
+ AnnounceRsp(Uint8 mtid,const Key & id);
+ virtual ~AnnounceRsp();
+
+ virtual void apply(DHT* dh_table);
+ virtual void print();
+ virtual void encode(QByteArray & arr);
+ };
+
+
+}
+
+#endif
diff --git a/libktorrent/kademlia/rpcserver.cpp b/libktorrent/kademlia/rpcserver.cpp
new file mode 100644
index 0000000..1242dae
--- /dev/null
+++ b/libktorrent/kademlia/rpcserver.cpp
@@ -0,0 +1,243 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <unistd.h>
+#include <string.h>
+#include <net/portlist.h>
+#include <util/log.h>
+#include <util/error.h>
+#include <torrent/globals.h>
+#include <torrent/bnode.h>
+#include <torrent/bdecoder.h>
+#include <torrent/bencoder.h>
+#include <ksocketdevice.h>
+#include "rpcserver.h"
+#include "rpccall.h"
+#include "rpcmsg.h"
+#include "kbucket.h"
+#include "node.h"
+#include "dht.h"
+
+using namespace KNetwork;
+using namespace bt;
+
+namespace dht
+{
+
+
+
+ RPCServer::RPCServer(DHT* dh_table,Uint16 port,QObject *parent) : QObject(parent),dh_table(dh_table),next_mtid(0),port(port)
+ {
+ sock = new KDatagramSocket(this);
+ sock->setBlocking(false);
+ sock->setAddressReuseable(true);
+ }
+
+
+ RPCServer::~RPCServer()
+ {
+ bt::Globals::instance().getPortList().removePort(port,net::UDP);
+ sock->close();
+ calls.setAutoDelete(true);
+ calls.clear();
+ call_queue.setAutoDelete(true);
+ call_queue.clear();
+ }
+
+ void RPCServer::start()
+ {
+ sock->setBlocking(true);
+ if (!sock->bind(QString::null,QString::number(port)))
+ {
+ Out(SYS_DHT|LOG_IMPORTANT) << "DHT: Failed to bind to UDP port " << port << " for DHT" << endl;
+ }
+ else
+ {
+ bt::Globals::instance().getPortList().addNewPort(port,net::UDP,true);
+ }
+ sock->setBlocking(false);
+ connect(sock,SIGNAL(readyRead()),this,SLOT(readPacket()));
+ }
+
+ void RPCServer::stop()
+ {
+ bt::Globals::instance().getPortList().removePort(port,net::UDP);
+ sock->close();
+ }
+
+ static void PrintRawData(const QByteArray & data)
+ {
+ QString tmp;
+ for (Uint32 i = 0;i < data.size();i++)
+ {
+ char c = QChar(data[i]).latin1();
+ if (!QChar(data[i]).isPrint() || c == 0)
+ tmp += '#';
+ else
+ tmp += c;
+ }
+
+ Out(SYS_DHT|LOG_DEBUG) << tmp << endl;
+ }
+
+ void RPCServer::readPacket()
+ {
+ if (sock->bytesAvailable() == 0)
+ {
+ Out(SYS_DHT|LOG_NOTICE) << "0 byte UDP packet " << endl;
+ // KDatagramSocket wrongly handles UDP packets with no payload
+ // so we need to deal with it oursleves
+ int fd = sock->socketDevice()->socket();
+ char tmp;
+ read(fd,&tmp,1);
+ return;
+ }
+
+ KDatagramPacket pck = sock->receive();
+ /*
+ Out() << "RPCServer::readPacket" << endl;
+ PrintRawData(pck.data());
+ */
+ BNode* n = 0;
+ try
+ {
+ // read and decode the packet
+ BDecoder bdec(pck.data(),false);
+ n = bdec.decode();
+
+ if (!n || n->getType() != BNode::DICT)
+ {
+ delete n;
+ return;
+ }
+
+ // try to make a RPCMsg of it
+ MsgBase* msg = MakeRPCMsg((BDictNode*)n,this);
+ if (msg)
+ {
+ msg->setOrigin(pck.address());
+ msg->apply(dh_table);
+ // erase an existing call
+ if (msg->getType() == RSP_MSG && calls.contains(msg->getMTID()))
+ {
+ // delete the call, but first notify it off the response
+ RPCCall* c = calls.find(msg->getMTID());
+ c->response(msg);
+ calls.erase(msg->getMTID());
+ c->deleteLater();
+ doQueuedCalls();
+ }
+ delete msg;
+ }
+ }
+ catch (bt::Error & err)
+ {
+ Out(SYS_DHT|LOG_IMPORTANT) << "Error happened during parsing : " << err.toString() << endl;
+ }
+ delete n;
+
+ if (sock->bytesAvailable() > 0)
+ readPacket();
+ }
+
+
+ void RPCServer::send(const KNetwork::KSocketAddress & addr,const QByteArray & msg)
+ {
+ sock->send(KNetwork::KDatagramPacket(msg,addr));
+ }
+
+ RPCCall* RPCServer::doCall(MsgBase* msg)
+ {
+ Uint8 start = next_mtid;
+ while (calls.contains(next_mtid))
+ {
+ next_mtid++;
+ if (next_mtid == start) // if this happens we cannot do any calls
+ {
+ // so queue the call
+ RPCCall* c = new RPCCall(this,msg,true);
+ call_queue.append(c);
+ Out(SYS_DHT|LOG_NOTICE) << "Queueing RPC call, no slots available at the moment" << endl;
+ return c;
+ }
+ }
+
+ msg->setMTID(next_mtid++);
+ sendMsg(msg);
+ RPCCall* c = new RPCCall(this,msg,false);
+ calls.insert(msg->getMTID(),c);
+ return c;
+ }
+
+ void RPCServer::sendMsg(MsgBase* msg)
+ {
+ QByteArray data;
+ msg->encode(data);
+ send(msg->getDestination(),data);
+
+ // PrintRawData(data);
+ }
+
+ void RPCServer::timedOut(Uint8 mtid)
+ {
+ // delete the call
+ RPCCall* c = calls.find(mtid);
+ if (c)
+ {
+ dh_table->timeout(c->getRequest());
+ calls.erase(mtid);
+ c->deleteLater();
+ }
+ doQueuedCalls();
+ }
+
+ void RPCServer::doQueuedCalls()
+ {
+ while (call_queue.count() > 0 && calls.count() < 256)
+ {
+ RPCCall* c = call_queue.first();
+ call_queue.removeFirst();
+
+ while (calls.contains(next_mtid))
+ next_mtid++;
+
+ MsgBase* msg = c->getRequest();
+ msg->setMTID(next_mtid++);
+ sendMsg(msg);
+ calls.insert(msg->getMTID(),c);
+ c->start();
+ }
+ }
+
+ const RPCCall* RPCServer::findCall(Uint8 mtid) const
+ {
+ return calls.find(mtid);
+ }
+
+ void RPCServer::ping(const dht::Key & our_id,const KNetwork::KSocketAddress & addr)
+ {
+ Out(SYS_DHT|LOG_NOTICE) << "DHT: pinging " << addr.nodeName() << endl;
+ PingReq* pr = new PingReq(our_id);
+ pr->setOrigin(addr);
+ doCall(pr);
+ }
+
+
+}
+#include "rpcserver.moc"
diff --git a/libktorrent/kademlia/rpcserver.h b/libktorrent/kademlia/rpcserver.h
new file mode 100644
index 0000000..4e54076
--- /dev/null
+++ b/libktorrent/kademlia/rpcserver.h
@@ -0,0 +1,122 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef DHTRPCSERVER_H
+#define DHTRPCSERVER_H
+
+#include <qptrlist.h>
+#include <kdatagramsocket.h>
+#include <util/constants.h>
+#include <util/array.h>
+#include <util/ptrmap.h>
+
+
+using KNetwork::KDatagramSocket;
+using bt::Uint32;
+using bt::Uint16;
+using bt::Uint8;
+
+namespace bt
+{
+ class BDictNode;
+}
+
+namespace dht
+{
+ class Key;
+ class KBucketEntry;
+ class RPCCall;
+ class RPCMsg;
+ class Node;
+ class DHT;
+ class MsgBase;
+
+ /**
+ * @author Joris Guisson
+ *
+ * Class to handle incoming and outgoing RPC messages.
+ */
+ class RPCServer : public QObject
+ {
+ Q_OBJECT
+ public:
+ RPCServer(DHT* dh_table,Uint16 port,QObject *parent = 0);
+ virtual ~RPCServer();
+
+ /// Start the server
+ void start();
+
+ /// Stop the server
+ void stop();
+
+ /**
+ * Do a RPC call.
+ * @param msg The message to send
+ * @return The call object
+ */
+ RPCCall* doCall(MsgBase* msg);
+
+ /**
+ * Send a message, this only sends the message, it does not keep any call
+ * information. This should be used for replies.
+ * @param msg The message to send
+ */
+ void sendMsg(MsgBase* msg);
+
+
+ /**
+ * A call was timed out.
+ * @param mtid mtid of call
+ */
+ void timedOut(Uint8 mtid);
+
+ /**
+ * Ping a node, we don't care about the MTID.
+ * @param addr The address
+ */
+ void ping(const dht::Key & our_id,const KNetwork::KSocketAddress & addr);
+
+ /**
+ * Find a RPC call, based on the mtid
+ * @param mtid The mtid
+ * @return The call
+ */
+ const RPCCall* findCall(Uint8 mtid) const;
+
+ /// Get the number of active calls
+ Uint32 getNumActiveRPCCalls() const {return calls.count();}
+ private slots:
+ void readPacket();
+
+ private:
+ void send(const KNetwork::KSocketAddress & addr,const QByteArray & msg);
+ void doQueuedCalls();
+
+ private:
+ KDatagramSocket* sock;
+ DHT* dh_table;
+ bt::PtrMap<bt::Uint8,RPCCall> calls;
+ QPtrList<RPCCall> call_queue;
+ bt::Uint8 next_mtid;
+ bt::Uint16 port;
+ };
+
+}
+
+#endif
diff --git a/libktorrent/kademlia/task.cpp b/libktorrent/kademlia/task.cpp
new file mode 100644
index 0000000..877a698
--- /dev/null
+++ b/libktorrent/kademlia/task.cpp
@@ -0,0 +1,134 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <kresolver.h>
+#include "task.h"
+#include "kclosestnodessearch.h"
+#include "rpcserver.h"
+#include "kbucket.h"
+
+using namespace KNetwork;
+
+namespace dht
+{
+
+ Task::Task(RPCServer* rpc,Node* node)
+ : node(node),rpc(rpc),outstanding_reqs(0),task_finished(false),queued(queued)
+ {
+
+ }
+
+
+ Task::~Task()
+ {
+ }
+
+ void Task::start(const KClosestNodesSearch & kns,bool queued)
+ {
+ // fill the todo list
+ for (KClosestNodesSearch::CItr i = kns.begin(); i != kns.end();i++)
+ todo.append(i->second);
+ this->queued = queued;
+ if (!queued)
+ update();
+ }
+
+ void Task::start()
+ {
+ if (queued)
+ {
+ queued = false;
+ update();
+ }
+ }
+
+
+ void Task::onResponse(RPCCall* c, MsgBase* rsp)
+ {
+ if (outstanding_reqs > 0)
+ outstanding_reqs--;
+
+ if (!isFinished())
+ {
+ callFinished(c,rsp);
+
+ if (canDoRequest() && !isFinished())
+ update();
+ }
+ }
+
+ void Task::onTimeout(RPCCall* c)
+ {
+ if (outstanding_reqs > 0)
+ outstanding_reqs--;
+
+ if (!isFinished())
+ {
+ callTimeout(c);
+
+ if (canDoRequest() && !isFinished())
+ update();
+ }
+ }
+
+ bool Task::rpcCall(MsgBase* req)
+ {
+ if (!canDoRequest())
+ return false;
+
+ RPCCall* c = rpc->doCall(req);
+ c->addListener(this);
+ outstanding_reqs++;
+ return true;
+ }
+
+ void Task::done()
+ {
+ task_finished = true;
+ finished(this);
+ }
+
+ void Task::emitDataReady()
+ {
+ dataReady(this);
+ }
+
+ void Task::kill()
+ {
+ task_finished = true;
+ finished(this);
+ }
+
+ void Task::addDHTNode(const QString & ip,bt::Uint16 port)
+ {
+ KResolver::resolveAsync(this,SLOT(onResolverResults(KResolverResults )),
+ ip,QString::number(port));
+ }
+
+ void Task::onResolverResults(KResolverResults res)
+ {
+ if (res.count() == 0)
+ return;
+
+ todo.append(KBucketEntry(res.front().address(),dht::Key()));
+ }
+
+}
+
+#include "task.moc"
diff --git a/libktorrent/kademlia/task.h b/libktorrent/kademlia/task.h
new file mode 100644
index 0000000..5a33ac0
--- /dev/null
+++ b/libktorrent/kademlia/task.h
@@ -0,0 +1,174 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef DHTTASK_H
+#define DHTTASK_H
+
+#include <qvaluelist.h>
+#include "rpccall.h"
+//#include "kbucket.h"
+
+namespace KNetwork
+{
+ class KResolverResults;
+}
+
+namespace dht
+{
+ class Node;
+ class Task;
+ class KClosestNodesSearch;
+ class KBucketEntry;
+
+ const Uint32 MAX_CONCURRENT_REQS = 16;
+
+ using KNetwork::KResolverResults;
+
+ /**
+ * @author Joris Guisson <joris.guisson@gmail.com>
+ *
+ * Performs a task on K nodes provided by a KClosestNodesSearch.
+ * This is a base class for all tasks.
+ */
+ class Task : public RPCCallListener
+ {
+ Q_OBJECT
+ public:
+ /**
+ * Create a task.
+ * @param rpc The RPC server to do RPC calls
+ * @param node The node
+ */
+ Task(RPCServer* rpc,Node* node);
+ virtual ~Task();
+
+ /**
+ * This will copy the results from the KClosestNodesSearch
+ * object into the todo list. And call update if the task is not queued.
+ * @param kns The KClosestNodesSearch object
+ * @param queued Is the task queued
+ */
+ void start(const KClosestNodesSearch & kns,bool queued);
+
+
+ /**
+ * Start the task, to be used when a task is queued.
+ */
+ void start();
+
+ /// Decrements the outstanding_reqs
+ virtual void onResponse(RPCCall* c, MsgBase* rsp);
+
+ /// Decrements the outstanding_reqs
+ virtual void onTimeout(RPCCall* c);
+
+ /**
+ * Will continue the task, this will be called every time we have
+ * rpc slots available for this task. Should be implemented by derived classes.
+ */
+ virtual void update() = 0;
+
+ /**
+ * A call is finished and a response was received.
+ * @param c The call
+ * @param rsp The response
+ */
+ virtual void callFinished(RPCCall* c, MsgBase* rsp) = 0;
+
+ /**
+ * A call timedout
+ * @param c The call
+ */
+ virtual void callTimeout(RPCCall* c) = 0;
+
+ /**
+ * Do a call to the rpc server, increments the outstanding_reqs variable.
+ * @param req THe request to send
+ * @return true if call was made, false if not
+ */
+ bool rpcCall(MsgBase* req);
+
+ /// See if we can do a request
+ bool canDoRequest() const {return outstanding_reqs < MAX_CONCURRENT_REQS;}
+
+ /// Is the task finished
+ bool isFinished() const {return task_finished;}
+
+ /// Set the task ID
+ void setTaskID(bt::Uint32 tid) {task_id = tid;}
+
+ /// Get the task ID
+ bt::Uint32 getTaskID() const {return task_id;}
+
+ /// Get the number of outstanding requests
+ bt::Uint32 getNumOutstandingRequests() const {return outstanding_reqs;}
+
+ bool isQueued() const {return queued;}
+
+ /**
+ * Tell listeners data is ready.
+ */
+ void emitDataReady();
+
+ /// Kills the task
+ void kill();
+
+ /**
+ * Add a node to the todo list
+ * @param ip The ip or hostname of the node
+ * @param port The port
+ */
+ void addDHTNode(const QString & ip,bt::Uint16 port);
+
+ signals:
+ /**
+ * The task is finsihed.
+ * @param t The Task
+ */
+ void finished(Task* t);
+
+ /**
+ * Called by the task when data is ready.
+ * Can be overrided if wanted.
+ * @param t The Task
+ */
+ void dataReady(Task* t);
+
+ protected:
+ void done();
+
+ protected slots:
+ void onResolverResults(KResolverResults res);
+
+ protected:
+ QValueList<KBucketEntry> visited; // nodes visited
+ QValueList<KBucketEntry> todo; // nodes todo
+ Node* node;
+
+ private:
+ RPCServer* rpc;
+ bt::Uint32 outstanding_reqs;
+ bt::Uint32 task_id;
+ bool task_finished;
+ bool queued;
+ };
+
+}
+
+#endif
diff --git a/libktorrent/kademlia/taskmanager.cpp b/libktorrent/kademlia/taskmanager.cpp
new file mode 100644
index 0000000..f71fc0d
--- /dev/null
+++ b/libktorrent/kademlia/taskmanager.cpp
@@ -0,0 +1,79 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <util/log.h>
+#include <torrent/globals.h>
+#include "taskmanager.h"
+#include "nodelookup.h"
+#include "dht.h"
+
+using namespace bt;
+
+namespace dht
+{
+ typedef bt::PtrMap<Uint32,Task>::iterator TaskItr;
+
+ TaskManager::TaskManager() : next_id(0)
+ {
+ tasks.setAutoDelete(true);
+ }
+
+
+ TaskManager::~TaskManager()
+ {
+ queued.setAutoDelete(true);
+ tasks.clear();
+ }
+
+
+ void TaskManager::addTask(Task* task)
+ {
+ Uint32 id = next_id++;
+ task->setTaskID(id);
+ if (task->isQueued())
+ queued.append(task);
+ else
+ tasks.insert(id,task);
+ }
+
+ void TaskManager::removeFinishedTasks(const DHT* dh_table)
+ {
+ QValueList<Uint32> rm;
+ for (TaskItr i = tasks.begin();i != tasks.end();i++)
+ {
+ if (i->second->isFinished())
+ rm.append(i->first);
+ }
+
+ for (QValueList<Uint32>::iterator i = rm.begin();i != rm.end();i++)
+ {
+ tasks.erase(*i);
+ }
+
+ while (dh_table->canStartTask() && queued.count() > 0)
+ {
+ Task* t = queued.first();
+ queued.removeFirst();
+ Out(SYS_DHT|LOG_NOTICE) << "DHT: starting queued task" << endl;
+ t->start();
+ tasks.insert(t->getTaskID(),t);
+ }
+ }
+
+}
diff --git a/libktorrent/kademlia/taskmanager.h b/libktorrent/kademlia/taskmanager.h
new file mode 100644
index 0000000..3df52b6
--- /dev/null
+++ b/libktorrent/kademlia/taskmanager.h
@@ -0,0 +1,69 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef DHTTASKMANAGER_H
+#define DHTTASKMANAGER_H
+
+#include <qptrlist.h>
+#include <util/ptrmap.h>
+#include <util/constants.h>
+#include "task.h"
+
+namespace dht
+{
+ class DHT;
+
+ /**
+ * @author Joris Guisson <joris.guisson@gmail.com>
+ *
+ * Manages all dht tasks.
+ */
+ class TaskManager
+ {
+ public:
+ TaskManager();
+ virtual ~TaskManager();
+
+ /**
+ * Add a task to manage.
+ * @param task
+ */
+ void addTask(Task* task);
+
+ /**
+ * Remove all finished tasks.
+ * @param dh_table Needed to ask permission to start a task
+ */
+ void removeFinishedTasks(const DHT* dh_table);
+
+ /// Get the number of running tasks
+ bt::Uint32 getNumTasks() const {return tasks.count();}
+
+ /// Get the number of queued tasks
+ bt::Uint32 getNumQueuedTasks() const {return queued.count();}
+
+ private:
+ bt::PtrMap<Uint32,Task> tasks;
+ QPtrList<Task> queued;
+ bt::Uint32 next_id;
+ };
+
+}
+
+#endif
diff --git a/libktorrent/ktorrent.kcfg b/libktorrent/ktorrent.kcfg
new file mode 100644
index 0000000..7d451b3
--- /dev/null
+++ b/libktorrent/ktorrent.kcfg
@@ -0,0 +1,233 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<kcfg xmlns="http://www.kde.org/standards/kcfg/1.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://www.kde.org/standards/kcfg/1.0
+ http://www.kde.org/standards/kcfg/1.0/kcfg.xsd" >
+
+ <kcfgfile name="ktorrentrc"/>
+ <group name="downloads">
+ <entry name="maxDownloads" type="Int">
+ <label>Maximum number of downloads (0 = no limit)</label>
+ <default>0</default>
+ <min>0</min>
+ </entry>
+ <entry name="maxSeeds" type="Int">
+ <label>Maximum number of seeds (0 = no limit)</label>
+ <default>0</default>
+ <min>0</min>
+ </entry>
+ <entry name="startDownloadsOnLowDiskSpace" type="Int">
+ <label>Start downloads on low disk space?</label>
+ <default>0</default>
+ </entry>
+ <entry name="maxConnections" type="Int">
+ <label>Maximum number of connections per torrent (0 = no limit)</label>
+ <default>120</default>
+ <min>0</min>
+ </entry>
+ <entry name="maxTotalConnections" type="Int">
+ <label>Maximum number of connections for all torrents (0 = no limit) </label>
+ <default>800</default>
+ <min>0</min>
+ </entry>
+ <entry name="maxUploadRate" type="Int">
+ <label>Maximum upload speed in KB/sec (0 = no limit)</label>
+ <default>0</default>
+ <min>0</min>
+ </entry>
+ <entry name="maxDownloadRate" type="Int">
+ <label>Maximum download speed in KB/sec (0 = no limit)</label>
+ <default>0</default>
+ <min>0</min>
+ </entry>
+ <entry name="maxRatio" type="Double">
+ <label>Maximum share ratio(0 = no limit)</label>
+ <default>0</default>
+ <min>0</min>
+ </entry>
+ <entry name="maxSeedTime" type="Double">
+ <label>Maximum seed time in hours (0 = no limit)</label>
+ <default>0</default>
+ <min>0</min>
+ </entry>
+ <entry name="port" type="Int">
+ <label>Port</label>
+ <default>6881</default>
+ <min>0</min>
+ <max>65535</max>
+ </entry>
+ <entry name="udpTrackerPort" type="Int">
+ <label>Port</label>
+ <default>4444</default>
+ <min>0</min>
+ <max>65535</max>
+ </entry>
+ <entry name="showSystemTrayIcon" type="Bool">
+ <label>Show a system tray icon</label>
+ <default>true</default>
+ </entry>
+ <entry name="showSpeedBarInTrayIcon" type="Bool">
+ <label>Show speed bar in tray icon</label>
+ <default>false</default>
+ </entry>
+ <entry name="downloadBandwidth" type="Int">
+ <label>Download bandwidth (in kb/s):</label>
+ <default>500</default>
+ <min>0</min>
+ <max>1000000</max>
+ </entry>
+ <entry name="uploadBandwidth" type="Int">
+ <label>Upload bandwidth (in kb/s):</label>
+ <default>500</default>
+ <min>0</min>
+ <max>1000000</max>
+ </entry>
+ <entry name="showPopups" type="Bool">
+ <label>Show popup messages when torrent is finished.</label>
+ <default>true</default>
+ </entry>
+ <entry name="keepSeeding" type="Bool">
+ <label>Keep seeding after download has finished</label>
+ <default>true</default>
+ </entry>
+ <entry name="tempDir" type="String">
+ <label>Folder to store temporary files</label>
+ <default code="true">QString::null</default>
+ </entry>
+ <entry name="useSaveDir" type="Bool">
+ <label>Whether to automatically save downloads to saveDir</label>
+ <default>false</default>
+ </entry>
+ <entry name="saveDir" type="String">
+ <label>Folder to store downloaded files</label>
+ <default code="true">QString::null</default>
+ </entry>
+ <entry name="useCompletedDir" type="Bool">
+ <label>Whether to automatically move completed downloads to completedDir</label>
+ <default>false</default>
+ </entry>
+ <entry name="completedDir" type="String">
+ <label>Folder to move completed downloaded files to</label>
+ <default code="true">QString::null</default>
+ </entry>
+ <entry name="useTorrentCopyDir" type="Bool">
+ <label>Whether to automatically copy .torrent files to torrentCopyDir</label>
+ <default>false</default>
+ </entry>
+ <entry name="torrentCopyDir" type="String">
+ <label>Folder to copy .torrent files to</label>
+ <default code="true">QString::null</default>
+ </entry>
+ <entry name="useExternalIP" type="Bool">
+ <label>Whether to use a custom IP to pass to the tracker</label>
+ <default>false</default>
+ </entry>
+ <entry name="lastSaveDir" type="String">
+ <label>Directory which was used as the last save directory</label>
+ <default code="true">QString::null</default>
+ </entry>
+ <entry name="externalIP" type="String">
+ <label>IP to pass to the tracker</label>
+ <default code="true">QString::null</default>
+ </entry>
+ <entry name="memoryUsage" type="Int">
+ <label>Memory usage</label>
+ <default>0</default>
+ </entry>
+ <entry name="guiUpdateInterval" type="Int">
+ <label>GUI update interval</label>
+ <default>0</default>
+ </entry>
+ <entry name="dhtSupport" type="Bool">
+ <label>Support for DHT</label>
+ <default>false</default>
+ </entry>
+ <entry name="dhtPort" type="Int">
+ <label>DHT port</label>
+ <default>6881</default>
+ <min>0</min>
+ <max>65535</max>
+ </entry>
+ <entry name="numUploadSlots" type="Int">
+ <label>Number of upload slots</label>
+ <default>2</default>
+ <min>2</min>
+ <max>100</max>
+ </entry>
+ <entry name="useEncryption" type="Bool">
+ <label>Use protocol encryption</label>
+ <default>false</default>
+ </entry>
+ <entry name="allowUnencryptedConnections" type="Bool">
+ <label>Allow unencrypted connections</label>
+ <default>true</default>
+ </entry>
+ <entry name="allwaysDoUploadDataCheck" type="Bool">
+ <default>true</default>
+ </entry>
+ <entry name="maxSizeForUploadDataCheck" type="Int">
+ <default>512</default>
+ <min>128</min>
+ <max>8192</max>
+ </entry>
+ <entry name="typeOfService" type="Int">
+ <default>8</default>
+ <min>0</min>
+ <max>255</max>
+ </entry>
+ <entry name="DSCP" type="Int">
+ <default>0</default>
+ <min>0</min>
+ <max>63</max>
+ </entry>
+ <entry name="maxConnectingSockets" type="Int">
+ <default>50</default>
+ <min>10</min>
+ <max>500</max>
+ </entry>
+ <entry name="autoRecheck" type="Bool">
+ <default>true</default>
+ </entry>
+ <entry name="maxCorruptedBeforeRecheck" type="Int">
+ <default>3</default>
+ <min>1</min>
+ </entry>
+ <entry name="shownColumns" type="IntList">
+ <label>Columns shown in KTorrentView</label>
+ </entry>
+ <entry name="doNotUseKDEProxy" type="Bool">
+ <default>false</default>
+ </entry>
+ <entry name="httpTrackerProxy" type="String">
+ <default code="true">QString::null</default>
+ </entry>
+ <entry name="eta" type="Int">
+ <label>ET algorithm</label>
+ <default>0</default>
+ </entry>
+ <entry name="diskPrealloc" type="Bool">
+ <default>true</default>
+ </entry>
+
+ <entry name="fullDiskPrealloc" type="Bool">
+ <default>false</default>
+ </entry>
+
+ <entry name="fullDiskPreallocMethod" type="Int">
+ <default>0</default>
+ </entry>
+
+ <entry name="cpuUsage" type="Int">
+ <default>25</default>
+ <min>1</min>
+ <max>50</max>
+ </entry>
+
+ <entry name="minDiskSpace" type="Int">
+ <label>When there's no space left to complete download and free diskspace is less than minDiskSpace, torrent will be stopped.</label>
+ <default>100</default>
+ <min>10</min>
+ <max>10000</max>
+ </entry>
+ </group>
+</kcfg>
diff --git a/libktorrent/ktversion.h b/libktorrent/ktversion.h
new file mode 100644
index 0000000..ffe3dbe
--- /dev/null
+++ b/libktorrent/ktversion.h
@@ -0,0 +1,36 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef KTVERSION_HH
+#define KTVERSION_HH
+
+
+#include "util/constants.h"
+
+namespace kt
+{
+ const bt::Uint32 MAJOR = 2;
+ const bt::Uint32 MINOR = 2;
+ const char VERSION_STRING[] = "2.2.6";
+ const char PEER_ID[] = "-KT2260-";
+}
+
+#define KT_VERSION_MACRO "2.2.6"
+
+#endif
diff --git a/libktorrent/labelview.cpp b/libktorrent/labelview.cpp
new file mode 100644
index 0000000..10c46d5
--- /dev/null
+++ b/libktorrent/labelview.cpp
@@ -0,0 +1,257 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <algorithm>
+#include <qlayout.h>
+#include <qlabel.h>
+#include <kiconloader.h>
+#include <kglobalsettings.h>
+#include <util/log.h>
+#include "labelview.h"
+
+using namespace bt;
+
+namespace kt
+{
+ LabelViewItem::LabelViewItem(const QString & icon,const QString & title,const QString & description,LabelView* view)
+ : LabelViewItemBase(view),odd(false),selected(false)
+ {
+ icon_lbl->setPixmap(DesktopIcon(icon));
+ title_lbl->setText(title);
+ description_lbl->setText(description);
+ setOdd(false);
+ }
+
+ LabelViewItem::~LabelViewItem()
+ {
+ }
+
+ void LabelViewItem::setTitle(const QString & title)
+ {
+ title_lbl->setText(title);
+ }
+
+ void LabelViewItem::setDescription(const QString & d)
+ {
+ description_lbl->setText(d);
+ }
+
+ void LabelViewItem::setIcon(const QString & icon)
+ {
+ icon_lbl->setPixmap(DesktopIcon(icon));
+ }
+
+ void LabelViewItem::setOdd(bool o)
+ {
+ odd = o;
+ setSelected(selected);
+ }
+
+ void LabelViewItem::setSelected(bool sel)
+ {
+ selected = sel;
+
+ if (selected)
+ {
+ setPaletteBackgroundColor(KGlobalSettings::highlightColor());
+ setPaletteForegroundColor(KGlobalSettings::highlightedTextColor());
+ }
+ else if (odd)
+ {
+ setPaletteBackgroundColor(KGlobalSettings::baseColor());
+ setPaletteForegroundColor(KGlobalSettings::textColor());
+ }
+ else
+ {
+ setPaletteBackgroundColor(KGlobalSettings::alternateBackgroundColor());
+ setPaletteForegroundColor(KGlobalSettings::textColor());
+ }
+ }
+
+ bool LabelViewItem::operator < (const LabelViewItem & item)
+ {
+ return title_lbl->text() < item.title_lbl->text();
+ }
+
+ void LabelViewItem::mousePressEvent(QMouseEvent *e)
+ {
+ if (e->button() == QMouseEvent::LeftButton)
+ {
+ clicked(this);
+ }
+
+ setFocus();
+ QWidget::mousePressEvent(e);
+ }
+
+ typedef std::list<LabelViewItem*>::iterator LabelViewItr;
+ typedef std::list<LabelViewItem*>::const_iterator LabelViewCItr;
+
+ class LabelViewBox : public QWidget
+ {
+ QVBoxLayout* layout;
+ public:
+ LabelViewBox(QWidget* parent) : QWidget(parent)
+ {
+ setPaletteBackgroundColor(KGlobalSettings::baseColor());
+ layout = new QVBoxLayout(this);
+ layout->setMargin(0);
+ }
+
+ virtual ~LabelViewBox()
+ {}
+
+ void add(LabelViewItem* item)
+ {
+ item->reparent(this,QPoint(0,0));
+ layout->add(item);
+ item->show();
+ }
+
+ void remove(LabelViewItem* item)
+ {
+ item->hide();
+ layout->remove(item);
+ item->reparent(0,QPoint(0,0));
+ }
+
+ void sorted(const std::list<LabelViewItem*> items)
+ {
+ for (LabelViewCItr i = items.begin();i != items.end();i++)
+ layout->remove(*i);
+
+ for (LabelViewCItr i = items.begin();i != items.end();i++)
+ layout->add(*i);
+ }
+ };
+
+
+
+ ///////////////////////////////////////
+
+ LabelView::LabelView ( QWidget *parent, const char *name )
+ : QScrollView ( parent, name ),selected(0)
+ {
+ item_box = new LabelViewBox(this->viewport());
+ setResizePolicy(QScrollView::AutoOneFit);
+
+ addChild(item_box, 0, 0);
+ item_box->show();
+ }
+
+
+ LabelView::~LabelView()
+ {}
+
+ void LabelView::addItem(LabelViewItem* item)
+ {
+ item_box->add(item);
+ items.push_back(item);
+ item->setOdd(items.size() % 2 == 1);
+
+ connect(item, SIGNAL(clicked(LabelViewItem*)),
+ this, SLOT(onItemClicked(LabelViewItem*)));
+ }
+
+ void LabelView::removeItem(LabelViewItem* item)
+ {
+ LabelViewItr i = std::find(items.begin(),items.end(),item);
+ if (i != items.end())
+ {
+ item_box->remove(item);
+ items.erase(i);
+ disconnect(item, SIGNAL(clicked(LabelViewItem*)),
+ this, SLOT(onItemClicked(LabelViewItem*)));
+
+ // check for selected being equal to item
+ if (item == selected)
+ selected = 0;
+
+ // update odd status of each item
+ updateOddStatus();
+ }
+ }
+
+ void LabelView::updateOddStatus()
+ {
+ bool odd = true;
+ LabelViewItr i = items.begin();
+ while (i != items.end())
+ {
+ LabelViewItem* item = *i;
+ item->setOdd(odd);
+ odd = !odd;
+ i++;
+ }
+ }
+
+ void LabelView::onItemClicked(LabelViewItem* it)
+ {
+ if (selected == it)
+ return;
+
+ if (selected)
+ selected->setSelected(false);
+
+ selected = it;
+ selected->setSelected(true);
+ currentChanged(selected);
+ }
+
+ void LabelView::clear()
+ {
+ LabelViewItr i = items.begin();
+ while (i != items.end())
+ {
+ LabelViewItem* item = *i;
+ item_box->remove(item);
+ i = items.erase(i);
+ delete item;
+ }
+ selected = 0;
+ }
+
+ void LabelView::update()
+ {
+ LabelViewItr i = items.begin();
+ while (i != items.end())
+ {
+ LabelViewItem* item = *i;
+ item->update();
+ i++;
+ }
+ }
+
+ struct LabelViewItemCmp
+ {
+ bool operator() (LabelViewItem* a,LabelViewItem* b)
+ {
+ return *a < *b;
+ }
+ };
+
+ void LabelView::sort()
+ {
+ items.sort(LabelViewItemCmp());
+ item_box->sorted(items);
+ updateOddStatus();
+ }
+
+}
+#include "labelview.moc"
diff --git a/libktorrent/labelview.h b/libktorrent/labelview.h
new file mode 100644
index 0000000..5e83213
--- /dev/null
+++ b/libktorrent/labelview.h
@@ -0,0 +1,125 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef KTLABELVIEW_H
+#define KTLABELVIEW_H
+
+#include <list>
+#include <qscrollview.h>
+#include "labelviewitembase.h"
+
+class QLabel;
+class QHBoxLayout;
+class QVBoxLayout;
+
+namespace kt
+{
+ class LabelView;
+
+ /**
+ Item in a LabelView
+ */
+ class LabelViewItem : public LabelViewItemBase
+ {
+ Q_OBJECT
+ public:
+ LabelViewItem(const QString & icon,const QString & title,const QString & description,LabelView* view);
+ virtual ~LabelViewItem();
+
+ /// Set the title of the item
+ void setTitle(const QString & title);
+
+ /// Set the description
+ void setDescription(const QString & d);
+
+ /// Set the name of the icon
+ void setIcon(const QString & icon);
+
+ /// Set if this is an odd item (they have a different background color)
+ void setOdd(bool odd);
+
+ /// Set if this item is selected
+ void setSelected(bool sel);
+
+ /// Can be reimplemented to update the GUI of the item by base classes
+ virtual void update() {}
+
+ /// Smaller then operator for sorting (by default we sort on title)
+ virtual bool operator < (const LabelViewItem & item);
+
+ private:
+ virtual void mousePressEvent(QMouseEvent *e);
+
+ signals:
+ void clicked(LabelViewItem* item);
+
+ private:
+ bool odd;
+ bool selected;
+ };
+
+ class LabelViewBox;
+
+ /**
+ @author Joris Guisson <joris.guisson@gmail.com>
+ */
+ class LabelView : public QScrollView
+ {
+ Q_OBJECT
+ public:
+ LabelView(QWidget *parent = 0, const char *name = 0);
+ virtual ~LabelView();
+
+ /// Add an item to the label view
+ void addItem(LabelViewItem* item);
+
+ /// Remove an item from the label view
+ void removeItem(LabelViewItem* item);
+
+ /// Get the current selected item (0 if none is selected)
+ LabelViewItem* selectedItem() {return selected;}
+
+ /// Clear the view
+ void clear();
+
+ /// Update all items in the view
+ void update();
+
+ /// Sort the items using the operator <
+ void sort();
+
+ private slots:
+ void onItemClicked(LabelViewItem* it);
+
+ private:
+ void updateOddStatus();
+
+ signals:
+ /// The current item has changed
+ void currentChanged(LabelViewItem* item);
+
+ private:
+ LabelViewBox* item_box;
+ std::list<LabelViewItem*> items;
+ LabelViewItem* selected;
+ };
+
+}
+
+#endif
diff --git a/libktorrent/labelviewitembase.ui b/libktorrent/labelviewitembase.ui
new file mode 100644
index 0000000..174803f
--- /dev/null
+++ b/libktorrent/labelviewitembase.ui
@@ -0,0 +1,73 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>LabelViewItemBase</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>LabelViewItemBase</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>600</width>
+ <height>100</height>
+ </rect>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>2</number>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>icon_lbl</cstring>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>64</width>
+ <height>64</height>
+ </size>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout3</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>title_lbl</cstring>
+ </property>
+ <property name="text">
+ <string>textLabel2</string>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>description_lbl</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>7</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>textLabel3</string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ </hbox>
+</widget>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/libktorrent/migrate/Makefile.am b/libktorrent/migrate/Makefile.am
new file mode 100644
index 0000000..9bb5528
--- /dev/null
+++ b/libktorrent/migrate/Makefile.am
@@ -0,0 +1,7 @@
+INCLUDES = -I$(srcdir)/.. $(all_includes)
+METASOURCES = AUTO
+libmigrate_la_LDFLAGS = $(all_libraries)
+noinst_LTLIBRARIES = libmigrate.la
+noinst_HEADERS = migrate.h ccmigrate.h cachemigrate.h
+libmigrate_la_SOURCES = migrate.cpp ccmigrate.cpp cachemigrate.cpp
+KDE_CXXFLAGS = $(USE_EXCEPTIONS) $(USE_RTTI)
diff --git a/libktorrent/migrate/cachemigrate.cpp b/libktorrent/migrate/cachemigrate.cpp
new file mode 100644
index 0000000..f9b203c
--- /dev/null
+++ b/libktorrent/migrate/cachemigrate.cpp
@@ -0,0 +1,120 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <qstringlist.h>
+#include <qfileinfo.h>
+#include <util/log.h>
+#include <util/fileops.h>
+#include <util/functions.h>
+#include <torrent/torrent.h>
+#include <torrent/globals.h>
+#include "cachemigrate.h"
+
+
+namespace bt
+{
+
+ bool IsCacheMigrateNeeded(const Torrent & tor,const QString & cache)
+ {
+ // mutli files always need to be migrated
+ if (tor.isMultiFile())
+ return true;
+
+ // a single file and a symlink do not need to be migrated
+ QFileInfo finfo(cache);
+ if (finfo.isSymLink())
+ return false;
+
+ return true;
+ }
+
+ static void MigrateSingleCache(const Torrent & tor,const QString & cache,const QString & output_dir)
+ {
+ Out() << "Migrating single cache " << cache << " to " << output_dir << endl;
+
+ bt::Move(cache,output_dir + tor.getNameSuggestion());
+ bt::SymLink(output_dir + tor.getNameSuggestion(),cache);
+ }
+
+ static void MakePath(const QString & startdir,const QString & path)
+ {
+ QStringList sl = QStringList::split(bt::DirSeparator(),path);
+
+ // create all necessary subdirs
+ QString ctmp = startdir;
+
+ for (Uint32 i = 0;i < sl.count() - 1;i++)
+ {
+ ctmp += sl[i];
+ // we need to make the same directory structure in the cache
+ // as the output dir
+ if (!bt::Exists(ctmp))
+ MakeDir(ctmp);
+
+ ctmp += bt::DirSeparator();
+ }
+ }
+
+ static void MigrateMultiCache(const Torrent & tor,const QString & cache,const QString & output_dir)
+ {
+ Out() << "Migrating multi cache " << cache << " to " << output_dir << endl;
+ // if the cache dir is a symlink, everything is OK
+ if (QFileInfo(cache).isSymLink())
+ return;
+
+ QString cache_dir = cache;
+
+
+ // make the output dir if it does not exists
+ if (!bt::Exists(output_dir + tor.getNameSuggestion()))
+ bt::MakeDir(output_dir + tor.getNameSuggestion());
+
+ QString odir = output_dir + tor.getNameSuggestion() + bt::DirSeparator();
+ QString cdir = cache;
+ if (!cdir.endsWith(bt::DirSeparator()))
+ cdir += bt::DirSeparator();
+
+ // loop over all files in the cache and see if they are symlinks
+ for (Uint32 i = 0;i < tor.getNumFiles();i++)
+ {
+ const TorrentFile & tf = tor.getFile(i);
+ QFileInfo fi(cdir + tf.getPath());
+ // symlinks are OK
+ if (fi.isSymLink())
+ continue;
+ // make the path if necessary
+ MakePath(odir,tf.getPath());
+ // no symlink so move to output_dir
+ bt::Move(cdir + tf.getPath(),odir + tf.getPath());
+ bt::SymLink(odir + tf.getPath(),cdir + tf.getPath());
+ }
+ }
+
+ void MigrateCache(const Torrent & tor,const QString & cache,const QString & output_dir)
+ {
+ QString odir = output_dir;
+ if (!odir.endsWith(bt::DirSeparator()))
+ odir += bt::DirSeparator();
+
+ if (!tor.isMultiFile())
+ MigrateSingleCache(tor,cache,odir);
+ else
+ MigrateMultiCache(tor,cache,odir);
+ }
+}
diff --git a/libktorrent/migrate/cachemigrate.h b/libktorrent/migrate/cachemigrate.h
new file mode 100644
index 0000000..3eea231
--- /dev/null
+++ b/libktorrent/migrate/cachemigrate.h
@@ -0,0 +1,34 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef BTCACHEMIGRATE_H
+#define BTCACHEMIGRATE_H
+
+namespace bt
+{
+ class Torrent;
+
+ /// See if a cache migrate is needed
+ bool IsCacheMigrateNeeded(const Torrent & tor,const QString & cache);
+
+ /// Migrate the cache
+ void MigrateCache(const Torrent & tor,const QString & cache,const QString & output_dir);
+}
+
+#endif
diff --git a/libktorrent/migrate/ccmigrate.cpp b/libktorrent/migrate/ccmigrate.cpp
new file mode 100644
index 0000000..80153bf
--- /dev/null
+++ b/libktorrent/migrate/ccmigrate.cpp
@@ -0,0 +1,167 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <klocale.h>
+#include <util/log.h>
+#include <util/file.h>
+#include <util/error.h>
+#include <util/array.h>
+#include <util/bitset.h>
+#include <util/fileops.h>
+#include <torrent/downloader.h>
+#include <torrent/torrent.h>
+#include <torrent/globals.h>
+#include <torrent/chunkdownload.h>
+#include <ktversion.h>
+#include "ccmigrate.h"
+
+namespace bt
+{
+ bool IsPreMMap(const QString & current_chunks)
+ {
+ File fptr;
+ if (!fptr.open(current_chunks,"rb"))
+ return false;
+
+ CurrentChunksHeader chdr;
+ fptr.read(&chdr,sizeof(CurrentChunksHeader));
+ if (chdr.magic != CURRENT_CHUNK_MAGIC)
+ {
+ // magic number not good, so pre
+ return true;
+ }
+
+ if (chdr.major >= 2 || (chdr.major == 1 && chdr.minor >= 2))
+ {
+ // version number is 1.2 or greater
+ return false;
+ }
+
+ return false;
+ }
+
+ static bool MigrateChunk(const Torrent & tor,File & new_cc,File & old_cc)
+ {
+ Uint32 ch = 0;
+ old_cc.read(&ch,sizeof(Uint32));
+
+ Out() << "Migrating chunk " << ch << endl;
+ if (ch >= tor.getNumChunks())
+ return false;
+
+ // calculate the size
+ Uint32 csize = 0;
+ if (ch == tor.getNumChunks() - 1)
+ {
+ // ch is the last chunk, so it might have a different size
+ csize = tor.getFileLength() % tor.getChunkSize();
+ if (ch == 0)
+ csize = tor.getChunkSize();
+ }
+ else
+ {
+ csize = tor.getChunkSize();
+ }
+
+ // calculate the number of pieces
+ Uint32 num_pieces = csize / MAX_PIECE_LEN;
+ if (csize % MAX_PIECE_LEN > 0)
+ num_pieces++;
+
+ // load the pieces array
+ Array<bool> pieces(num_pieces);
+ old_cc.read(pieces,sizeof(bool)*num_pieces);
+
+ // convert bool array to bitset
+ BitSet pieces_bs(num_pieces);
+ for (Uint32 i = 0;i < num_pieces;i++)
+ pieces_bs.set(i,pieces[i]);
+
+ // load the actual data
+ Array<Uint8> data(csize);
+ old_cc.read(data,csize);
+
+ // write to the new file
+ ChunkDownloadHeader hdr;
+ hdr.index = ch;
+ hdr.num_bits = num_pieces;
+ hdr.buffered = 1; // by default we will use buffered chunks
+ // save the chunk header
+ new_cc.write(&hdr,sizeof(ChunkDownloadHeader));
+ // save the bitset
+ new_cc.write(pieces_bs.getData(),pieces_bs.getNumBytes());
+ new_cc.write(data,csize);
+ return true;
+ }
+
+ static void MigrateCC(const Torrent & tor,const QString & current_chunks)
+ {
+ Out() << "Migrating current_chunks file " << current_chunks << endl;
+ // open the old current_chunks file
+ File old_cc;
+ if (!old_cc.open(current_chunks,"rb"))
+ throw Error(i18n("Cannot open file %1 : %2").arg(current_chunks).arg(old_cc.errorString()));
+
+ // open a new file in the /tmp dir
+ File new_cc;
+ QString tmp = current_chunks + ".tmp";
+ if (!new_cc.open(tmp,"wb"))
+ throw Error(i18n("Cannot open file %1 : %2").arg(tmp).arg(old_cc.errorString()));
+
+ // read the number of chunks
+ Uint32 num = 0;
+ old_cc.read(&num,sizeof(Uint32));
+ Out() << "Found " << num << " chunks" << endl;
+
+ // write the new current_chunks header
+ CurrentChunksHeader hdr;
+ hdr.magic = CURRENT_CHUNK_MAGIC;
+ hdr.major = kt::MAJOR;
+ hdr.minor = kt::MINOR;
+ hdr.num_chunks = num;
+ new_cc.write(&hdr,sizeof(CurrentChunksHeader));
+
+ for (Uint32 i = 0;i < num;i++)
+ {
+ if (!MigrateChunk(tor,new_cc,old_cc))
+ break;
+ }
+
+ // migrate done, close both files and move new_cc to old_cc
+ new_cc.close();
+ old_cc.close();
+ bt::Delete(current_chunks);
+ bt::Move(tmp,current_chunks);
+ }
+
+ void MigrateCurrentChunks(const Torrent & tor,const QString & current_chunks)
+ {
+ try
+ {
+ MigrateCC(tor,current_chunks);
+ }
+ catch (...)
+ {
+ // cleanup tmp files upon error
+ bt::Delete("/tmp/kt_current_chunks",true);
+ throw;
+ }
+ }
+
+}
diff --git a/libktorrent/migrate/ccmigrate.h b/libktorrent/migrate/ccmigrate.h
new file mode 100644
index 0000000..890bdfa
--- /dev/null
+++ b/libktorrent/migrate/ccmigrate.h
@@ -0,0 +1,36 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef BTCCMIGRATE_H
+#define BTCCMIGRATE_H
+
+namespace bt
+{
+ class Torrent;
+
+ /// Migrates the current_chunks file to the post-mmap era.
+ void MigrateCurrentChunks(const Torrent & tor,const QString & current_chunks);
+
+
+ /// Test if a current_chunks file is from the pre-mmap period
+ bool IsPreMMap(const QString & current_chunks);
+
+}
+
+#endif
diff --git a/libktorrent/migrate/migrate.cpp b/libktorrent/migrate/migrate.cpp
new file mode 100644
index 0000000..eddde83
--- /dev/null
+++ b/libktorrent/migrate/migrate.cpp
@@ -0,0 +1,75 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <kurl.h>
+#include <klocale.h>
+#include <util/log.h>
+#include <util/error.h>
+#include <util/fileops.h>
+#include <util/functions.h>
+#include <torrent/globals.h>
+#include "migrate.h"
+#include "ccmigrate.h"
+#include "cachemigrate.h"
+
+namespace bt
+{
+
+ Migrate::Migrate()
+ {}
+
+
+ Migrate::~Migrate()
+ {}
+
+ void Migrate::migrate(const Torrent & tor,const QString & tor_dir,const QString & sdir)
+ {
+ // check if directory exists
+ if (!bt::Exists(tor_dir))
+ throw Error(i18n("The directory %1 does not exist").arg(tor_dir));
+
+ // make sure it ends with a /
+ QString tdir = tor_dir;
+ if (!tdir.endsWith(bt::DirSeparator()))
+ tdir += bt::DirSeparator();
+
+ // see if the current_chunks file exists
+ if (bt::Exists(tdir + "current_chunks"))
+ {
+ // first see if it isn't a download started by a post-mmap version
+ if (!IsPreMMap(tdir + "current_chunks"))
+ {
+ // it's not pre, so it must be post, so just return
+ Out() << "No migrate needed" << endl;
+ return;
+ }
+
+ MigrateCurrentChunks(tor,tdir + "current_chunks");
+ }
+
+ // now we need to migrate t
+ if (IsCacheMigrateNeeded(tor,tdir + "cache" + bt::DirSeparator()))
+ {
+ MigrateCache(tor,tdir + "cache" + bt::DirSeparator(),sdir);
+ }
+ }
+
+
+
+}
diff --git a/libktorrent/migrate/migrate.h b/libktorrent/migrate/migrate.h
new file mode 100644
index 0000000..ef862ec
--- /dev/null
+++ b/libktorrent/migrate/migrate.h
@@ -0,0 +1,53 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef BTMIGRATE_H
+#define BTMIGRATE_H
+
+namespace bt
+{
+ class Torrent;
+
+ /**
+ @author Joris Guisson <joris.guisson@gmail.com>
+
+ Class to migrate old pre-mmap downloads to new ones
+ */
+ class Migrate
+ {
+ public:
+ Migrate();
+ virtual ~Migrate();
+
+ /**
+ * Migrate a download to the new format.
+ * @param tor The torrent
+ * @param tor_dir TorX directory
+ * @param sdir The save directory
+ * @throw Error if something goes wrong
+ */
+ void migrate(const Torrent & tor,const QString & tor_dir,const QString & sdir);
+ private:
+ bool preMMap(const QString & current_chunks);
+ void migrateCurrentChunks(const QString & current_chunks);
+ };
+
+}
+
+#endif
diff --git a/libktorrent/mse/Makefile.am b/libktorrent/mse/Makefile.am
new file mode 100644
index 0000000..d6a8ac5
--- /dev/null
+++ b/libktorrent/mse/Makefile.am
@@ -0,0 +1,9 @@
+INCLUDES = -I$(srcdir)/.. $(all_includes)
+METASOURCES = AUTO
+libmse_la_LDFLAGS = -lgmp $(all_libraries)
+noinst_LTLIBRARIES = libmse.la
+noinst_HEADERS = bigint.h rc4encryptor.h streamsocket.h encryptedauthenticate.h \
+ encryptedserverauthenticate.h functions.h
+libmse_la_SOURCES = bigint.cpp rc4encryptor.cpp streamsocket.cpp \
+ encryptedauthenticate.cpp encryptedserverauthenticate.cpp functions.cpp
+KDE_CXXFLAGS = $(USE_EXCEPTIONS) $(USE_RTTI)
diff --git a/libktorrent/mse/bigint.cpp b/libktorrent/mse/bigint.cpp
new file mode 100644
index 0000000..90c6d9e
--- /dev/null
+++ b/libktorrent/mse/bigint.cpp
@@ -0,0 +1,100 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <time.h>
+#include <string.h>
+#include <stdlib.h>
+#include <util/log.h>
+#include <util/functions.h>
+#include <torrent/globals.h>
+#include "bigint.h"
+
+using namespace bt;
+
+namespace mse
+{
+
+
+ BigInt::BigInt(Uint32 num_bits)
+ {
+ mpz_init2(val,num_bits);
+ }
+
+ BigInt::BigInt(const QString & value)
+ {
+ mpz_init2(val,(value.length() - 2)*4);
+ mpz_set_str(val,value.ascii(),0);
+ }
+
+ BigInt::BigInt(const BigInt & bi)
+ {
+ mpz_set(val,bi.val);
+ }
+
+ BigInt::~BigInt()
+ {
+ mpz_clear(val);
+ }
+
+
+ BigInt & BigInt::operator = (const BigInt & bi)
+ {
+ mpz_set(val,bi.val);
+ return *this;
+ }
+
+ BigInt BigInt::powerMod(const BigInt & x,const BigInt & e,const BigInt & d)
+ {
+ BigInt r;
+ mpz_powm(r.val,x.val,e.val,d.val);
+ return r;
+ }
+
+ BigInt BigInt::random()
+ {
+ static Uint32 rnd = 0;
+ if (rnd % 10 == 0)
+ {
+ TimeStamp now = bt::GetCurrentTime();
+ srand(now);
+ rnd = 0;
+ }
+ rnd++;
+ Uint8 tmp[20];
+ for (Uint32 i = 0;i < 20;i++)
+ tmp[i] = (Uint8)rand() % 0x100;
+
+ return BigInt::fromBuffer(tmp,20);
+ }
+
+ Uint32 BigInt::toBuffer(Uint8* buf,Uint32 max_size) const
+ {
+ size_t foo;
+ mpz_export(buf,&foo,1,1,1,0,val);
+ return foo;
+ }
+
+ BigInt BigInt::fromBuffer(const Uint8* buf,Uint32 size)
+ {
+ BigInt r(size*8);
+ mpz_import(r.val,size,1,1,1,0,buf);
+ return r;
+ }
+
+}
diff --git a/libktorrent/mse/bigint.h b/libktorrent/mse/bigint.h
new file mode 100644
index 0000000..ad94d20
--- /dev/null
+++ b/libktorrent/mse/bigint.h
@@ -0,0 +1,98 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef MSEBIGINT_H
+#define MSEBIGINT_H
+
+#include <qstring.h>
+#include <util/constants.h>
+#include <stdio.h>
+#include <gmp.h>
+
+using bt::Uint8;
+using bt::Uint16;
+using bt::Uint32;
+using bt::Uint64;
+
+namespace mse
+{
+
+ /**
+ * @author Joris Guisson <joris.guisson@gmail.com>
+ *
+ * Class which can hold an arbitrary large integer. This will be a very important part of our
+ * MSE implementation.
+ */
+ class BigInt
+ {
+ public:
+ /**
+ * Create a big integer, with num_bits bits.
+ * All bits will be set to 0.
+ * @param num_bits The number of bits
+ */
+ BigInt(Uint32 num_bits = 0);
+
+ /**
+ * Create a big integer of a string. The string must be
+ * a hexadecimal representation of an integer. For example :
+ * 12AFFE123488BBBE123
+ *
+ * Letters can be upper or lower case. Invalid chars will create an invalid number.
+ * @param value The hexadecimal representation of the number
+ */
+ BigInt(const QString & value);
+
+ /**
+ * Copy constructor.
+ * @param bi BigInt to copy
+ */
+ BigInt(const BigInt & bi);
+ virtual ~BigInt();
+
+ /**
+ * Assignment operator.
+ * @param bi The BigInt to copy
+ * @return *this
+ */
+ BigInt & operator = (const BigInt & bi);
+
+ /**
+ * Calculates
+ * (x ^ e) mod d
+ * ^ is power
+ */
+ static BigInt powerMod(const BigInt & x,const BigInt & e,const BigInt & d);
+
+ /// Make a random BigInt
+ static BigInt random();
+
+ /// Export the bigint ot a buffer
+ Uint32 toBuffer(Uint8* buf,Uint32 max_size) const;
+
+ /// Make a BigInt out of a buffer
+ static BigInt fromBuffer(const Uint8* buf,Uint32 size);
+
+ private:
+ mpz_t val;
+ };
+
+}
+
+#endif
diff --git a/libktorrent/mse/encryptedauthenticate.cpp b/libktorrent/mse/encryptedauthenticate.cpp
new file mode 100644
index 0000000..644ba7b
--- /dev/null
+++ b/libktorrent/mse/encryptedauthenticate.cpp
@@ -0,0 +1,302 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <stdlib.h>
+#include <algorithm>
+#include <util/functions.h>
+#include <util/log.h>
+#include <torrent/globals.h>
+#include <torrent/server.h>
+#include "encryptedauthenticate.h"
+#include "rc4encryptor.h"
+#include "streamsocket.h"
+#include "functions.h"
+
+using namespace bt;
+
+namespace mse
+{
+
+
+
+ EncryptedAuthenticate::EncryptedAuthenticate(
+ const QString& ip,
+ Uint16 port,
+ const SHA1Hash& info_hash,
+ const PeerID& peer_id,
+ PeerManager* pman)
+ : Authenticate(ip, port, info_hash, peer_id, pman)
+ {
+ mse::GeneratePublicPrivateKey(xa,ya);
+ state = NOT_CONNECTED;
+ buf_size = 0;
+ our_rc4 = 0;
+ vc_off = 0;
+ dec_bytes = 0;
+ crypto_select = 0;
+ pad_D_len = 0;
+ end_of_crypto_handshake = 0;
+ //Out(SYS_CON|LOG_DEBUG) << "EncryptedAuthenticate : " << ip << ":" << port << endl;
+ }
+
+
+ EncryptedAuthenticate::~EncryptedAuthenticate()
+ {
+ delete our_rc4;
+ }
+
+
+
+ void EncryptedAuthenticate::connected()
+ {
+ // we are connected so send ya and some padding
+ Uint8 tmp[608];
+ ya.toBuffer(tmp,96);
+ sock->sendData(tmp,96 + rand() % 512);
+ state = SENT_YA;
+ }
+
+ /*
+ 1 A->B: Diffie Hellman Ya, PadA
+ 2 B->A: Diffie Hellman Yb, PadB
+ 3 A->B: HASH('req1', S), HASH('req2', SKEY) xor HASH('req3', S), ENCRYPT(VC, crypto_provide, len(PadC), PadC, len(IA)), ENCRYPT(IA)
+ 4 B->A: ENCRYPT(VC, crypto_select, len(padD), padD), ENCRYPT2(Payload Stream)
+ 5 A->B: ENCRYPT2(Payload Stream)
+ */
+
+
+
+ void EncryptedAuthenticate::handleYB()
+ {
+ // if you can't sent 96 bytes you are not worth the effort
+ if (buf_size < 96)
+ {
+ Out(SYS_CON|LOG_DEBUG) << "Not enough data received, encrypted authentication failed" << endl;
+ onFinish(false);
+ return;
+ }
+
+ // read Yb
+ yb = BigInt::fromBuffer(buf,96);
+
+ // calculate s
+ s = mse::DHSecret(xa,yb);
+
+ state = GOT_YB;
+ // now we must send line 3
+ Uint8 tmp_buf[120]; // temporary buffer
+ bt::SHA1Hash h1,h2; // temporary hash
+
+ // generate and send the first hash
+ memcpy(tmp_buf,"req1",4);
+ s.toBuffer(tmp_buf + 4,96);
+ h1 = SHA1Hash::generate(tmp_buf,100);
+ sock->sendData(h1.getData(),20);
+
+ // generate second and third hash and xor them
+ memcpy(tmp_buf,"req2",4);
+ memcpy(tmp_buf+4,info_hash.getData(),20);
+ h1 = SHA1Hash::generate(tmp_buf,24);
+
+ memcpy(tmp_buf,"req3",4);
+ s.toBuffer(tmp_buf + 4,96);
+ h2 = SHA1Hash::generate(tmp_buf,100);
+ sock->sendData((h1 ^ h2).getData(),20);
+
+ // now we enter encrypted mode the keys are :
+ // HASH('keyA', S, SKEY) for the encryption key
+ // HASH('keyB', S, SKEY) for the decryption key
+ enc = mse::EncryptionKey(true,s,info_hash);
+ dec = mse::EncryptionKey(false,s,info_hash);
+
+ our_rc4 = new RC4Encryptor(dec,enc);
+
+ // now we must send ENCRYPT(VC, crypto_provide, len(PadC), PadC, len(IA))
+ memset(tmp_buf,0,16); // VC are 8 0x00's
+ if (Globals::instance().getServer().unencryptedConnectionsAllowed())
+ tmp_buf[11] = 0x03; // we support both plain text and rc4
+ else
+ tmp_buf[11] = 0x02;
+ WriteUint16(tmp_buf,12,0x0000); // no padC
+ WriteUint16(tmp_buf,14,68); // length of IA, which will be the bittorrent handshake
+ // send IA which is the handshake
+ makeHandshake(tmp_buf+16,info_hash,our_peer_id);
+ sock->sendData(our_rc4->encrypt(tmp_buf,84),84);
+
+ // search for the encrypted VC in the data
+ findVC();
+ }
+
+ void EncryptedAuthenticate::findVC()
+ {
+ Uint8 vc[8] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
+
+ RC4Encryptor rc4(enc,dec);
+ memcpy(vc,rc4.encrypt(vc,8),8);
+
+ Uint32 max_i = buf_size - 8;
+ for (Uint32 i = 96;i < max_i;i++)
+ {
+ if (vc[0] == buf[i] && memcmp(buf+i,vc,8) == 0)
+ {
+ state = FOUND_VC;
+ vc_off = i;
+ handleCryptoSelect();
+ return;
+ }
+ }
+
+ // we haven't found it in the first 616 bytes (96 + max 512 padding + 8 bytes VC)
+ if (buf_size >= 616)
+ {
+ onFinish(false);
+ }
+ }
+
+ void EncryptedAuthenticate::handleCryptoSelect()
+ {
+ // not enough data available so lets come back later
+ if (vc_off + 14 >= buf_size)
+ return;
+
+ // now decrypt the first 14 bytes
+ our_rc4->decrypt(buf + vc_off,14);
+ // check the VC
+ for (Uint32 i = vc_off;i < vc_off + 8;i++)
+ {
+ if (buf[i])
+ {
+ Out(SYS_CON|LOG_DEBUG) << "Invalid VC " << endl;
+ onFinish(false);
+ return;
+ }
+ }
+
+ crypto_select = ReadUint32(buf,vc_off + 8);
+ pad_D_len = ReadUint16(buf,vc_off + 12);
+ if (pad_D_len > 512)
+ {
+ Out(SYS_CON|LOG_DEBUG) << "Invalid pad D length" << endl;
+ onFinish(false);
+ return;
+ }
+
+ end_of_crypto_handshake = vc_off + 14 + pad_D_len;
+ if (!(vc_off + 14 + pad_D_len < buf_size))
+ {
+ // padD is not complete, wait for that
+ state = WAIT_FOR_PAD_D;
+ return;
+ }
+
+ handlePadD();
+ }
+
+ void EncryptedAuthenticate::handlePadD()
+ {
+ // decrypt the padding
+ our_rc4->decrypt(buf + (vc_off + 14),pad_D_len);
+
+ bool rc4 = false;
+ if (crypto_select & 0x00000001) // plain_text selected
+ {
+ delete our_rc4;
+ our_rc4 = 0;
+ }
+ else if (crypto_select & 0x00000002) // now it must be rc4 if not exit
+ {
+ sock->setRC4Encryptor(our_rc4);
+ our_rc4 = 0;
+ rc4 = true;
+ }
+ else // we don't support anything else so error out
+ {
+ onFinish(false);
+ return;
+ }
+
+ // noz we wait for the normal handshake
+ state = NORMAL_HANDSHAKE;
+ // if we have read more then the crypto handshake, reinsert it
+ if (buf_size > vc_off + 14 + pad_D_len)
+ {
+ Uint32 off = vc_off + 14 + pad_D_len;
+ sock->reinsert(buf + off,buf_size - off);
+ Authenticate::onReadyRead();
+ }
+ }
+
+ void EncryptedAuthenticate::onReadyRead()
+ {
+ if (finished)
+ return;
+
+
+ Uint32 ba = sock->bytesAvailable();
+ if (ba == 0)
+ {
+ onFinish(false);
+ return;
+ }
+
+ if (state != NORMAL_HANDSHAKE)
+ {
+ if (buf_size + ba > MAX_EA_BUF_SIZE)
+ ba = MAX_EA_BUF_SIZE - buf_size;
+
+ // do not read past the end of padD
+ if (pad_D_len > 0 && buf_size + ba > vc_off + 14 + pad_D_len)
+ ba = (vc_off + 14 + pad_D_len) - buf_size;
+ // read data
+ buf_size += sock->readData(buf + buf_size,ba);
+
+ }
+
+ switch (state)
+ {
+ case SENT_YA:
+ if (ba > 608)
+ {
+ onFinish(false);
+ }
+ else
+ {
+ handleYB();
+ }
+ break;
+ case GOT_YB:
+ findVC();
+ break;
+ case FOUND_VC:
+ handleCryptoSelect();
+ break;
+ case WAIT_FOR_PAD_D:
+ handlePadD();
+ break;
+ case NORMAL_HANDSHAKE:
+ // let AuthenticateBase deal with the data
+ AuthenticateBase::onReadyRead();
+ break;
+ };
+ }
+
+
+}
+
+#include "encryptedauthenticate.moc"
diff --git a/libktorrent/mse/encryptedauthenticate.h b/libktorrent/mse/encryptedauthenticate.h
new file mode 100644
index 0000000..74ccc1b
--- /dev/null
+++ b/libktorrent/mse/encryptedauthenticate.h
@@ -0,0 +1,82 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef MSEENCRYPTEDAUTHENTICATE_H
+#define MSEENCRYPTEDAUTHENTICATE_H
+
+#include <util/sha1hash.h>
+#include <torrent/authenticate.h>
+#include "bigint.h"
+
+
+namespace mse
+{
+ class RC4Encryptor;
+
+ const Uint32 MAX_EA_BUF_SIZE = 622 + 512;
+
+ /**
+ * @author Joris Guisson <joris.guisson@gmail.com>
+ *
+ * Encrypted version of the Authenticate class
+ */
+ class EncryptedAuthenticate : public bt::Authenticate
+ {
+ Q_OBJECT
+ public:
+ EncryptedAuthenticate(const QString& ip, Uint16 port, const bt::SHA1Hash& info_hash, const bt::PeerID& peer_id, bt::PeerManager* pman);
+ virtual ~EncryptedAuthenticate();
+
+ private slots:
+ virtual void connected();
+ virtual void onReadyRead();
+
+ private:
+ void handleYB();
+ void handleCryptoSelect();
+ void findVC();
+ void handlePadD();
+
+ private:
+ enum State
+ {
+ NOT_CONNECTED,
+ SENT_YA,
+ GOT_YB,
+ FOUND_VC,
+ WAIT_FOR_PAD_D,
+ NORMAL_HANDSHAKE
+ };
+
+ BigInt xa,ya,s,skey,yb;
+ State state;
+ RC4Encryptor* our_rc4;
+ Uint8 buf[MAX_EA_BUF_SIZE];
+ Uint32 buf_size;
+ Uint32 vc_off;
+ Uint32 dec_bytes;
+ bt::SHA1Hash enc,dec;
+ Uint32 crypto_select;
+ Uint16 pad_D_len;
+ Uint32 end_of_crypto_handshake;
+ };
+
+}
+
+#endif
diff --git a/libktorrent/mse/encryptedserverauthenticate.cpp b/libktorrent/mse/encryptedserverauthenticate.cpp
new file mode 100644
index 0000000..40353ad
--- /dev/null
+++ b/libktorrent/mse/encryptedserverauthenticate.cpp
@@ -0,0 +1,354 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <stdlib.h>
+#include <util/functions.h>
+#include <util/log.h>
+#include <torrent/server.h>
+#include <torrent/globals.h>
+#include "encryptedserverauthenticate.h"
+#include "functions.h"
+#include "streamsocket.h"
+#include "rc4encryptor.h"
+
+using namespace bt;
+
+namespace mse
+{
+ EncryptedServerAuthenticate::EncryptedServerAuthenticate(mse::StreamSocket* sock, bt::Server* server): bt::ServerAuthenticate(sock, server)
+ {
+ mse::GeneratePublicPrivateKey(xb,yb);
+ state = WAITING_FOR_YA;
+ buf_size = 0;
+ req1_off = 0;
+ our_rc4 = 0;
+ pad_C_len = 0;
+ crypto_provide = crypto_select = 0;
+ }
+
+
+ EncryptedServerAuthenticate::~EncryptedServerAuthenticate()
+ {
+ delete our_rc4;
+ }
+
+ void EncryptedServerAuthenticate::sendYB()
+ {
+ Uint8 tmp[608];
+ yb.toBuffer(tmp,96);
+ // DumpBigInt("Xb",xb);
+ // DumpBigInt("Yb",yb);
+ sock->sendData(tmp,96 + rand() % 512);
+ //Out() << "Sent YB" << endl;
+ }
+
+
+ void EncryptedServerAuthenticate::handleYA()
+ {
+ sendYB();
+
+ ya = BigInt::fromBuffer(buf,96);
+ // DumpBigInt("Ya",ya);
+ // now calculate secret
+ s = mse::DHSecret(xb,ya);
+ // DumpBigInt("S",s);
+ state = WAITING_FOR_REQ1;
+ // see if we can find req1
+ findReq1();
+ }
+
+ void EncryptedServerAuthenticate::findReq1()
+ {
+ if (buf_size < 116) // safety check
+ return;
+
+ // Out() << "Find Req1" << endl;
+ Uint8 tmp[100];
+ memcpy(tmp,"req1",4);
+ s.toBuffer(tmp + 4,96);
+ SHA1Hash req1 = SHA1Hash::generate(tmp,100);
+ for (Uint32 i = 96;i < buf_size - 20;i++)
+ {
+ if (buf[i] == req1.getData()[0] && memcmp(buf+i,req1.getData(),20) == 0)
+ {
+ state = FOUND_REQ1;
+ req1_off = i;
+ calculateSKey();
+ return;
+ }
+ }
+
+ if (buf_size > 608)
+ {
+ // Out(SYS_CON|LOG_DEBUG) << "Couldn't find req1" << endl;
+ onFinish(false);
+ }
+ }
+
+ void EncryptedServerAuthenticate::calculateSKey()
+ {
+ // Out(SYS_CON|LOG_DEBUG) << "Calculate SKEY" << endl;
+ // not enough data return
+ if (req1_off + 40 > buf_size)
+ return;
+
+ Uint8 tmp[100];
+ memcpy(tmp,"req3",4);
+ s.toBuffer(tmp+4,96);
+ SHA1Hash r3 = SHA1Hash::generate(tmp,100);
+ SHA1Hash r(buf + req1_off + 20);
+
+ // r = HASH('req2', SKEY) xor HASH('req3', S)
+ SHA1Hash r2 = r ^ r3; // now calculate HASH('req2', SKEY)
+ if (!server->findInfoHash(r2,info_hash))
+ {
+ // Out(SYS_CON|LOG_DEBUG) << "Unknown info_hash" << endl;
+ onFinish(false);
+ return;
+ }
+ // we have found the info_hash, now process VC and the rest
+ state = FOUND_INFO_HASH;
+ processVC();
+ }
+
+ void EncryptedServerAuthenticate::processVC()
+ {
+ // Out(SYS_CON|LOG_DEBUG) << "Process VC" << endl;
+ if (!our_rc4)
+ {
+ // calculate the keys
+ SHA1Hash enc = mse::EncryptionKey(false,s,info_hash);
+ SHA1Hash dec = mse::EncryptionKey(true,s,info_hash);
+ //Out() << "enc = " << enc.toString() << endl;
+ //Out() << "dec = " << dec.toString() << endl;
+ our_rc4 = new RC4Encryptor(dec,enc);
+ }
+
+ // if we do not have everything return
+ if (buf_size < req1_off + 40 + 14)
+ return;
+
+
+ Uint32 off = req1_off + 40;
+ // now decrypt the vc and crypto_provide and the length of pad_C
+ our_rc4->decrypt(buf + off,14);
+
+ // check the VC
+ for (Uint32 i = 0;i < 8;i++)
+ {
+ if (buf[off + i])
+ {
+ // Out(SYS_CON|LOG_DEBUG) << "Illegal VC" << endl;
+ onFinish(false);
+ return;
+ }
+ }
+ // get crypto_provide and the length of pad_C
+ crypto_provide = bt::ReadUint32(buf,off + 8);
+ pad_C_len = bt::ReadUint16(buf,off + 12);
+ if (pad_C_len > 512)
+ {
+ Out(SYS_CON|LOG_DEBUG) << "Illegal pad C length" << endl;
+ onFinish(false);
+ return;
+ }
+
+ // now we have crypto_provide we can send
+ // ENCRYPT(VC, crypto_select, len(padD), padD)
+ Uint8 tmp[14];
+ memset(tmp,0,14); // VC
+ if (crypto_provide & 0x0000002) // RC4
+ {
+ WriteUint32(tmp,8,0x0000002);
+ crypto_select = 0x0000002;
+ }
+ else
+ {
+ WriteUint32(tmp,8,0x0000001);
+ crypto_select = 0x0000001;
+ }
+ bt::WriteUint16(tmp,12,0); // no pad D
+
+ sock->sendData(our_rc4->encrypt(tmp,14),14);
+
+ // handle pad C
+ if (buf_size < req1_off + 14 + pad_C_len)
+ {
+ // we do not have the full padC
+ state = WAIT_FOR_PAD_C;
+ return;
+ }
+
+ handlePadC();
+ }
+
+ void EncryptedServerAuthenticate::handlePadC()
+ {
+ // Out(SYS_CON|LOG_DEBUG) << "Handle PAD C" << endl;
+ // not enough data, so return, we need padC and the length of IA
+ if (buf_size < req1_off + 54 + pad_C_len + 2)
+ return;
+
+ // we have decrypted everything up to pad_C_len
+ Uint32 off = req1_off + 54;
+ our_rc4->decrypt(buf + off,pad_C_len + 2);
+ ia_len = bt::ReadUint16(buf,off + pad_C_len);
+ if (buf_size < off + ia_len)
+ {
+ // we do not have the IA, so wait for it
+ state = WAIT_FOR_IA;
+ return;
+ }
+ handleIA();
+ }
+
+ void EncryptedServerAuthenticate::handleIA()
+ {
+ // Out(SYS_CON|LOG_DEBUG) << "Handle IA" << endl;
+ // not enough data, so return, we need padC and the length of IA
+ if (buf_size < req1_off + 54 + pad_C_len + 2 + ia_len)
+ return;
+
+ // decrypt the initial argument
+ if (ia_len > 0)
+ {
+ Uint32 off = req1_off + 54 + pad_C_len + 2;
+ // reinsert everything so that the normal authentication can handle it
+ sock->reinsert(buf + off,buf_size - off);
+ }
+
+ bool allow_unenc = Globals::instance().getServer().unencryptedConnectionsAllowed();
+
+ if (crypto_select & 0x0000002)
+ {
+ sock->setRC4Encryptor(our_rc4);
+ our_rc4 = 0;
+ }
+ else if (!allow_unenc && crypto_select & 0x00000001)
+ {
+ // if no encrypted connections
+ Out(SYS_CON|LOG_DEBUG) << "Unencrypted connections not allowed" << endl;
+ onFinish(false);
+ return;
+ }
+ else
+ {
+ delete our_rc4;
+ our_rc4 = 0;
+ }
+
+ // hand it over to ServerAuthenticate
+ state = NON_ENCRYPTED_HANDSHAKE;
+ ServerAuthenticate::onReadyRead();
+ }
+
+ void EncryptedServerAuthenticate::onReadyRead()
+ {
+ if (!sock)
+ return;
+
+ Uint32 ba = sock->bytesAvailable();
+ if (!ba)
+ {
+ onFinish(false);
+ return;
+ }
+
+ // make sure we don't write past the end of the buffer
+ if (buf_size + ba > MAX_SEA_BUF_SIZE)
+ ba = MAX_SEA_BUF_SIZE - buf_size;
+
+ switch (state)
+ {
+ case WAITING_FOR_YA:
+ if (ba <= 68 && Globals::instance().getServer().unencryptedConnectionsAllowed())
+ {
+ // this is most likely an unencrypted handshake, so if we can find a peer manager
+ // for the info hash in it, add it to the list of potential peers of that peer manager
+ // so it will be contacted later on
+ /* buf_size += sock->readData(buf + buf_size,ba);
+ if (buf_size >= 48)
+ {
+ SHA1Hash rh(buf+28);
+ PeerManager* pman = server->findPeerManager(rh);
+ if (pman)
+ {
+ PotentialPeer pp;
+ pp.ip = sock->getRemoteIPAddress();
+ pp.port = sock->getRemotePort();
+ pman->addPotentialPeer(pp);
+ }
+ }
+ onFinish(false);
+ */
+ Out(SYS_CON|LOG_DEBUG) << "Switching back to normal server authenticate" << endl;
+ state = NON_ENCRYPTED_HANDSHAKE;
+ ServerAuthenticate::onReadyRead();
+ }
+ else
+ {
+ buf_size += sock->readData(buf + buf_size,ba);
+ if (buf_size >= 96)
+ handleYA();
+ }
+ break;
+ case WAITING_FOR_REQ1:
+ if (buf_size + ba > MAX_SEA_BUF_SIZE)
+ ba = MAX_SEA_BUF_SIZE - buf_size;
+
+ buf_size += sock->readData(buf + buf_size,ba);
+ findReq1();
+ break;
+ case FOUND_REQ1:
+ if (buf_size + ba > MAX_SEA_BUF_SIZE)
+ ba = MAX_SEA_BUF_SIZE - buf_size;
+
+ buf_size += sock->readData(buf + buf_size,ba);
+ calculateSKey();
+ break;
+ case FOUND_INFO_HASH:
+ if (buf_size + ba > MAX_SEA_BUF_SIZE)
+ ba = MAX_SEA_BUF_SIZE - buf_size;
+
+ buf_size += sock->readData(buf + buf_size,ba);
+ processVC();
+ break;
+ case WAIT_FOR_PAD_C:
+ if (buf_size + ba > MAX_SEA_BUF_SIZE)
+ ba = MAX_SEA_BUF_SIZE - buf_size;
+
+ buf_size += sock->readData(buf + buf_size,ba);
+ handlePadC();
+ break;
+ case WAIT_FOR_IA:
+ if (buf_size + ba > MAX_SEA_BUF_SIZE)
+ ba = MAX_SEA_BUF_SIZE - buf_size;
+
+ buf_size += sock->readData(buf + buf_size,ba);
+ handleIA();
+ break;
+ case NON_ENCRYPTED_HANDSHAKE:
+ ServerAuthenticate::onReadyRead();
+ break;
+ }
+ }
+
+
+}
+#include "encryptedserverauthenticate.moc"
diff --git a/libktorrent/mse/encryptedserverauthenticate.h b/libktorrent/mse/encryptedserverauthenticate.h
new file mode 100644
index 0000000..3c358cd
--- /dev/null
+++ b/libktorrent/mse/encryptedserverauthenticate.h
@@ -0,0 +1,80 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef MSEENCRYPTEDSERVERAUTHENTICATE_H
+#define MSEENCRYPTEDSERVERAUTHENTICATE_H
+
+#include <util/sha1hash.h>
+#include <torrent/serverauthenticate.h>
+#include "bigint.h"
+
+namespace mse
+{
+ class RC4Encryptor;
+
+
+ const Uint32 MAX_SEA_BUF_SIZE = 608 + 20 + 20 + 8 + 4 + 2 + 512 + 2 + 68;
+ /**
+ @author Joris Guisson <joris.guisson@gmail.com>
+ */
+ class EncryptedServerAuthenticate : public bt::ServerAuthenticate
+ {
+ Q_OBJECT
+ public:
+ EncryptedServerAuthenticate(mse::StreamSocket* sock, bt::Server* server);
+ virtual ~EncryptedServerAuthenticate();
+
+ private slots:
+ virtual void onReadyRead();
+
+ private:
+ void handleYA();
+ void sendYB();
+ void findReq1();
+ void calculateSKey();
+ void processVC();
+ void handlePadC();
+ void handleIA();
+
+ private:
+ enum State
+ {
+ WAITING_FOR_YA,
+ WAITING_FOR_REQ1,
+ FOUND_REQ1,
+ FOUND_INFO_HASH,
+ WAIT_FOR_PAD_C,
+ WAIT_FOR_IA,
+ NON_ENCRYPTED_HANDSHAKE
+ };
+ BigInt xb,yb,s,ya;
+ bt::SHA1Hash skey,info_hash;
+ State state;
+ Uint8 buf[MAX_SEA_BUF_SIZE];
+ Uint32 buf_size;
+ Uint32 req1_off;
+ Uint32 crypto_provide,crypto_select;
+ Uint16 pad_C_len;
+ Uint16 ia_len;
+ RC4Encryptor* our_rc4;
+ };
+
+}
+
+#endif
diff --git a/libktorrent/mse/functions.cpp b/libktorrent/mse/functions.cpp
new file mode 100644
index 0000000..bb19b93
--- /dev/null
+++ b/libktorrent/mse/functions.cpp
@@ -0,0 +1,74 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <util/log.h>
+#include <torrent/globals.h>
+#include <util/sha1hash.h>
+#include "functions.h"
+#include "bigint.h"
+
+using namespace bt;
+
+namespace mse
+{
+ /*
+ static const BigInt P = BigInt(
+ "0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD"
+ "129024E088A67CC74020BBEA63B139B22514A08798E3404"
+ "DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C"
+ "245E485B576625E7EC6F44C42E9A63A36210000000000090563");
+ */
+ static const BigInt P = BigInt("0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A63A36210000000000090563");
+
+ void GeneratePublicPrivateKey(BigInt & priv,BigInt & pub)
+ {
+ BigInt G = BigInt("0x02");
+ priv = BigInt::random();
+ pub = BigInt::powerMod(G,priv,P);
+ }
+
+ BigInt DHSecret(const BigInt & our_priv,const BigInt & peer_pub)
+ {
+ return BigInt::powerMod(peer_pub,our_priv,P);
+ }
+
+ bt::SHA1Hash EncryptionKey(bool a,const BigInt & s,const bt::SHA1Hash & skey)
+ {
+ Uint8 buf[120];
+ memcpy(buf,"key",3);
+ buf[3] = (Uint8)(a ? 'A' : 'B');
+ s.toBuffer(buf + 4,96);
+ memcpy(buf + 100,skey.getData(),20);
+ return bt::SHA1Hash::generate(buf,120);
+ }
+
+ void DumpBigInt(const QString & name,const BigInt & bi)
+ {
+ static Uint8 buf[512];
+ Uint32 nb = bi.toBuffer(buf,512);
+ bt::Log & lg = Out();
+ lg << name << " (" << nb << ") = ";
+ for (Uint32 i = 0;i < nb;i++)
+ {
+ lg << QString("0x%1 ").arg(buf[i],0,16);
+ }
+ lg << endl;
+ }
+
+}
diff --git a/libktorrent/mse/functions.h b/libktorrent/mse/functions.h
new file mode 100644
index 0000000..4be1667
--- /dev/null
+++ b/libktorrent/mse/functions.h
@@ -0,0 +1,39 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef MSEFUNCTIONS_H
+#define MSEFUNCTIONS_H
+
+namespace bt
+{
+ class SHA1Hash;
+}
+
+namespace mse
+{
+ class BigInt;
+
+ void GeneratePublicPrivateKey(BigInt & pub,BigInt & priv);
+ BigInt DHSecret(const BigInt & our_priv,const BigInt & peer_pub);
+ bt::SHA1Hash EncryptionKey(bool a,const BigInt & s,const bt::SHA1Hash & skey);
+
+ void DumpBigInt(const QString & name,const BigInt & bi);
+}
+
+#endif
diff --git a/libktorrent/mse/rc4encryptor.cpp b/libktorrent/mse/rc4encryptor.cpp
new file mode 100644
index 0000000..422fe5d
--- /dev/null
+++ b/libktorrent/mse/rc4encryptor.cpp
@@ -0,0 +1,100 @@
+ /***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include "rc4encryptor.h"
+
+namespace mse
+{
+ static void swap(Uint8 & a,Uint8 & b)
+ {
+ Uint8 tmp = a;
+ a = b;
+ b = tmp;
+ }
+
+ static Uint8 rc4_enc_buffer[bt::MAX_MSGLEN];
+
+ RC4::RC4(const Uint8* key,Uint32 size) : i(0),j(0)
+ {
+ // initialize state
+ for (Uint32 t = 0;t < 256;t++)
+ s[t] = t;
+
+ j = 0;
+ for (Uint32 t=0;t < 256;t++)
+ {
+ j = (j + s[t] + key[t % size]) & 0xff;
+ swap(s[t],s[j]);
+ }
+
+ i = j = 0;
+ }
+
+ RC4::~RC4()
+ {
+ }
+
+ void RC4::process(const Uint8* in,Uint8* out,Uint32 size)
+ {
+ for (Uint32 k = 0;k < size;k++)
+ {
+ out[k] = process(in[k]);
+ }
+ }
+
+ Uint8 RC4::process(Uint8 b)
+ {
+ i = (i + 1) & 0xff;
+ j = (j + s[i]) & 0xff;
+ swap(s[i],s[j]);
+ Uint8 tmp = s[ (s[i] + s[j]) & 0xff];
+ return tmp ^ b;
+ }
+
+
+ RC4Encryptor::RC4Encryptor(const bt::SHA1Hash & dk,const bt::SHA1Hash & ek)
+ : enc(ek.getData(),20),dec(dk.getData(),20)
+ {
+ Uint8 tmp[1024];
+ enc.process(tmp,tmp,1024);
+ dec.process(tmp,tmp,1024);
+ }
+
+
+ RC4Encryptor::~RC4Encryptor()
+ {}
+
+
+ void RC4Encryptor::decrypt(Uint8* data,Uint32 len)
+ {
+ dec.process(data,data,len);
+ }
+
+ const Uint8* RC4Encryptor::encrypt(const Uint8* data,Uint32 len)
+ {
+ enc.process(data,rc4_enc_buffer,len);
+ return rc4_enc_buffer;
+ }
+
+ void RC4Encryptor::encryptReplace(Uint8* data,Uint32 len)
+ {
+ enc.process(data,data,len);
+ }
+
+}
diff --git a/libktorrent/mse/rc4encryptor.h b/libktorrent/mse/rc4encryptor.h
new file mode 100644
index 0000000..650b54e
--- /dev/null
+++ b/libktorrent/mse/rc4encryptor.h
@@ -0,0 +1,96 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef MSERC4ENCRYPTOR_H
+#define MSERC4ENCRYPTOR_H
+
+#include <util/sha1hash.h>
+#include <util/constants.h>
+
+using bt::Uint8;
+using bt::Uint32;
+
+namespace mse
+{
+ /**
+ * Helper class to do the actual encryption / decryption
+ */
+ class RC4
+ {
+ Uint8 i,j;
+ Uint8 s[256];
+ public:
+ RC4(const Uint8* key,Uint32 size);
+ virtual ~RC4();
+
+ void process(const Uint8* in,Uint8* out,Uint32 size);
+ Uint8 process(Uint8 b);
+ };
+
+ /**
+ * @author Joris Guisson <joris.guisson@gmail.com>
+ *
+ * RC4 encryptor. Uses the RC4 algorithm to encrypt and decrypt data.
+ * This class has a static encryption buffer, which makes it not thread safe
+ * because the buffer is not protected by mutexes.
+ */
+ class RC4Encryptor
+ {
+ RC4 enc,dec;
+ public:
+ RC4Encryptor(const bt::SHA1Hash & dkey,const bt::SHA1Hash & ekey);
+ virtual ~RC4Encryptor();
+
+ /**
+ * Decrypt some data, decryption happens in place (original data gets overwritten)
+ * @param data The data
+ * @param len Size of the data
+ */
+ void decrypt(Uint8* data,Uint32 len);
+
+ /**
+ * Encrypt the data. Encryption happens into the static buffer.
+ * So that the data passed to this function is never overwritten.
+ * If we send pieces we point directly to the mmap region of data,
+ * this cannot be overwritten, hence the static buffer.
+ * @param data The data
+ * @param len The length of the data
+ * @return Pointer to the static buffer
+ */
+ const Uint8* encrypt(const Uint8* data,Uint32 len);
+
+ /**
+ * Encrypt data, encryption will happen in the same buffer. So data will
+ * be changed replaced by it's encrypted version.
+ * @param data The data to encrypt
+ * @param len The length of the data
+ */
+ void encryptReplace(Uint8* data,Uint32 len);
+
+ /**
+ * Encrypts one byte.
+ * @param b The byte to encrypt
+ * @return The encrypted byte
+ */
+ Uint8 encrypt(Uint8 b) {return enc.process(b);}
+ };
+
+}
+
+#endif
diff --git a/libktorrent/mse/streamsocket.cpp b/libktorrent/mse/streamsocket.cpp
new file mode 100644
index 0000000..19a0a2e
--- /dev/null
+++ b/libktorrent/mse/streamsocket.cpp
@@ -0,0 +1,326 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <errno.h>
+#include <qsocket.h>
+#include <qsocketdevice.h>
+#include <util/sha1hash.h>
+#include <util/log.h>
+#include <torrent/peer.h>
+#include <torrent/globals.h>
+#include <torrent/authenticatebase.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/tcp.h>
+#include <net/socketmonitor.h>
+#include "streamsocket.h"
+#include "rc4encryptor.h"
+
+using namespace bt;
+using namespace net;
+
+namespace mse
+{
+
+ Uint8 StreamSocket::tos = IPTOS_THROUGHPUT;
+ Uint32 StreamSocket::num_connecting = 0;
+ Uint32 StreamSocket::max_connecting = 50;
+
+ StreamSocket::StreamSocket() : sock(0),enc(0),monitored(false)
+ {
+ sock = new BufferedSocket(true);
+ sock->setNonBlocking();
+ reinserted_data = 0;
+ reinserted_data_size = 0;
+ reinserted_data_read = 0;
+
+ }
+
+ StreamSocket::StreamSocket(int fd) : sock(0),enc(0),monitored(false)
+ {
+ sock = new BufferedSocket(fd);
+ sock->setNonBlocking();
+ reinserted_data = 0;
+ reinserted_data_size = 0;
+ reinserted_data_read = 0;
+ sock->setTOS(tos);
+ }
+
+ StreamSocket::~StreamSocket()
+ {
+ // make sure the number of connecting sockets is updated
+ if (connecting() && num_connecting > 0)
+ num_connecting--;
+
+ SocketMonitor::instance().remove(sock);
+ delete [] reinserted_data;
+ delete enc;
+ delete sock;
+ }
+
+ void StreamSocket::startMonitoring(net::SocketReader* rdr,net::SocketWriter* wrt)
+ {
+ this->rdr = rdr;
+ this->wrt = wrt;
+ sock->setReader(this);
+ sock->setWriter(this);
+ SocketMonitor::instance().add(sock);
+ monitored = true;
+ if (reinserted_data)
+ {
+ if (enc)
+ enc->decrypt(reinserted_data + reinserted_data_read,
+ reinserted_data_size - reinserted_data_read);
+
+ rdr->onDataReady(reinserted_data + reinserted_data_read,
+ reinserted_data_size - reinserted_data_read);
+ delete [] reinserted_data;
+ reinserted_data = 0;
+ reinserted_data_size = 0;
+ }
+ }
+
+
+ Uint32 StreamSocket::sendData(const Uint8* data,Uint32 len)
+ {
+ if (enc)
+ {
+ // we need to make sure all data is sent because of the encryption
+ Uint32 ds = 0;
+ const Uint8* ed = enc->encrypt(data,len);
+ while (sock->ok() && ds < len)
+ {
+ Uint32 ret = sock->send(ed + ds,len - ds);
+ ds += ret;
+ if (ret == 0)
+ {
+ Out(SYS_CON|LOG_DEBUG) << "ret = 0" << endl;
+ }
+ }
+ if (ds != len)
+ Out() << "ds != len" << endl;
+ return ds;
+ }
+ else
+ {
+ Uint32 ret = sock->send(data,len);
+ if (ret != len)
+ Out() << "ret != len" << endl;
+ return ret;
+ }
+ }
+
+ Uint32 StreamSocket::readData(Uint8* buf,Uint32 len)
+ {
+ Uint32 ret2 = 0;
+ if (reinserted_data)
+ {
+ Uint32 tr = reinserted_data_size - reinserted_data_read;
+ if (tr < len)
+ {
+ memcpy(buf,reinserted_data + reinserted_data_read,tr);
+ delete [] reinserted_data;
+ reinserted_data = 0;
+ reinserted_data_size = reinserted_data_read = 0;
+ ret2 = tr;
+ if (enc)
+ enc->decrypt(buf,tr);
+ }
+ else
+ {
+ tr = len;
+ memcpy(buf,reinserted_data + reinserted_data_read,tr);
+ reinserted_data_read += tr;
+ if (enc)
+ enc->decrypt(buf,tr);
+ return tr;
+ }
+ }
+
+ if (len == ret2)
+ return ret2;
+
+ Uint32 ret = sock->recv(buf + ret2,len - ret2);
+ if (ret + ret2 > 0 && enc)
+ enc->decrypt(buf,ret + ret2);
+
+ return ret;
+ }
+
+ Uint32 StreamSocket::bytesAvailable() const
+ {
+ Uint32 ba = sock->bytesAvailable();
+ if (reinserted_data_size - reinserted_data_read > 0)
+ return ba + (reinserted_data_size - reinserted_data_read);
+ else
+ return ba;
+ }
+
+ void StreamSocket::close()
+ {
+ sock->close();
+ }
+
+ bool StreamSocket::connectTo(const QString & ip,Uint16 port)
+ {
+ // do a safety check
+ if (ip.isNull() || ip.length() == 0)
+ return false;
+
+ // we don't wanna block the current thread so set non blocking
+ sock->setNonBlocking();
+ if (sock->connectTo(Address(ip,port)))
+ {
+ sock->setTOS(tos);
+ return true;
+ }
+ else if (connecting())
+ {
+ num_connecting++;
+ }
+
+ return false;
+ }
+
+ void StreamSocket::initCrypt(const bt::SHA1Hash & dkey,const bt::SHA1Hash & ekey)
+ {
+ if (enc)
+ delete enc;
+
+ enc = new RC4Encryptor(dkey,ekey);
+ }
+
+ void StreamSocket::disableCrypt()
+ {
+ delete enc;
+ enc = 0;
+ }
+
+ bool StreamSocket::ok() const
+ {
+ return sock->ok();
+ }
+
+ QString StreamSocket::getRemoteIPAddress() const
+ {
+ return sock->getPeerName().toString();
+ }
+
+ bt::Uint16 StreamSocket::getRemotePort() const
+ {
+ return sock->getPeerName().port();
+ }
+
+ net::Address StreamSocket::getRemoteAddress() const
+ {
+ return sock->getPeerName();
+ }
+
+ void StreamSocket::setRC4Encryptor(RC4Encryptor* e)
+ {
+ if (enc)
+ delete enc;
+
+ enc = e;
+ }
+
+ void StreamSocket::reinsert(const Uint8* d,Uint32 size)
+ {
+// Out() << "Reinsert : " << size << endl;
+ Uint32 off = 0;
+ if (reinserted_data)
+ {
+ off = reinserted_data_size;
+ reinserted_data = (Uint8*)realloc(reinserted_data,reinserted_data_size + size);
+ reinserted_data_size += size;
+ }
+ else
+ {
+ reinserted_data = new Uint8[size];
+ reinserted_data_size = size;
+ }
+ memcpy(reinserted_data + off,d,size);
+ }
+
+ bool StreamSocket::connecting() const
+ {
+ return sock->state() == net::Socket::CONNECTING;
+ }
+
+ void StreamSocket::onDataReady(Uint8* buf,Uint32 size)
+ {
+ if (enc)
+ enc->decrypt(buf,size);
+
+ if (rdr)
+ rdr->onDataReady(buf,size);
+ }
+
+ Uint32 StreamSocket::onReadyToWrite(Uint8* data,Uint32 max_to_write)
+ {
+ if (!wrt)
+ return 0;
+
+ Uint32 ret = wrt->onReadyToWrite(data,max_to_write);
+ if (enc && ret > 0) // do encryption if necessary
+ enc->encryptReplace(data,ret);
+
+
+ return ret;
+ }
+
+ bool StreamSocket::hasBytesToWrite() const
+ {
+ return wrt ? wrt->hasBytesToWrite() : false;
+ }
+
+ float StreamSocket::getDownloadRate() const
+ {
+ return sock ? sock->getDownloadRate() : 0.0f;
+ }
+
+ float StreamSocket::getUploadRate() const
+ {
+ return sock ? sock->getUploadRate() : 0.0f;
+ }
+
+ bool StreamSocket::connectSuccesFull() const
+ {
+ bool ret = sock->connectSuccesFull();
+ if (ret)
+ sock->setTOS(tos);
+
+ if (num_connecting > 0)
+ num_connecting--;
+
+ return ret;
+ }
+
+ void StreamSocket::setGroupIDs(Uint32 up,Uint32 down)
+ {
+ sock->setGroupID(up,true);
+ sock->setGroupID(down,false);
+ }
+}
+
+#include "streamsocket.moc"
diff --git a/libktorrent/mse/streamsocket.h b/libktorrent/mse/streamsocket.h
new file mode 100644
index 0000000..5006a7b
--- /dev/null
+++ b/libktorrent/mse/streamsocket.h
@@ -0,0 +1,185 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef MSESTREAMSOCKET_H
+#define MSESTREAMSOCKET_H
+
+#include <qobject.h>
+#include <util/constants.h>
+#include <net/bufferedsocket.h>
+
+class QString;
+
+using bt::Uint8;
+using bt::Uint16;
+using bt::Uint32;
+
+namespace bt
+{
+ class SHA1Hash;
+ class Peer;
+ class AuthenticateBase;
+}
+
+namespace mse
+{
+ class RC4Encryptor;
+
+
+
+
+ /**
+ * @author Joris Guisson <joris.guisson@gmail.com>
+ *
+ * Wrapper around a TCP socket which handles RC4 encryption.
+ * Once authentication is done, the sendData and readData interfaces should
+ * not be used anymore, a SocketReader and SocketWriter should be provided,
+ * so that reading and writing is controlled from the monitor thread.
+ */
+ class StreamSocket : public QObject,public net::SocketReader,public net::SocketWriter
+ {
+ Q_OBJECT
+ public:
+ StreamSocket();
+ StreamSocket(int fd);
+ virtual ~StreamSocket();
+
+ /**
+ * Send a chunk of data. (Does not encrypt the data)
+ * @param data The data
+ * @param len The length
+ * @return Number of bytes written
+ */
+ Uint32 sendData(const Uint8* data,Uint32 len);
+
+ /**
+ * Reads data from the peer.
+ * @param buf The buffer to store the data
+ * @param len The maximum number of bytes to read
+ * @return The number of bytes read
+ */
+ Uint32 readData(Uint8* buf,Uint32 len);
+
+ /// Get the number of bytes available to read.
+ Uint32 bytesAvailable() const;
+
+ /// Are we using encryption
+ bool encrypted() const {return enc != 0;}
+
+ /**
+ * Initialize the RC4 encryption algorithm.
+ * @param dkey
+ * @param ekey
+ */
+ void initCrypt(const bt::SHA1Hash & dkey,const bt::SHA1Hash & ekey);
+
+ /// Set the encryptor
+ void setRC4Encryptor(RC4Encryptor* enc);
+
+ /// Disables encryption. All data will be sent over as plain text.
+ void disableCrypt();
+
+ /// Close the socket
+ void close();
+
+ /// Connect the socket to a remote host
+ bool connectTo(const QString & ip,Uint16 port);
+
+ /// Get the IP address of the remote peer
+ QString getRemoteIPAddress() const;
+
+ /// Get the port of the remote peer
+ bt::Uint16 getRemotePort() const;
+
+ /// Get the full address
+ net::Address getRemoteAddress() const;
+
+ /**
+ * Reinsert data, this is needed when we read to much during the crypto handshake.
+ * This data will be the first to read out. The data will be copied to a temporary buffer
+ * which will be destroyed when the reinserted data has been read.
+ */
+ void reinsert(const Uint8* d,Uint32 size);
+
+ /// see if the socket is still OK
+ bool ok() const;
+
+ /// Get the file descriptor
+ int fd() const {return sock->fd();}
+
+ /// Start monitoring of this socket by the monitor thread
+ void startMonitoring(net::SocketReader* rdr,net::SocketWriter* wrt);
+
+ /// Is this socket connecting to a remote host
+ bool connecting() const;
+
+ /// See if a connect was success full
+ bool connectSuccesFull() const;
+
+ /// Get the current download rate
+ float getDownloadRate() const;
+
+ /// Get the current download rate
+ float getUploadRate() const;
+
+ /**
+ * Set the TOS byte for new sockets.
+ * @param t TOS value
+ */
+ static void setTOS(Uint8 t) {tos = t;}
+
+ /**
+ * Set the download and upload group ID's
+ * @param up Upload group ID
+ * @param down Download group ID
+ */
+ void setGroupIDs(Uint32 up,Uint32 down);
+
+ /**
+ * Check if we are allowed to initiate another outgoing connection.
+ */
+ static bool canInitiateNewConnection() {return num_connecting < max_connecting;}
+
+ /**
+ * Set the maximum number of connecting sockets we are allowed to have.
+ */
+ static void setMaxConnecting(Uint32 mc) {max_connecting = mc;}
+ private:
+ virtual void onDataReady(Uint8* buf,Uint32 size);
+ virtual Uint32 onReadyToWrite(Uint8* data,Uint32 max_to_write);
+ virtual bool hasBytesToWrite() const;
+
+ private:
+ net::BufferedSocket* sock;
+ RC4Encryptor* enc;
+ Uint8* reinserted_data;
+ Uint32 reinserted_data_size;
+ Uint32 reinserted_data_read;
+ bool monitored;
+ net::SocketReader* rdr;
+ net::SocketWriter* wrt;
+
+ static Uint8 tos;
+ static Uint32 num_connecting; // the number of connections we have in SYN_SENT state
+ static Uint32 max_connecting;
+ };
+
+}
+
+#endif
diff --git a/libktorrent/net/Makefile.am b/libktorrent/net/Makefile.am
new file mode 100644
index 0000000..e67354c
--- /dev/null
+++ b/libktorrent/net/Makefile.am
@@ -0,0 +1,10 @@
+INCLUDES = -I$(srcdir)/.. -I$(srcdir)/. $(all_includes)
+METASOURCES = AUTO
+libnet_la_LDFLAGS = $(all_libraries)
+noinst_LTLIBRARIES = libnet.la
+noinst_HEADERS = address.h bufferedsocket.h circularbuffer.h downloadthread.h \
+ networkthread.h portlist.h socket.h socketmonitor.h speed.h uploadthread.h
+libnet_la_SOURCES = address.cpp bufferedsocket.cpp circularbuffer.cpp \
+ downloadthread.cpp networkthread.cpp portlist.cpp socket.cpp socketgroup.cpp \
+ socketmonitor.cpp speed.cpp uploadthread.cpp
+KDE_CXXFLAGS = $(USE_EXCEPTIONS) $(USE_RTTI)
diff --git a/libktorrent/net/address.cpp b/libktorrent/net/address.cpp
new file mode 100644
index 0000000..4a4da3c
--- /dev/null
+++ b/libktorrent/net/address.cpp
@@ -0,0 +1,67 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include "address.h"
+
+namespace net
+{
+
+ Address::Address() : m_ip(0),m_port(0) {}
+
+ Address::Address(const QString & host,Uint16 port) : m_ip(0),m_port(port)
+ {
+ struct in_addr a;
+ if (inet_aton(host.ascii(),&a))
+ m_ip = ntohl(a.s_addr);
+ }
+
+ Address::Address(const Address & addr) : m_ip(addr.ip()),m_port(addr.port())
+ {
+ }
+
+ Address:: ~Address()
+ {}
+
+
+ Address & Address::operator = (const Address & a)
+ {
+ m_ip = a.ip();
+ m_port = a.port();
+ return *this;
+ }
+
+
+ bool Address::operator == (const Address & a)
+ {
+ return m_ip == a.ip() && m_port == a.port();
+ }
+
+ QString Address::toString() const
+ {
+ return QString("%1.%2.%3.%4")
+ .arg((m_ip & 0xFF000000) >> 24)
+ .arg((m_ip & 0x00FF0000) >> 16)
+ .arg((m_ip & 0x0000FF00) >> 8)
+ .arg(m_ip & 0x000000FF);
+ }
+
+}
diff --git a/libktorrent/net/address.h b/libktorrent/net/address.h
new file mode 100644
index 0000000..28c4e2c
--- /dev/null
+++ b/libktorrent/net/address.h
@@ -0,0 +1,60 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef NETADDRESS_H
+#define NETADDRESS_H
+
+#include <qstring.h>
+#include <util/constants.h>
+
+namespace net
+{
+ using bt::Uint32;
+ using bt::Uint16;
+
+ /**
+ @author Joris Guisson <joris.guisson@gmail.com>
+ */
+ class Address
+ {
+ Uint32 m_ip;
+ Uint16 m_port;
+ public:
+ Address();
+ Address(const QString & host,Uint16 port);
+ Address(const Address & addr);
+ virtual ~Address();
+
+
+ Address & operator = (const Address & a);
+ bool operator == (const Address & a);
+
+ Uint32 ip() const {return m_ip;}
+ void setIP(Uint32 ip) {m_ip = ip;}
+
+ Uint16 port() const {return m_port;}
+ void setPort(Uint16 p) {m_port = p;}
+
+ QString toString() const;
+
+ };
+
+}
+
+#endif
diff --git a/libktorrent/net/bufferedsocket.cpp b/libktorrent/net/bufferedsocket.cpp
new file mode 100644
index 0000000..2165f70
--- /dev/null
+++ b/libktorrent/net/bufferedsocket.cpp
@@ -0,0 +1,217 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <util/log.h>
+#include <torrent/globals.h>
+#include "bufferedsocket.h"
+#include "circularbuffer.h"
+#include "speed.h"
+
+using namespace bt;
+
+namespace net
+{
+#define OUTPUT_BUFFER_SIZE 16393
+
+ BufferedSocket::BufferedSocket(int fd) : Socket(fd),rdr(0),wrt(0),up_gid(0),down_gid(0)
+ {
+ bytes_in_output_buffer = 0;
+ bytes_sent = 0;
+ down_speed = new Speed();
+ up_speed = new Speed();
+ output_buffer = new Uint8[OUTPUT_BUFFER_SIZE];
+ poll_index = -1;
+ }
+
+ BufferedSocket::BufferedSocket(bool tcp) : Socket(tcp),rdr(0),wrt(0),up_gid(0),down_gid(0)
+ {
+ bytes_in_output_buffer = 0;
+ bytes_sent = 0;
+ down_speed = new Speed();
+ up_speed = new Speed();
+ output_buffer = new Uint8[OUTPUT_BUFFER_SIZE];
+ poll_index = -1;
+ }
+
+
+ BufferedSocket::~BufferedSocket()
+ {
+ delete [] output_buffer;
+ delete up_speed;
+ delete down_speed;
+ }
+
+ void BufferedSocket::setGroupID(Uint32 gid,bool upload)
+ {
+ if (upload)
+ up_gid = gid;
+ else
+ down_gid = gid;
+ }
+
+ float BufferedSocket::getDownloadRate() const
+ {
+ mutex.lock();
+ float ret = down_speed->getRate();
+ mutex.unlock();
+ return ret;
+ }
+
+ float BufferedSocket::getUploadRate() const
+ {
+ mutex.lock();
+ float ret = up_speed->getRate();
+ mutex.unlock();
+ return ret;
+ }
+
+ static Uint8 input_buffer[OUTPUT_BUFFER_SIZE];
+
+ Uint32 BufferedSocket::readBuffered(Uint32 max_bytes_to_read,bt::TimeStamp now)
+ {
+ Uint32 br = 0;
+ bool no_limit = (max_bytes_to_read == 0);
+
+ if (bytesAvailable() == 0)
+ {
+ close();
+ return 0;
+ }
+
+ while ((br < max_bytes_to_read || no_limit) && bytesAvailable() > 0)
+ {
+ Uint32 tr = bytesAvailable();
+ if (tr > OUTPUT_BUFFER_SIZE)
+ tr = OUTPUT_BUFFER_SIZE;
+ if (!no_limit && tr + br > max_bytes_to_read)
+ tr = max_bytes_to_read - br;
+
+ int ret = Socket::recv(input_buffer,tr);
+ if (ret != 0)
+ {
+ mutex.lock();
+ down_speed->onData(ret,now);
+ mutex.unlock();
+ if (rdr)
+ rdr->onDataReady(input_buffer,ret);
+ br += ret;
+ }
+ else
+ {
+ // connection closed, so just return the number of bytes read
+ return br;
+ }
+ }
+ return br;
+ }
+
+ Uint32 BufferedSocket::sendOutputBuffer(Uint32 max,bt::TimeStamp now)
+ {
+ if (bytes_in_output_buffer == 0)
+ return 0;
+
+ if (max == 0 || bytes_in_output_buffer <= max)
+ {
+ // try to send everything
+ Uint32 bw = bytes_in_output_buffer;
+ Uint32 off = bytes_sent;
+ Uint32 ret = Socket::send(output_buffer + off,bw);
+ if (ret > 0)
+ {
+ mutex.lock();
+ up_speed->onData(ret,now);
+ mutex.unlock();
+ bytes_in_output_buffer -= ret;
+ bytes_sent += ret;
+ if (bytes_sent == bytes_in_output_buffer)
+ bytes_in_output_buffer = bytes_sent = 0;
+ return ret;
+ }
+ else
+ {
+ return 0;
+ }
+ }
+ else
+ {
+ Uint32 bw = max;
+ Uint32 off = bytes_sent;
+ Uint32 ret = Socket::send(output_buffer + off,bw);
+ if (ret > 0)
+ {
+ mutex.lock();
+ up_speed->onData(ret,now);
+ mutex.unlock();
+ bytes_in_output_buffer -= ret;
+ bytes_sent += ret;
+ return ret;
+ }
+ else
+ {
+ return 0;
+ }
+ }
+ }
+
+ Uint32 BufferedSocket::writeBuffered(Uint32 max,bt::TimeStamp now)
+ {
+ if (!wrt)
+ return 0;
+
+ Uint32 bw = 0;
+ bool no_limit = max == 0;
+ if (bytes_in_output_buffer > 0)
+ {
+ Uint32 ret = sendOutputBuffer(max,now);
+ if (bytes_in_output_buffer > 0)
+ {
+ // haven't sent it fully so return
+ return ret;
+ }
+
+ bw += ret;
+ }
+
+ // run as long as we do not hit the limit and we can send everything
+ while ((no_limit || bw < max) && bytes_in_output_buffer == 0)
+ {
+ // fill output buffer
+ bytes_in_output_buffer = wrt->onReadyToWrite(output_buffer,OUTPUT_BUFFER_SIZE);
+ bytes_sent = 0;
+ if (bytes_in_output_buffer > 0)
+ {
+ // try to send
+ bw += sendOutputBuffer(max - bw,now);
+ }
+ else
+ {
+ // no bytes available in output buffer so break
+ break;
+ }
+ }
+
+ return bw;
+ }
+
+ void BufferedSocket::updateSpeeds(bt::TimeStamp now)
+ {
+ up_speed->update(now);
+ down_speed->update(now);
+ }
+}
diff --git a/libktorrent/net/bufferedsocket.h b/libktorrent/net/bufferedsocket.h
new file mode 100644
index 0000000..2c0c3ec
--- /dev/null
+++ b/libktorrent/net/bufferedsocket.h
@@ -0,0 +1,150 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef NETBUFFEREDSOCKET_H
+#define NETBUFFEREDSOCKET_H
+
+#include <qmutex.h>
+#include <net/socket.h>
+
+namespace net
+{
+ using bt::Uint8;
+ using bt::Uint32;
+
+ class Speed;
+
+ class SocketReader
+ {
+ public:
+ SocketReader() {}
+ virtual ~SocketReader() {}
+
+ /**
+ * Function which will be called whenever data has been read from the socket.
+ * This data should be dealt with, otherwise it will be discarded.
+ * @param buf The buffer
+ * @param size The size of the buffer
+ */
+ virtual void onDataReady(Uint8* buf,Uint32 size) = 0;
+ };
+
+ class SocketWriter
+ {
+ public:
+ SocketWriter() {}
+ virtual ~SocketWriter() {}
+
+ /**
+ * The socket is ready to write, the writer is asked to provide the data.
+ * The data will be fully sent, before another request is done.
+ * @param data The data
+ * @param max_to_write The maximum number of bytes to put in the buffer
+ * @param The number of bytes placed in the buffer
+ */
+ virtual Uint32 onReadyToWrite(Uint8* data,Uint32 max_to_write) = 0;
+
+ /// Check if data is ready to write
+ virtual bool hasBytesToWrite() const = 0;
+
+ };
+
+ /**
+ * @author Joris Guisson <joris.guisson@gmail.com>
+ *
+ * Extends the Socket class with
+ */
+ class BufferedSocket : public Socket
+ {
+ mutable QMutex mutex;
+ SocketReader* rdr;
+ SocketWriter* wrt;
+ Uint8* output_buffer;
+ Uint32 bytes_in_output_buffer; // bytes in the output buffer
+ Uint32 bytes_sent; // bytes written of the output buffer
+ Speed* down_speed;
+ Speed* up_speed;
+ int poll_index;
+
+ Uint32 up_gid;
+ Uint32 down_gid; // group id which this torrent belongs to, group 0 means the default group
+
+ public:
+ BufferedSocket(int fd);
+ BufferedSocket(bool tcp);
+ virtual ~BufferedSocket();
+
+ /**
+ * Set the group ID of the socket
+ * @param gid THe ID (0 is default group)
+ * @param upload Wether this is an upload group or a download group
+ */
+ void setGroupID(Uint32 gid,bool upload);
+
+ /// Get the download group ID
+ Uint32 downloadGroupID() const {return down_gid;}
+
+ /// Get the upload group ID
+ Uint32 uploadGroupID() const {return up_gid;}
+
+ void setReader(SocketReader* r) {rdr = r;}
+ void setWriter(SocketWriter* r) {wrt = r;}
+
+ /**
+ * Reads data from the socket to the buffer.
+ * @param max_bytes_to_read Maximum number of bytes to read (0 is no limit)
+ * @param now Current time stamp
+ * @return The number of bytes read
+ */
+ Uint32 readBuffered(Uint32 max_bytes_to_read,bt::TimeStamp now);
+
+ /**
+ * Writes data from the buffer to the socket.
+ * @param max The maximum number of bytes to send over the socket (0 = no limit)
+ * * @param now Current time stamp
+ * @return The number of bytes written
+ */
+ Uint32 writeBuffered(Uint32 max,bt::TimeStamp now);
+
+ /// See if the socket has something ready to write
+ bool bytesReadyToWrite() const
+ {
+ return bytes_in_output_buffer > 0 || (!wrt ? false : wrt->hasBytesToWrite());
+ }
+
+
+ /// Get the current download rate
+ float getDownloadRate() const;
+
+ /// Get the current download rate
+ float getUploadRate() const;
+
+ /// Update up and down speed
+ void updateSpeeds(bt::TimeStamp now);
+
+ int getPollIndex() const {return poll_index;}
+ void setPollIndex(int pi) {poll_index = pi;}
+
+ private:
+ Uint32 sendOutputBuffer(Uint32 max,bt::TimeStamp now);
+ };
+
+}
+
+#endif
diff --git a/libktorrent/net/circularbuffer.cpp b/libktorrent/net/circularbuffer.cpp
new file mode 100644
index 0000000..abce80a
--- /dev/null
+++ b/libktorrent/net/circularbuffer.cpp
@@ -0,0 +1,146 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <util/log.h>
+#include <torrent/globals.h>
+#include "circularbuffer.h"
+#include "bufferedsocket.h"
+
+using namespace bt;
+
+namespace net
+{
+
+ CircularBuffer::CircularBuffer(Uint32 max_size) : buf(0),max_size(max_size),first(0),size(0)
+ {
+ buf = new Uint8[max_size];
+ }
+
+
+ CircularBuffer::~CircularBuffer()
+ {
+ delete [] buf;
+ }
+
+ Uint32 CircularBuffer::freeSpace() const
+ {
+ return max_size - size;
+ }
+
+ Uint32 CircularBuffer::write(const Uint8* data,Uint32 dsize)
+ {
+ if (size == max_size)
+ return 0;
+
+ mutex.lock();
+ Uint32 wp = (first + size) % max_size;
+ Uint32 j = 0;
+ while (size < max_size && (dsize == 0 || j < dsize))
+ {
+ buf[wp] = data[j];
+ j++;
+ wp = (wp + 1) % max_size;
+ size++;
+ }
+
+ mutex.unlock();
+ return j;
+ }
+
+ Uint32 CircularBuffer::read(Uint8* data,Uint32 max_to_read)
+ {
+ if (!size)
+ return 0;
+
+ mutex.lock();
+ Uint32 j = 0;
+ while (size > 0 && j < max_to_read)
+ {
+ data[j] = buf[first];
+ j++;
+ first = (first + 1) % max_size;
+ size--;
+ }
+ mutex.unlock();
+ return j;
+ }
+
+ Uint32 CircularBuffer::send(BufferedSocket* s,Uint32 max)
+ {
+ if (!size)
+ return 0;
+
+ Uint32 ret = 0;
+ mutex.lock();
+
+ if (first + size <= max_size)
+ {
+ Uint32 ts = size;
+ if (max > 0 && size > max)
+ ts = max;
+ ret = s->send(buf + first,ts);
+ first += ret;
+ size -= ret;
+ }
+ else if (max > 0) // if there is a limit
+ {
+ // write from first to the end of the buffer
+ Uint32 to_send = max_size - first;
+ if (to_send > max)
+ to_send = max;
+
+ ret = s->send(buf + first,to_send);
+
+ // update first, wrap around if necessary
+ first = (first + ret) % max_size;
+ size -= ret; // ret bytes less in the buffer
+ max -= ret; // decrease limit
+
+ if (max > 0 && ret == to_send && size > 0)
+ {
+ // we have sent everything so we can send more
+ to_send = size > max ? max : size;
+ Uint32 ret2 = s->send(buf,to_send);
+
+ ret += ret2;
+ first += ret2;
+ size -= ret2;
+ }
+ }
+ else // no limit
+ {
+ Uint32 to_send = max_size - first;
+ ret = s->send(buf + first,to_send);
+ // update first, wrap around if necessary
+ first = (first + ret) % max_size;
+ size -= ret; // ret bytes less in the buffer
+ if (ret == to_send && size > 0)
+ {
+ // we have sent everything so we can send more
+ Uint32 ret2 = s->send(buf,size);
+ ret += ret2;
+ first += ret2;
+ size -= ret2;
+ }
+ }
+ mutex.unlock();
+ return ret;
+ }
+
+}
diff --git a/libktorrent/net/circularbuffer.h b/libktorrent/net/circularbuffer.h
new file mode 100644
index 0000000..63e271e
--- /dev/null
+++ b/libktorrent/net/circularbuffer.h
@@ -0,0 +1,89 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef NETCIRCULARBUFFER_H
+#define NETCIRCULARBUFFER_H
+
+#include <qmutex.h>
+#include <util/constants.h>
+
+namespace net
+{
+ using bt::Uint8;
+ using bt::Uint32;
+
+ class BufferedSocket;
+
+ /**
+ * @author Joris Guisson <joris.guisson@gmail.com>
+ *
+ * Simple circular buffer, to simulate a queue.
+ * Writes happen at the end, reads at the beginning.
+ * The buffer is protected by a mutex.
+ */
+ class CircularBuffer
+ {
+ Uint8* buf;
+ Uint32 max_size;
+ Uint32 first; // index of first byte in the buffer
+ Uint32 size; // number of bytes in use
+ mutable QMutex mutex;
+ public:
+ /**
+ * Create the buffer.
+ * @param max_size Maximum size of the buffer.
+ */
+ CircularBuffer(Uint32 max_size);
+ virtual ~CircularBuffer();
+
+ /// How much capacity does the buffer have
+ Uint32 capacity() const {return max_size;}
+
+ /// How much free space is there
+ Uint32 freeSpace() const;
+
+
+ /**
+ * Write a bunch of data at the back of the buffer.
+ * @param data Data to write
+ * @param size How many bytes to write
+ * @return The number of bytes written in the buffer
+ */
+ Uint32 write(const Uint8* data,Uint32 size);
+
+ /**
+ * Read from the buffer.
+ * @param data Buffer to store read data
+ * @param max_to_read Maximum amount of bytes to read
+ * @return The number of bytes read
+ */
+ Uint32 read(Uint8* data,Uint32 max_to_read);
+
+ /**
+ * Send the data in the buffer over the socket
+ * @param s THe socket
+ * @param max Maximum bytes to send
+ * @return The number of bytes written
+ */
+ Uint32 send(BufferedSocket* s,Uint32 max);
+ };
+
+}
+
+#endif
diff --git a/libktorrent/net/downloadthread.cpp b/libktorrent/net/downloadthread.cpp
new file mode 100644
index 0000000..ae0f0b9
--- /dev/null
+++ b/libktorrent/net/downloadthread.cpp
@@ -0,0 +1,137 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <math.h>
+#include <sys/poll.h>
+#include <util/functions.h>
+#include "socketgroup.h"
+#include "downloadthread.h"
+#include "socketmonitor.h"
+#include "bufferedsocket.h"
+
+using namespace bt;
+
+namespace net
+{
+ Uint32 DownloadThread::dcap = 0;
+ Uint32 DownloadThread::sleep_time = 3;
+
+ DownloadThread::DownloadThread(SocketMonitor* sm) : NetworkThread(sm)
+ {
+ }
+
+
+ DownloadThread::~DownloadThread()
+ {}
+
+ void DownloadThread::update()
+ {
+ sm->lock();
+ int num = fillPollVector();
+ sm->unlock();
+
+ int timeout = 10;
+ if (poll(&fd_vec[0],num,timeout) > 0)
+ {
+ sm->lock();
+ TimeStamp now = bt::Now();
+ Uint32 num_ready = 0;
+ SocketMonitor::Itr itr = sm->begin();
+ while (itr != sm->end())
+ {
+ BufferedSocket* s = *itr;
+ int pi = s->getPollIndex();
+ if (pi >= 0 && s->ok() && fd_vec[pi].revents & POLLIN)
+ {
+ // add to the correct group
+ Uint32 gid = s->downloadGroupID();
+ SocketGroup* g = groups.find(gid);
+ if (!g)
+ g = groups.find(0);
+
+ g->add(s);
+ num_ready++;
+ }
+ itr++;
+ }
+
+ if (num_ready > 0)
+ doGroups(num_ready,now,dcap);
+ prev_run_time = now;
+ sm->unlock();
+ }
+
+ if (dcap > 0 || groups.count() > 0)
+ msleep(sleep_time);
+ }
+
+ int DownloadThread::fillPollVector()
+ {
+ TimeStamp ts = bt::Now();
+ int i = 0;
+
+ // fill the poll vector with all sockets
+ SocketMonitor::Itr itr = sm->begin();
+ while (itr != sm->end())
+ {
+ BufferedSocket* s = *itr;
+ if (s && s->ok() && s->fd() > 0)
+ {
+ if (fd_vec.size() <= i)
+ {
+ // expand pollfd vector if necessary
+ struct pollfd pfd;
+ pfd.fd = s->fd();
+ pfd.revents = 0;
+ pfd.events = POLLIN;
+ fd_vec.push_back(pfd);
+ }
+ else
+ {
+ // use existing slot
+ struct pollfd & pfd = fd_vec[i];
+ pfd.fd = s->fd();
+ pfd.revents = 0;
+ pfd.events = POLLIN;
+ }
+ s->setPollIndex(i);
+ i++;
+ s->updateSpeeds(ts);
+ }
+ else
+ {
+ s->setPollIndex(-1);
+ }
+ itr++;
+ }
+
+ return i;
+ }
+
+ void DownloadThread::setSleepTime(Uint32 stime)
+ {
+ if (stime >= 1 && stime <= 10)
+ sleep_time = stime;
+ }
+
+ bool DownloadThread::doGroup(SocketGroup* g,Uint32 & allowance,bt::TimeStamp now)
+ {
+ return g->download(allowance,now);
+ }
+}
diff --git a/libktorrent/net/downloadthread.h b/libktorrent/net/downloadthread.h
new file mode 100644
index 0000000..08e9e46
--- /dev/null
+++ b/libktorrent/net/downloadthread.h
@@ -0,0 +1,64 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef NETDOWNLOADTHREAD_H
+#define NETDOWNLOADTHREAD_H
+
+#include <vector>
+#include "networkthread.h"
+
+struct pollfd;
+
+namespace net
+{
+
+ /**
+ * @author Joris Guisson <joris.guisson@gmail.com>
+ *
+ * Thread which processes incoming data
+ */
+ class DownloadThread : public NetworkThread
+ {
+ static bt::Uint32 dcap;
+ static bt::Uint32 sleep_time;
+
+ std::vector<struct pollfd> fd_vec;
+
+ public:
+ DownloadThread(SocketMonitor* sm);
+ virtual ~DownloadThread();
+
+
+ /// Set the download cap
+ static void setCap(bt::Uint32 cap) {dcap = cap;}
+
+ /// Set the sleep time when using download caps
+ static void setSleepTime(bt::Uint32 stime);
+ private:
+ int fillPollVector();
+
+ virtual void update();
+ virtual bool doGroup(SocketGroup* g,Uint32 & allowance,bt::TimeStamp now);
+
+// void processIncomingData(bt::TimeStamp now);
+ };
+
+}
+
+#endif
diff --git a/libktorrent/net/networkthread.cpp b/libktorrent/net/networkthread.cpp
new file mode 100644
index 0000000..40791c9
--- /dev/null
+++ b/libktorrent/net/networkthread.cpp
@@ -0,0 +1,165 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <math.h>
+#include <util/functions.h>
+#include <util/log.h>
+#include "socketgroup.h"
+#include "socketmonitor.h"
+#include "networkthread.h"
+
+using namespace bt;
+
+namespace net
+{
+
+ NetworkThread::NetworkThread(SocketMonitor* sm)
+ : sm(sm),running(false)
+ {
+ groups.setAutoDelete(true);
+ groups.insert(0,new SocketGroup(0));
+ }
+
+
+ NetworkThread::~NetworkThread()
+ {}
+
+ void NetworkThread::run()
+ {
+ running = true;
+ prev_run_time = bt::Now();
+ while (running)
+ update();
+ }
+
+ void NetworkThread::addGroup(Uint32 gid,Uint32 limit)
+ {
+ // if group already exists, just change the limit
+ SocketGroup* g = groups.find(gid);
+ if (g)
+ {
+ g->setLimit(limit);
+ }
+ else
+ {
+ g = new SocketGroup(limit);
+ groups.insert(gid,g);
+ }
+ }
+
+ void NetworkThread::removeGroup(Uint32 gid)
+ {
+ // make sure the 0 group is never erased
+ if (gid != 0)
+ groups.erase(gid);
+ }
+
+ void NetworkThread::setGroupLimit(Uint32 gid,Uint32 limit)
+ {
+ SocketGroup* g = groups.find(gid);
+ if (g)
+ {
+ g->setLimit(limit);
+ }
+ }
+
+ Uint32 NetworkThread::doGroupsLimited(Uint32 num_ready,bt::TimeStamp now,Uint32 & allowance)
+ {
+ Uint32 num_still_ready = 0;
+
+ // this is one pass over all the groups
+ bt::PtrMap<Uint32,SocketGroup>::iterator itr = groups.begin();
+ while (itr != groups.end() && allowance > 0)
+ {
+ SocketGroup* g = itr->second;
+ if (g->numSockets() > 0)
+ {
+ Uint32 group_allowance = (Uint32)ceil(((double)g->numSockets() / num_ready) * allowance);
+
+ // lets not do to much and make sure we don't pass 0 to the socket group (0 is unlimited)
+ if (group_allowance > allowance || group_allowance == 0)
+ group_allowance = allowance;
+
+ Uint32 ga = group_allowance;
+
+ if (!doGroup(g,ga,now))
+ g->clear(); // group is done, so clear it
+ else
+ num_still_ready += g->numSockets(); // keep track of the number of sockets which are still ready
+
+ Uint32 done = group_allowance - ga;
+ if (allowance >= done)
+ allowance -= done;
+ else
+ allowance = 0;
+ }
+ itr++;
+ }
+
+ return num_still_ready > 0;
+ }
+
+ void NetworkThread::doGroups(Uint32 num_ready,bt::TimeStamp now,bt::Uint32 limit)
+ {
+ if (limit == 0)
+ {
+ Uint32 allowance = 0;
+ bt::PtrMap<Uint32,SocketGroup>::iterator itr = groups.begin();
+ while (itr != groups.end())
+ {
+ SocketGroup* g = itr->second;
+ if (g->numSockets() > 0)
+ {
+ g->calcAllowance(now);
+ doGroup(g,allowance,now);
+ g->clear();
+ }
+ itr++;
+ }
+ }
+ else
+ {
+ // calculate group allowance for each group
+ bt::PtrMap<Uint32,SocketGroup>::iterator itr = groups.begin();
+ while (itr != groups.end())
+ {
+ SocketGroup* g = itr->second;
+ g->calcAllowance(now);
+ itr++;
+ }
+
+ Uint32 allowance = (Uint32)ceil(1.02 * limit * (now - prev_run_time) * 0.001);
+
+ while (allowance > 0 && num_ready > 0)
+ {
+ // loop until nobody is ready anymore or the allowance is up
+ num_ready = doGroupsLimited(num_ready,now,allowance);
+ }
+
+ // make sure all groups are cleared
+ itr = groups.begin();
+ while (itr != groups.end())
+ {
+ SocketGroup* g = itr->second;
+ g->clear();
+ itr++;
+ }
+ }
+ }
+}
diff --git a/libktorrent/net/networkthread.h b/libktorrent/net/networkthread.h
new file mode 100644
index 0000000..7472c15
--- /dev/null
+++ b/libktorrent/net/networkthread.h
@@ -0,0 +1,113 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef NETNETWORKTHREAD_H
+#define NETNETWORKTHREAD_H
+
+#include <qthread.h>
+#include <util/constants.h>
+#include <util/ptrmap.h>
+
+using bt::Uint32;
+
+namespace net
+{
+ class SocketMonitor;
+ class SocketGroup;
+ class BufferedSocket;
+
+ /**
+ @author Joris Guisson <joris.guisson@gmail.com>
+
+ Base class for the 2 networking threads. Handles the socket groups.
+ */
+ class NetworkThread : public QThread
+ {
+ protected:
+ SocketMonitor* sm;
+ bool running;
+ bt::PtrMap<Uint32,SocketGroup> groups;
+ bt::TimeStamp prev_run_time;
+
+ public:
+ NetworkThread(SocketMonitor* sm);
+ virtual ~NetworkThread();
+
+
+ /**
+ * Add a new group with a given limit
+ * @param gid The group ID (cannot be 0, 0 is the default group)
+ * @param limit The limit in bytes per sec
+ */
+ void addGroup(Uint32 gid,Uint32 limit);
+
+ /**
+ * Remove a group
+ * @param gid The group ID
+ */
+ void removeGroup(Uint32 gid);
+
+ /**
+ * Set the limit for a group
+ * @param gid The group ID
+ * @param limit The limit
+ */
+ void setGroupLimit(Uint32 gid,Uint32 limit);
+
+ /**
+ * The main function of the thread
+ */
+ void run();
+
+ /**
+ * Subclasses must implement this function
+ */
+ virtual void update() = 0;
+
+ /**
+ * Do one SocketGroup
+ * @param g The group
+ * @param allowance The groups allowance
+ * @param now The current time
+ * @return true if the group can go again
+ */
+ virtual bool doGroup(SocketGroup* g,Uint32 & allowance,bt::TimeStamp now) = 0;
+
+ /// Stop before the next update
+ void stop() {running = false;}
+
+ /// Is the thread running
+ bool isRunning() const {return running;}
+
+ protected:
+ /**
+ * Go over all groups and do them
+ * @param num_ready The number of ready sockets
+ * @param now The current time
+ * @param limit The global limit in bytes per sec
+ */
+ void doGroups(Uint32 num_ready,bt::TimeStamp now,bt::Uint32 limit);
+
+ private:
+ Uint32 doGroupsLimited(Uint32 num_ready,bt::TimeStamp now,Uint32 & allowance);
+ };
+
+}
+
+#endif
diff --git a/libktorrent/net/portlist.cpp b/libktorrent/net/portlist.cpp
new file mode 100644
index 0000000..56076ed
--- /dev/null
+++ b/libktorrent/net/portlist.cpp
@@ -0,0 +1,73 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include "portlist.h"
+
+namespace net
+{
+ Port::Port() : number(0),proto(TCP),forward(false)
+ {
+ }
+
+ Port::Port(bt::Uint16 number,Protocol proto,bool forward)
+ : number(number),proto(proto),forward(forward)
+ {
+ }
+
+ Port::Port(const Port & p) : number(p.number),proto(p.proto),forward(p.forward)
+ {
+ }
+
+ bool Port::operator == (const Port & p) const
+ {
+ return number == p.number && proto == p.proto;
+ }
+
+ PortList::PortList() : lst(0)
+ {}
+
+
+ PortList::~PortList()
+ {}
+
+
+ void PortList::addNewPort(bt::Uint16 number,Protocol proto,bool forward)
+ {
+ Port p = Port(number,proto,forward);
+ append(p);
+ if (lst)
+ lst->portAdded(p);
+ }
+
+
+ void PortList::removePort(bt::Uint16 number,Protocol proto)
+ {
+ PortList::iterator itr = find(Port(number,proto,false));
+ if (itr == end())
+ return;
+
+ if (lst)
+ lst->portRemoved(*itr);
+
+ erase(itr);
+ }
+
+
+
+}
diff --git a/libktorrent/net/portlist.h b/libktorrent/net/portlist.h
new file mode 100644
index 0000000..af60c1c
--- /dev/null
+++ b/libktorrent/net/portlist.h
@@ -0,0 +1,103 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef NETPORTLIST_H
+#define NETPORTLIST_H
+
+#include <qvaluelist.h>
+#include <util/constants.h>
+
+namespace net
+{
+ enum Protocol
+ {
+ TCP,
+ UDP
+ };
+
+ struct Port
+ {
+ bt::Uint16 number;
+ Protocol proto;
+ bool forward;
+
+ Port();
+ Port(bt::Uint16 number,Protocol proto,bool forward);
+ Port(const Port & p);
+
+ bool operator == (const Port & p) const;
+ };
+
+ /**
+ * Listener class for the PortList.
+ */
+ class PortListener
+ {
+ public:
+ /**
+ * A port has been added.
+ * @param port The port
+ */
+ virtual void portAdded(const Port & port) = 0;
+
+ /**
+ * A port has been removed
+ * @param port The port
+ */
+ virtual void portRemoved(const Port & port) = 0;
+ };
+
+ /**
+ * @author Joris Guisson <joris.guisson@gmail.com>
+ *
+ * List of ports which are currently being used.
+ *
+ */
+ class PortList : public QValueList<Port>
+ {
+ PortListener* lst;
+ public:
+ PortList();
+ virtual ~PortList();
+
+ /**
+ * When a port is in use, this function needs to be called.
+ * @param number Port number
+ * @param proto Protocol
+ * @param forward Wether or not it needs to be forwarded
+ */
+ void addNewPort(bt::Uint16 number,Protocol proto,bool forward);
+
+ /**
+ * Needs to be called when a port is not being using anymore.
+ * @param number Port number
+ * @param proto Protocol
+ */
+ void removePort(bt::Uint16 number,Protocol proto);
+
+ /**
+ * Set the port listener.
+ * @param pl Port listener
+ */
+ void setListener(PortListener* pl) {lst = pl;}
+ };
+
+}
+
+#endif
diff --git a/libktorrent/net/socket.cpp b/libktorrent/net/socket.cpp
new file mode 100644
index 0000000..b9a53f3
--- /dev/null
+++ b/libktorrent/net/socket.cpp
@@ -0,0 +1,326 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+
+#include <qglobal.h>
+
+#include <unistd.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+
+#if defined(Q_OS_LINUX) && !defined(__FreeBSD_kernel__)
+#include <asm/ioctls.h>
+#endif
+
+#ifdef Q_OS_SOLARIS
+#include <sys/filio.h>
+#endif
+
+#ifndef MSG_NOSIGNAL
+#define MSG_NOSIGNAL 0
+#endif
+
+#include <unistd.h>
+#include <fcntl.h>
+
+#include <torrent/globals.h>
+#include <util/log.h>
+#include "socket.h"
+
+using namespace bt;
+
+namespace net
+{
+
+ Socket::Socket(int fd) : m_fd(fd),m_state(IDLE)
+ {
+#if defined(Q_OS_MACX) || defined(Q_OS_DARWIN) || (defined(Q_OS_FREEBSD) && !defined(__DragonFly__) && __FreeBSD_version < 600020)
+ int val = 1;
+ if (setsockopt(m_fd,SOL_SOCKET,SO_NOSIGPIPE,&val,sizeof(int)) < 0)
+ {
+ Out(SYS_CON|LOG_NOTICE) << QString("Failed to set the NOSIGPIPE option : %1").arg(strerror(errno)) << endl;
+ }
+#endif
+ cacheAddress();
+ }
+
+ Socket::Socket(bool tcp) : m_fd(-1),m_state(IDLE)
+ {
+ int fd = socket(PF_INET,tcp ? SOCK_STREAM : SOCK_DGRAM,0);
+ if (fd < 0)
+ {
+ Out(SYS_GEN|LOG_IMPORTANT) << QString("Cannot create socket : %1").arg(strerror(errno)) << endl;
+ }
+ m_fd = fd;
+#if defined(Q_OS_MACX) || defined(Q_OS_DARWIN) || (defined(Q_OS_FREEBSD) && !defined(__DragonFly__) && __FreeBSD_version < 600020)
+ int val = 1;
+ if (setsockopt(m_fd,SOL_SOCKET,SO_NOSIGPIPE,&val,sizeof(int)) < 0)
+ {
+ Out(SYS_CON|LOG_NOTICE) << QString("Failed to set the NOSIGPIPE option : %1").arg(strerror(errno)) << endl;
+ }
+#endif
+ }
+
+ Socket::~Socket()
+ {
+ if (m_fd >= 0)
+ {
+ shutdown(m_fd, SHUT_RDWR);
+ ::close(m_fd);
+ }
+ }
+
+ void Socket::close()
+ {
+ if (m_fd >= 0)
+ {
+ shutdown(m_fd, SHUT_RDWR);
+ ::close(m_fd);
+ m_fd = -1;
+ m_state = CLOSED;
+ }
+ }
+
+ void Socket::setNonBlocking()
+ {
+ fcntl(m_fd, F_SETFL, O_NONBLOCK);
+ }
+
+ bool Socket::connectTo(const Address & a)
+ {
+ struct sockaddr_in addr;
+ memset(&addr,0,sizeof(struct sockaddr_in));
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(a.port());
+ addr.sin_addr.s_addr = htonl(a.ip());
+
+ if (::connect(m_fd,(struct sockaddr*)&addr,sizeof(struct sockaddr)) < 0)
+ {
+ if (errno == EINPROGRESS)
+ {
+ // Out(SYS_CON|LOG_DEBUG) << "Socket is connecting" << endl;
+ m_state = CONNECTING;
+ return false;
+ }
+ else
+ {
+ Out(SYS_CON|LOG_NOTICE) << QString("Cannot connect to host %1:%2 : %3")
+ .arg(a.toString()).arg(a.port()).arg(strerror(errno)) << endl;
+ return false;
+ }
+ }
+ m_state = CONNECTED;
+ cacheAddress();
+ return true;
+ }
+
+ bool Socket::bind(Uint16 port,bool also_listen)
+ {
+ struct sockaddr_in addr;
+ memset(&addr,0,sizeof(struct sockaddr_in));
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(port);
+
+ if (::bind(m_fd,(struct sockaddr*)&addr,sizeof(struct sockaddr)) < 0)
+ {
+ Out(SYS_CON|LOG_IMPORTANT) << QString("Cannot bind to port %1 : %2").arg(port).arg(strerror(errno)) << endl;
+ return false;
+ }
+
+ if (also_listen && listen(m_fd,5) < 0)
+ {
+ Out(SYS_CON|LOG_IMPORTANT) << QString("Cannot listen to port %1 : %2").arg(port).arg(strerror(errno)) << endl;
+ return false;
+ }
+
+ int val = 1;
+ if (setsockopt(m_fd,SOL_SOCKET,SO_REUSEADDR,&val,sizeof(int)) < 0)
+ {
+ Out(SYS_CON|LOG_NOTICE) << QString("Failed to set the reuseaddr option : %1").arg(strerror(errno)) << endl;
+ }
+ m_state = BOUND;
+ return true;
+ }
+
+ int Socket::send(const bt::Uint8* buf,int len)
+ {
+ int ret = ::send(m_fd,buf,len,MSG_NOSIGNAL);
+ if (ret < 0)
+ {
+ if (errno != EAGAIN && errno != EWOULDBLOCK)
+ {
+ // Out(SYS_CON|LOG_DEBUG) << "Send error : " << QString(strerror(errno)) << endl;
+ close();
+ }
+ return 0;
+ }
+ return ret;
+ }
+
+ int Socket::recv(bt::Uint8* buf,int max_len)
+ {
+ int ret = ::recv(m_fd,buf,max_len,0);
+ if (ret < 0)
+ {
+ if (errno != EAGAIN && errno != EWOULDBLOCK)
+ {
+ // Out(SYS_CON|LOG_DEBUG) << "Receive error : " << QString(strerror(errno)) << endl;
+ close();
+ }
+ return 0;
+ }
+ else if (ret == 0)
+ {
+ // connection closed
+ close();
+ return 0;
+ }
+ return ret;
+ }
+
+ int Socket::sendTo(const bt::Uint8* buf,int len,const Address & a)
+ {
+ struct sockaddr_in addr;
+ memset(&addr,0,sizeof(struct sockaddr_in));
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(a.port());
+ addr.sin_addr.s_addr = htonl(a.ip());
+
+ int ns = 0;
+ while (ns < len)
+ {
+ int left = len - ns;
+ int ret = ::sendto(m_fd,(char*)buf + ns,left,0,(struct sockaddr*)&addr,sizeof(struct sockaddr));
+ if (ret < 0)
+ {
+ Out(SYS_CON|LOG_DEBUG) << "Send error : " << QString(strerror(errno)) << endl;
+ return 0;
+ }
+
+ ns += ret;
+ }
+ return ns;
+ }
+
+ int Socket::recvFrom(bt::Uint8* buf,int max_len,Address & a)
+ {
+ struct sockaddr_in addr;
+ memset(&addr,0,sizeof(struct sockaddr_in));
+ socklen_t sl = sizeof(struct sockaddr);
+
+ int ret = ::recvfrom(m_fd,buf,max_len,0,(struct sockaddr*)&addr,&sl);
+ if (ret < 0)
+ {
+ Out(SYS_CON|LOG_DEBUG) << "Receive error : " << QString(strerror(errno)) << endl;
+ return 0;
+ }
+
+ a.setPort(ntohs(addr.sin_port));
+ a.setIP(ntohl(addr.sin_addr.s_addr));
+ return ret;
+ }
+
+ int Socket::accept(Address & a)
+ {
+ struct sockaddr_in addr;
+ memset(&addr,0,sizeof(struct sockaddr_in));
+ socklen_t slen = sizeof(struct sockaddr_in);
+
+ int sfd = ::accept(m_fd,(struct sockaddr*)&addr,&slen);
+ if (sfd < 0)
+ {
+ Out(SYS_CON|LOG_DEBUG) << "Accept error : " << QString(strerror(errno)) << endl;
+ return -1;
+ }
+
+ a.setPort(ntohs(addr.sin_port));
+ a.setIP(ntohl(addr.sin_addr.s_addr));
+
+ Out(SYS_CON|LOG_DEBUG) << "Accepted connection from " << QString(inet_ntoa(addr.sin_addr)) << endl;
+ return sfd;
+ }
+
+ bool Socket::setTOS(unsigned char type_of_service)
+ {
+#if defined(Q_OS_MACX) || defined(Q_OS_DARWIN) || (defined(Q_OS_FREEBSD) && __FreeBSD_version < 600020) || defined(Q_OS_NETBSD) || defined(Q_OS_OPENBSD) || defined(Q_OS_BSD4)
+ unsigned int c = type_of_service;
+#else
+ unsigned char c = type_of_service;
+#endif
+ if (setsockopt(m_fd,IPPROTO_IP,IP_TOS,&c,sizeof(c)) < 0)
+ {
+ Out(SYS_CON|LOG_NOTICE) << QString("Failed to set TOS to %1 : %2")
+ .arg(type_of_service).arg(strerror(errno)) << endl;
+ return false;
+ }
+ return true;
+ }
+
+ Uint32 Socket::bytesAvailable() const
+ {
+ int ret = 0;
+ if (ioctl(m_fd,FIONREAD,&ret) < 0)
+ return 0;
+
+ return ret;
+ }
+
+ bool Socket::connectSuccesFull()
+ {
+ if (m_state != CONNECTING)
+ return false;
+
+ int err = 0;
+ socklen_t len = sizeof(int);
+ if (getsockopt(m_fd,SOL_SOCKET,SO_ERROR,&err,&len) < 0)
+ return false;
+
+ if (err == 0)
+ {
+ m_state = CONNECTED;
+ cacheAddress();
+ }
+
+ return err == 0;
+ }
+
+ void Socket::cacheAddress()
+ {
+ struct sockaddr_in raddr;
+ socklen_t slen = sizeof(struct sockaddr_in);
+ if (getpeername(m_fd,(struct sockaddr*)&raddr,&slen) == 0)
+ addr = Address(inet_ntoa(raddr.sin_addr),ntohs(raddr.sin_port));
+ }
+
+ /*
+ void Socket::setReadBufferSize(int rbs)
+ {
+ if (setsockopt(m_fd, SOL_SOCKET, SO_RCVBUF, (char *)&rbs,sizeof(int)) < 0)
+ {
+ Out(SYS_CON|LOG_DEBUG) << "Failed to set read buffer size " << endl;
+ }
+ }
+ */
+}
diff --git a/libktorrent/net/socket.h b/libktorrent/net/socket.h
new file mode 100644
index 0000000..db8953b
--- /dev/null
+++ b/libktorrent/net/socket.h
@@ -0,0 +1,83 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef NETSOCKET_H
+#define NETSOCKET_H
+
+#include <util/constants.h>
+#include "address.h"
+
+namespace net
+{
+
+ /**
+ @author Joris Guisson <joris.guisson@gmail.com>
+ */
+ class Socket
+ {
+ public:
+ enum State
+ {
+ IDLE,
+ CONNECTING,
+ CONNECTED,
+ BOUND,
+ CLOSED
+ };
+
+ Socket(int fd);
+ Socket(bool tcp);
+ virtual ~Socket();
+
+ void setNonBlocking();
+ bool connectTo(const Address & addr);
+ /// See if a connectTo was succesfull in non blocking mode
+ bool connectSuccesFull();
+ bool bind(Uint16 port,bool also_listen);
+ int send(const bt::Uint8* buf,int len);
+ int recv(bt::Uint8* buf,int max_len);
+ int sendTo(const bt::Uint8* buf,int size,const Address & addr);
+ int recvFrom(bt::Uint8* buf,int max_size,Address & addr);
+ int accept(Address & a);
+ bool ok() const {return m_fd >= 0;}
+ int fd() const {return m_fd;}
+ bool setTOS(unsigned char type_of_service);
+ const Address & getPeerName() const {return addr;}
+ void close();
+ State state() const {return m_state;}
+
+ /**
+ * Set the size of the TCP read buffer.
+ * @param rbs
+ */
+// void setReadBufferSize(Uint32 rbs);
+
+ Uint32 bytesAvailable() const;
+ private:
+ void cacheAddress();
+
+ private:
+ int m_fd;
+ State m_state;
+ Address addr;
+ };
+
+}
+
+#endif
diff --git a/libktorrent/net/socketgroup.cpp b/libktorrent/net/socketgroup.cpp
new file mode 100644
index 0000000..8c9c5e7
--- /dev/null
+++ b/libktorrent/net/socketgroup.cpp
@@ -0,0 +1,186 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <math.h>
+#include <util/log.h>
+#include <util/functions.h>
+#include "socketgroup.h"
+#include "bufferedsocket.h"
+
+using namespace bt;
+
+namespace net
+{
+
+ SocketGroup::SocketGroup(Uint32 limit) : limit(limit)
+ {
+ prev_run_time = bt::GetCurrentTime();
+ group_allowance = 0;
+ }
+
+
+ SocketGroup::~SocketGroup()
+ {}
+
+ void SocketGroup::processUnlimited(bool up,bt::TimeStamp now)
+ {
+ std::list<BufferedSocket*>::iterator i = sockets.begin();
+ while (i != sockets.end())
+ {
+ BufferedSocket* s = *i;
+ if (s)
+ {
+ if (up)
+ s->writeBuffered(0,now);
+ else
+ s->readBuffered(0,now);
+ }
+ i++;
+ }
+ }
+
+ bool SocketGroup::processLimited(bool up,bt::TimeStamp now,Uint32 & allowance)
+ {
+ Uint32 bslot = allowance / sockets.size() + 1;
+
+ std::list<BufferedSocket*>::iterator itr = sockets.begin();
+
+ // while we can send and there are sockets left to send
+ while (sockets.size() > 0 && allowance > 0)
+ {
+ Uint32 as = bslot;
+ if (as > allowance)
+ as = allowance;
+
+ BufferedSocket* s = *itr;
+ if (s)
+ {
+ Uint32 ret = 0;
+ if (up)
+ ret = s->writeBuffered(as,now);
+ else
+ ret = s->readBuffered(as,now);
+
+ // if this socket did what it was supposed to do,
+ // it can have another go if stuff is leftover
+ // if it doesn't, we erase it from the list
+ if (ret != as)
+ itr = sockets.erase(itr);
+ else
+ itr++;
+
+ if (ret > allowance)
+ allowance = 0;
+ else
+ allowance -= ret;
+ }
+ else
+ {
+ // 0 pointer so just erase
+ itr = sockets.erase(itr);
+ }
+
+ // wrap around if necessary
+ if (itr == sockets.end())
+ itr = sockets.begin();
+ }
+
+ return sockets.size() > 0;
+ }
+
+ bool SocketGroup::download(Uint32 & global_allowance,bt::TimeStamp now)
+ {
+ return process(false,now,global_allowance);
+ }
+
+ bool SocketGroup::upload(Uint32 & global_allowance,bt::TimeStamp now)
+ {
+ return process(true,now,global_allowance);
+ }
+
+ void SocketGroup::calcAllowance(bt::TimeStamp now)
+ {
+ if (limit > 0)
+ group_allowance = (Uint32)ceil(1.02 * limit * (now - prev_run_time) * 0.001);
+ else
+ group_allowance = 0;
+ prev_run_time = now;
+ }
+
+ bool SocketGroup::process(bool up,bt::TimeStamp now,Uint32 & global_allowance)
+ {
+ if (limit > 0)
+ {
+ bool ret = false;
+ if (global_allowance == 0)
+ {
+ Uint32 p = group_allowance;
+ ret = processLimited(up,now,p);
+ group_allowance = p;
+ }
+ else if (global_allowance <= group_allowance)
+ {
+ Uint32 tmp = global_allowance;
+ ret = processLimited(up,now,tmp);
+
+ Uint32 done = (global_allowance - tmp);
+ if (group_allowance < done)
+ group_allowance = 0;
+ else
+ group_allowance -= done;
+
+ global_allowance = tmp;
+ }
+ else
+ {
+ Uint32 p = group_allowance;
+ ret = processLimited(up,now,p);
+
+ Uint32 done = (group_allowance - p);
+ if (global_allowance < done)
+ global_allowance = 0;
+ else
+ global_allowance -= done;
+
+ group_allowance = p;
+ }
+
+ // if group allowance is used up, this group can no longer do anything
+ if (group_allowance == 0)
+ {
+ clear();
+ return false;
+ }
+ else
+ return ret;
+ }
+ else if (global_allowance > 0)
+ {
+ return processLimited(up,now,global_allowance);
+ }
+ else
+ {
+ processUnlimited(up,now);
+ return false;
+ }
+ }
+
+
+
+}
diff --git a/libktorrent/net/socketgroup.h b/libktorrent/net/socketgroup.h
new file mode 100644
index 0000000..ba08029
--- /dev/null
+++ b/libktorrent/net/socketgroup.h
@@ -0,0 +1,90 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef NETSOCKETGROUP_H
+#define NETSOCKETGROUP_H
+
+#include <list>
+#include <util/constants.h>
+
+namespace net
+{
+ using bt::Uint32;
+
+ class BufferedSocket;
+
+ /**
+ @author Joris Guisson <joris.guisson@gmail.com>
+ */
+ class SocketGroup
+ {
+ Uint32 limit;
+ std::list<BufferedSocket*> sockets;
+ bt::TimeStamp prev_run_time;
+ Uint32 group_allowance;
+ public:
+ SocketGroup(Uint32 limit);
+ virtual ~SocketGroup();
+
+ /// Clear the lists of sockets
+ void clear() {sockets.clear();}
+
+ /// Add a socket for processing
+ void add(BufferedSocket* s) {sockets.push_back(s);}
+
+ /**
+ Process all the sockets in the vector for download.
+ @param global_allowance How much the group can do, this will be updated, 0 means no limit
+ @param now Current time
+ @return true if we can download more data, false otherwise
+ */
+ bool download(Uint32 & global_allowance,bt::TimeStamp now);
+
+ /**
+ Process all the sockets in the vector for upload
+ @param global_allowance How much the group can do, this will be updated, 0 means no limit
+ @param now Current time
+ @return true if we can upload more data, false otherwise
+ */
+ bool upload(Uint32 & global_allowance,bt::TimeStamp now);
+
+ /**
+ * Set the group limit in bytes per sec
+ * @param lim The limit
+ */
+ void setLimit(Uint32 lim) {limit = lim;}
+
+ /// Get the number of sockets
+ Uint32 numSockets() const {return sockets.size();}
+
+ /**
+ * Calculate the allowance for this group
+ * @param now Current timestamp
+ */
+ void calcAllowance(bt::TimeStamp now);
+ private:
+ void processUnlimited(bool up,bt::TimeStamp now);
+ bool processLimited(bool up,bt::TimeStamp now,Uint32 & allowance);
+ bool process(bool up,bt::TimeStamp now,Uint32 & global_allowance);
+ };
+
+
+}
+
+#endif
diff --git a/libktorrent/net/socketmonitor.cpp b/libktorrent/net/socketmonitor.cpp
new file mode 100644
index 0000000..38225ab
--- /dev/null
+++ b/libktorrent/net/socketmonitor.cpp
@@ -0,0 +1,173 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <math.h>
+#include <unistd.h>
+#include <util/functions.h>
+#include <util/log.h>
+#include <torrent/globals.h>
+#include "socketmonitor.h"
+#include "bufferedsocket.h"
+#include "uploadthread.h"
+#include "downloadthread.h"
+
+using namespace bt;
+
+namespace net
+{
+ SocketMonitor SocketMonitor::self;
+
+ SocketMonitor::SocketMonitor() : ut(0),dt(0),next_group_id(1)
+ {
+ dt = new DownloadThread(this);
+ ut = new UploadThread(this);
+ }
+
+
+ SocketMonitor::~SocketMonitor()
+ {
+ if (ut && ut->isRunning())
+ {
+ ut->stop();
+ ut->signalDataReady(); // kick it in the nuts, if the thread is waiting for data
+ if (!ut->wait(250))
+ {
+ ut->terminate();
+ ut->wait();
+ }
+ }
+
+
+ if (dt && dt->isRunning())
+ {
+ dt->stop();
+ if (!dt->wait(250))
+ {
+ dt->terminate();
+ dt->wait();
+ }
+ }
+
+ delete ut;
+ delete dt;
+ }
+
+ void SocketMonitor::lock()
+ {
+ mutex.lock();
+ }
+
+ void SocketMonitor::unlock()
+ {
+ mutex.unlock();
+ }
+
+ void SocketMonitor::setDownloadCap(Uint32 bytes_per_sec)
+ {
+ DownloadThread::setCap(bytes_per_sec);
+ }
+
+ void SocketMonitor::setUploadCap(Uint32 bytes_per_sec)
+ {
+ UploadThread::setCap(bytes_per_sec);
+ }
+
+ void SocketMonitor::setSleepTime(Uint32 sleep_time)
+ {
+ DownloadThread::setSleepTime(sleep_time);
+ UploadThread::setSleepTime(sleep_time);
+ }
+
+ void SocketMonitor::add(BufferedSocket* sock)
+ {
+ QMutexLocker lock(&mutex);
+
+ bool start_threads = smap.count() == 0;
+ smap.append(sock);
+
+ if (start_threads)
+ {
+ Out(SYS_CON|LOG_DEBUG) << "Starting socketmonitor threads" << endl;
+
+ if (!dt->isRunning())
+ dt->start(QThread::IdlePriority);
+ if (!ut->isRunning())
+ ut->start(QThread::IdlePriority);
+ }
+ }
+
+ void SocketMonitor::remove(BufferedSocket* sock)
+ {
+ QMutexLocker lock(&mutex);
+ if (smap.count() == 0)
+ return;
+
+ smap.remove(sock);
+ if (smap.count() == 0)
+ {
+ Out(SYS_CON|LOG_DEBUG) << "Stopping socketmonitor threads" << endl;
+ if (dt && dt->isRunning())
+ dt->stop();
+ if (ut && ut->isRunning())
+ {
+ ut->stop();
+ ut->signalDataReady();
+ }
+ }
+ }
+
+ void SocketMonitor::signalPacketReady()
+ {
+ if (ut)
+ ut->signalDataReady();
+ }
+
+ Uint32 SocketMonitor::newGroup(GroupType type,Uint32 limit)
+ {
+ lock();
+ Uint32 gid = next_group_id++;
+ if (type == UPLOAD_GROUP)
+ ut->addGroup(gid,limit);
+ else
+ dt->addGroup(gid,limit);
+ unlock();
+ return gid;
+ }
+
+ void SocketMonitor::setGroupLimit(GroupType type,Uint32 gid,Uint32 limit)
+ {
+ lock();
+ if (type == UPLOAD_GROUP)
+ ut->setGroupLimit(gid,limit);
+ else
+ dt->setGroupLimit(gid,limit);
+ unlock();
+ }
+
+ void SocketMonitor::removeGroup(GroupType type,Uint32 gid)
+ {
+ lock();
+ if (type == UPLOAD_GROUP)
+ ut->removeGroup(gid);
+ else
+ dt->removeGroup(gid);
+ unlock();
+ }
+
+}
diff --git a/libktorrent/net/socketmonitor.h b/libktorrent/net/socketmonitor.h
new file mode 100644
index 0000000..79e4a2e
--- /dev/null
+++ b/libktorrent/net/socketmonitor.h
@@ -0,0 +1,118 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef NETSOCKETMONITOR_H
+#define NETSOCKETMONITOR_H
+
+
+#include <qmutex.h>
+#include <qptrlist.h>
+#include <util/constants.h>
+
+
+namespace net
+{
+ using bt::Uint32;
+
+ class BufferedSocket;
+ class UploadThread;
+ class DownloadThread;
+
+ /**
+ * @author Joris Guisson <joris.guisson@gmail.com>
+ *
+ * Monitors all sockets for upload and download traffic.
+ * It uses two threads to do this.
+ */
+ class SocketMonitor
+ {
+ static SocketMonitor self;
+
+ QMutex mutex;
+ UploadThread* ut;
+ DownloadThread* dt;
+ QPtrList<BufferedSocket> smap;
+ Uint32 next_group_id;
+
+ SocketMonitor();
+ public:
+ virtual ~SocketMonitor();
+
+ /// Add a new socket, will start the threads if necessary
+ void add(BufferedSocket* sock);
+
+ /// Remove a socket, will stop threads if no more sockets are left
+ void remove(BufferedSocket* sock);
+
+ enum GroupType
+ {
+ UPLOAD_GROUP,
+ DOWNLOAD_GROUP
+ };
+
+
+ /**
+ * Creata a new upload or download group
+ * @param type Wether it is an upload or download group
+ * @param limit Limit of group in bytes/s
+ * @return The group ID
+ */
+ Uint32 newGroup(GroupType type,Uint32 limit);
+
+ /**
+ * Change the group limit
+ * @param type The group type
+ * @param gid The group id
+ * @param limit The limit
+ */
+ void setGroupLimit(GroupType type,Uint32 gid,Uint32 limit);
+
+ /**
+ * Remove a group
+ * @param type The group type
+ * @param gid The group id
+ */
+ void removeGroup(GroupType type,Uint32 gid);
+
+ typedef QPtrList<BufferedSocket>::iterator Itr;
+
+ /// Get the begin of the list of sockets
+ Itr begin() {return smap.begin();}
+
+ /// Get the end of the list of sockets
+ Itr end() {return smap.end();}
+
+ /// lock the monitor
+ void lock();
+
+ /// unlock the monitor
+ void unlock();
+
+ /// Tell upload thread a packet is ready
+ void signalPacketReady();
+
+ static void setDownloadCap(Uint32 bytes_per_sec);
+ static void setUploadCap(Uint32 bytes_per_sec);
+ static void setSleepTime(Uint32 sleep_time);
+ static SocketMonitor & instance() {return self;}
+ };
+
+}
+
+#endif
diff --git a/libktorrent/net/speed.cpp b/libktorrent/net/speed.cpp
new file mode 100644
index 0000000..aa57513
--- /dev/null
+++ b/libktorrent/net/speed.cpp
@@ -0,0 +1,78 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <util/log.h>
+#include <util/timer.h>
+#include <util/functions.h>
+#include "speed.h"
+
+using namespace bt;
+
+namespace net
+{
+ const Uint64 SPEED_INTERVAL = 5000;
+
+ Speed::Speed() : rate(0),bytes(0)
+ {}
+
+
+ Speed::~Speed()
+ {}
+
+ void Speed::onData(Uint32 b,bt::TimeStamp ts)
+ {
+ dlrate.append(qMakePair(b,ts));
+ bytes += b;
+ }
+
+ void Speed::update(bt::TimeStamp now)
+ {
+ QValueList<QPair<Uint32,TimeStamp> >::iterator i = dlrate.begin();
+ while (i != dlrate.end())
+ {
+ QPair<Uint32,TimeStamp> & p = *i;
+ if (now - p.second > SPEED_INTERVAL || now < p.second)
+ {
+ if (bytes >= p.first) // make sure we don't wrap around
+ bytes -= p.first; // subtract bytes
+ else
+ bytes = 0;
+ i = dlrate.erase(i);
+ }
+ else
+ {
+ // seeing that newer entries are appended, they are in the list chronologically
+ // so once we hit an entry which is in the interval, we can just break out of the loop
+ // because all following entries will be in the interval
+ break;
+ }
+ }
+
+ if (bytes == 0)
+ {
+ rate = 0;
+ }
+ else
+ {
+ // Out() << "bytes = " << bytes << " d = " << d << endl;
+ rate = (float) bytes / (float)(SPEED_INTERVAL * 0.001);
+ }
+ }
+
+}
diff --git a/libktorrent/net/speed.h b/libktorrent/net/speed.h
new file mode 100644
index 0000000..d5825e9
--- /dev/null
+++ b/libktorrent/net/speed.h
@@ -0,0 +1,51 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef NETSPEED_H
+#define NETSPEED_H
+
+#include <qpair.h>
+#include <qvaluelist.h>
+#include <util/constants.h>
+
+namespace net
+{
+
+ /**
+ @author Joris Guisson <joris.guisson@gmail.com>
+
+ Measures the download and upload speed.
+ */
+ class Speed
+ {
+ float rate;
+ bt::Uint32 bytes;
+ QValueList<QPair<bt::Uint32,bt::TimeStamp> > dlrate;
+ public:
+ Speed();
+ virtual ~Speed();
+
+ void onData(bt::Uint32 bytes,bt::TimeStamp ts);
+ void update(bt::TimeStamp now);
+ float getRate() const {return rate;}
+ };
+
+}
+
+#endif
diff --git a/libktorrent/net/uploadthread.cpp b/libktorrent/net/uploadthread.cpp
new file mode 100644
index 0000000..0023cf6
--- /dev/null
+++ b/libktorrent/net/uploadthread.cpp
@@ -0,0 +1,91 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <math.h>
+#include <util/functions.h>
+#include "uploadthread.h"
+#include "socketmonitor.h"
+#include "bufferedsocket.h"
+#include "socketgroup.h"
+
+using namespace bt;
+
+namespace net
+{
+ Uint32 UploadThread::ucap = 0;
+ Uint32 UploadThread::sleep_time = 3;
+
+ UploadThread::UploadThread(SocketMonitor* sm) : NetworkThread(sm)
+ {}
+
+
+ UploadThread::~UploadThread()
+ {}
+
+
+ void UploadThread::update()
+ {
+ sm->lock();
+ bt::TimeStamp now = bt::Now();
+
+ Uint32 num_ready = 0;
+ // loop over all sockets and see which ones have data ready
+ SocketMonitor::Itr itr = sm->begin();
+ while (itr != sm->end())
+ {
+ BufferedSocket* s = *itr;
+ if (s && s->ok() && s->bytesReadyToWrite())
+ {
+ SocketGroup* g = groups.find(s->uploadGroupID());
+ if (!g)
+ g = groups.find(0);
+
+ g->add(s);
+ num_ready++;
+ }
+ itr++;
+ }
+
+ if (num_ready > 0)
+ doGroups(num_ready,now,ucap);
+ prev_run_time = now;
+ sm->unlock();
+
+ if (num_ready == 0) // nobody was ready so go to sleep
+ data_ready.wait();
+ else
+ msleep(sleep_time);
+ }
+
+ void UploadThread::signalDataReady()
+ {
+ data_ready.wakeOne();
+ }
+
+ void UploadThread::setSleepTime(Uint32 stime)
+ {
+ if (stime >= 1 && stime <= 10)
+ sleep_time = stime;
+ }
+
+ bool UploadThread::doGroup(SocketGroup* g,Uint32 & allowance,bt::TimeStamp now)
+ {
+ return g->upload(allowance,now);
+ }
+}
diff --git a/libktorrent/net/uploadthread.h b/libktorrent/net/uploadthread.h
new file mode 100644
index 0000000..265abac
--- /dev/null
+++ b/libktorrent/net/uploadthread.h
@@ -0,0 +1,61 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef NETUPLOADTHREAD_H
+#define NETUPLOADTHREAD_H
+
+
+
+#include <qwaitcondition.h>
+#include "networkthread.h"
+
+namespace net
+{
+ class SocketMonitor;
+ class BufferedSocket;
+
+ /**
+ @author Joris Guisson <joris.guisson@gmail.com>
+ */
+ class UploadThread : public NetworkThread
+ {
+ static bt::Uint32 ucap;
+ static bt::Uint32 sleep_time;
+
+ QWaitCondition data_ready;
+ public:
+ UploadThread(SocketMonitor* sm);
+ virtual ~UploadThread();
+
+ /// Wake up thread, data is ready to be sent
+ void signalDataReady();
+
+ /// Set the upload cap
+ static void setCap(bt::Uint32 uc) {ucap = uc;}
+
+ /// Set the sleep time when using upload caps
+ static void setSleepTime(bt::Uint32 stime);
+ private:
+ virtual void update();
+ virtual bool doGroup(SocketGroup* g,Uint32 & allowance,bt::TimeStamp now);
+ };
+
+}
+
+#endif
diff --git a/libktorrent/pluginmanager.cpp b/libktorrent/pluginmanager.cpp
new file mode 100644
index 0000000..db9e0a3
--- /dev/null
+++ b/libktorrent/pluginmanager.cpp
@@ -0,0 +1,312 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <qfile.h>
+#include <qtextstream.h>
+#include <kparts/componentfactory.h>
+#include <util/log.h>
+#include <util/error.h>
+#include <util/fileops.h>
+#include <util/waitjob.h>
+#include <torrent/globals.h>
+#include <interfaces/guiinterface.h>
+#include "pluginmanager.h"
+#include "pluginmanagerprefpage.h"
+
+using namespace bt;
+
+namespace kt
+{
+
+ PluginManager::PluginManager(CoreInterface* core,GUIInterface* gui) : core(core),gui(gui)
+ {
+ unloaded.setAutoDelete(false);
+ plugins.setAutoDelete(false);
+ prefpage = 0;
+ pltoload.append("Info Widget");
+ pltoload.append("Search");
+ }
+
+ PluginManager::~PluginManager()
+ {
+ delete prefpage;
+ unloaded.setAutoDelete(true);
+ plugins.setAutoDelete(true);
+ }
+
+ void PluginManager::loadPluginList()
+ {
+ KTrader::OfferList offers = KTrader::self()->query("KTorrent/Plugin");
+
+ KTrader::OfferList::ConstIterator iter;
+ for(iter = offers.begin(); iter != offers.end(); ++iter)
+ {
+ KService::Ptr service = *iter;
+ int errCode = 0;
+ Plugin* plugin =
+ KParts::ComponentFactory::createInstanceFromService<kt::Plugin>
+ (service, 0, 0, QStringList(),&errCode);
+
+ if (!plugin)
+ continue;
+
+
+ if (!plugin->versionCheck(kt::VERSION_STRING))
+ {
+ Out(SYS_GEN|LOG_NOTICE) <<
+ QString("Plugin %1 version does not match KTorrent version, unloading it.")
+ .arg(service->library()) << endl;
+
+ delete plugin;
+ // unload the library again, no need to have it loaded
+ KLibLoader::self()->unloadLibrary(service->library().local8Bit());
+ continue;
+ }
+
+ unloaded.insert(plugin->getName(),plugin);
+ if (pltoload.contains(plugin->getName()))
+ load(plugin->getName());
+ }
+
+ if (!prefpage)
+ {
+ prefpage = new PluginManagerPrefPage(this);
+ gui->addPrefPage(prefpage);
+ }
+ prefpage->updatePluginList();
+ }
+
+
+ void PluginManager::load(const QString & name)
+ {
+ Plugin* p = unloaded.find(name);
+ if (!p)
+ return;
+
+ Out(SYS_GEN|LOG_NOTICE) << "Loading plugin "<< p->getName() << endl;
+ p->setCore(core);
+ p->setGUI(gui);
+ p->load();
+ gui->mergePluginGui(p);
+ unloaded.erase(name);
+ plugins.insert(p->getName(),p);
+ p->loaded = true;
+
+ if (!cfg_file.isNull())
+ saveConfigFile(cfg_file);
+ }
+
+ void PluginManager::unload(const QString & name)
+ {
+ Plugin* p = plugins.find(name);
+ if (!p)
+ return;
+
+ // first shut it down properly
+ bt::WaitJob* wjob = new WaitJob(2000);
+ try
+ {
+ p->shutdown(wjob);
+ if (wjob->needToWait())
+ bt::WaitJob::execute(wjob);
+ else
+ delete wjob;
+ }
+ catch (Error & err)
+ {
+ Out(SYS_GEN|LOG_NOTICE) << "Error when unloading plugin: " << err.toString() << endl;
+ }
+
+
+ gui->removePluginGui(p);
+ p->unload();
+ plugins.erase(name);
+ unloaded.insert(p->getName(),p);
+ p->loaded = false;
+
+ if (!cfg_file.isNull())
+ saveConfigFile(cfg_file);
+ }
+
+ void PluginManager::loadAll()
+ {
+ bt::PtrMap<QString,Plugin>::iterator i = unloaded.begin();
+ while (i != unloaded.end())
+ {
+ Plugin* p = i->second;
+ p->setCore(core);
+ p->setGUI(gui);
+ p->load();
+ gui->mergePluginGui(p);
+ plugins.insert(p->getName(),p);
+ p->loaded = true;
+ i++;
+ }
+ unloaded.clear();
+ if (!cfg_file.isNull())
+ saveConfigFile(cfg_file);
+ }
+
+ void PluginManager::unloadAll(bool save)
+ {
+ // first properly shutdown all plugins
+ bt::WaitJob* wjob = new WaitJob(2000);
+ try
+ {
+ bt::PtrMap<QString,Plugin>::iterator i = plugins.begin();
+ while (i != plugins.end())
+ {
+ Plugin* p = i->second;
+ p->shutdown(wjob);
+ i++;
+ }
+
+ if (wjob->needToWait())
+ bt::WaitJob::execute(wjob);
+ else
+ delete wjob;
+ }
+ catch (Error & err)
+ {
+ Out(SYS_GEN|LOG_NOTICE) << "Error when unloading all plugins: " << err.toString() << endl;
+ }
+
+ // then unload them
+ bt::PtrMap<QString,Plugin>::iterator i = plugins.begin();
+ while (i != plugins.end())
+ {
+ Plugin* p = i->second;
+ gui->removePluginGui(p);
+ p->unload();
+ unloaded.insert(p->getName(),p);
+ p->loaded = false;
+ i++;
+ }
+ plugins.clear();
+ if (save && !cfg_file.isNull())
+ saveConfigFile(cfg_file);
+ }
+
+ void PluginManager::updateGuiPlugins()
+ {
+ bt::PtrMap<QString,Plugin>::iterator i = plugins.begin();
+ while (i != plugins.end())
+ {
+ Plugin* p = i->second;
+ p->guiUpdate();
+ i++;
+ }
+ }
+
+ void PluginManager::fillPluginList(QPtrList<Plugin> & plist)
+ {
+ bt::PtrMap<QString,Plugin>::iterator i = plugins.begin();
+ while (i != plugins.end())
+ {
+ Plugin* p = i->second;
+ plist.append(p);
+ i++;
+ }
+
+
+ i = unloaded.begin();
+ while (i != unloaded.end())
+ {
+ Plugin* p = i->second;
+ plist.append(p);
+ i++;
+ }
+ }
+
+ bool PluginManager::isLoaded(const QString & name) const
+ {
+ const Plugin* p = plugins.find(name);
+ return p != 0;
+ }
+
+ void PluginManager::loadConfigFile(const QString & file)
+ {
+ cfg_file = file;
+ // make a default config file if doesn't exist
+ if (!bt::Exists(file))
+ {
+ writeDefaultConfigFile(file);
+ return;
+ }
+
+ QFile f(file);
+ if (!f.open(IO_ReadOnly))
+ {
+ Out(SYS_GEN|LOG_DEBUG) << "Cannot open file " << file << " : " << f.errorString() << endl;
+ return;
+ }
+
+ pltoload.clear();
+
+ QTextStream in(&f);
+ while (!in.atEnd())
+ {
+ QString l = in.readLine();
+ if (l.isNull())
+ break;
+
+ pltoload.append(l);
+ }
+ }
+
+ void PluginManager::saveConfigFile(const QString & file)
+ {
+ cfg_file = file;
+ QFile f(file);
+ if (!f.open(IO_WriteOnly))
+ {
+ Out(SYS_GEN|LOG_DEBUG) << "Cannot open file " << file << " : " << f.errorString() << endl;
+ return;
+ }
+
+ QTextStream out(&f);
+ bt::PtrMap<QString,Plugin>::iterator i = plugins.begin();
+ while (i != plugins.end())
+ {
+ Plugin* p = i->second;
+ out << p->getName() << endl;
+ i++;
+ }
+ }
+
+
+ void PluginManager::writeDefaultConfigFile(const QString & file)
+ {
+ // by default we will load the infowidget and searchplugin
+ QFile f(file);
+ if (!f.open(IO_WriteOnly))
+ {
+ Out(SYS_GEN|LOG_DEBUG) << "Cannot open file " << file << " : " << f.errorString() << endl;
+ return;
+ }
+
+ QTextStream out(&f);
+
+ out << "Info Widget" << endl << "Search" << endl;
+
+ pltoload.clear();
+ pltoload.append("Info Widget");
+ pltoload.append("Search");
+ }
+}
diff --git a/libktorrent/pluginmanager.h b/libktorrent/pluginmanager.h
new file mode 100644
index 0000000..611ec66
--- /dev/null
+++ b/libktorrent/pluginmanager.h
@@ -0,0 +1,118 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef KTPLUGINMANAGER_H
+#define KTPLUGINMANAGER_H
+
+#include <qptrlist.h>
+#include <util/ptrmap.h>
+#include <interfaces/plugin.h>
+#include <qstringlist.h>
+
+
+namespace kt
+{
+ class CoreInterface;
+ class GUIInterface;
+ class PluginManagerPrefPage;
+
+ /**
+ * @author Joris Guisson
+ * @brief Class to manage plugins
+ *
+ * This class manages all plugins. Plugins are stored in a map
+ */
+ class PluginManager
+ {
+ bt::PtrMap<QString,Plugin> plugins,unloaded;
+ CoreInterface* core;
+ GUIInterface* gui;
+ PluginManagerPrefPage* prefpage;
+ QStringList pltoload;
+ QString cfg_file;
+ public:
+ PluginManager(CoreInterface* core,GUIInterface* gui);
+ virtual ~PluginManager();
+
+ /**
+ * Load the list of plugins.
+ * This basically uses KTrader to get a list of available plugins, and
+ * loads those, but does not initialize them. We will consider a plugin loaded
+ * when it's load method is called.
+ */
+ void loadPluginList();
+
+ /**
+ * Loads which plugins need to be loaded from a file.
+ * @param file The file
+ */
+ void loadConfigFile(const QString & file);
+
+ /**
+ * Saves which plugins are loaded to a file.
+ * @param file The file
+ */
+ void saveConfigFile(const QString & file);
+
+ /**
+ * Fill a list with all available plugins.
+ * @param pllist The plugin list
+ */
+ void fillPluginList(QPtrList<Plugin> & plist);
+
+ /**
+ * Is a plugin loaded
+ * @param name Naame of plugin.
+ * @return True if it is, false if it isn't
+ */
+ bool isLoaded(const QString & name) const;
+
+ /**
+ * Load a plugin.
+ * @param name Name of the plugin
+ */
+ void load(const QString & name);
+
+ /**
+ * Unload a plugin.
+ * @param name Name of the plugin
+ */
+ void unload(const QString & name);
+
+ /**
+ * Load all unloaded plugins.
+ */
+ void loadAll();
+
+ /**
+ * Unload all loaded plugins.
+ */
+ void unloadAll(bool save = true);
+
+ /**
+ * Update all plugins who need a periodical GUI update.
+ */
+ void updateGuiPlugins();
+ private:
+ void writeDefaultConfigFile(const QString & file);
+ };
+
+}
+
+#endif
diff --git a/libktorrent/pluginmanagerprefpage.cpp b/libktorrent/pluginmanagerprefpage.cpp
new file mode 100644
index 0000000..8151be4
--- /dev/null
+++ b/libktorrent/pluginmanagerprefpage.cpp
@@ -0,0 +1,213 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <klocale.h>
+#include <kpushbutton.h>
+#include <klistview.h>
+#include <qheader.h>
+#include <kglobal.h>
+#include <kiconloader.h>
+#include <util/constants.h>
+#include "pluginmanager.h"
+#include "pluginmanagerwidget.h"
+#include "pluginmanagerprefpage.h"
+#include "labelview.h"
+
+using namespace bt;
+
+namespace kt
+{
+
+ class PluginViewItem : public LabelViewItem
+ {
+ Plugin* p;
+ public:
+ PluginViewItem(Plugin* p,LabelView* parent)
+ : LabelViewItem(p->getIcon(),p->getGuiName(),p->getDescription(),parent),p(p)
+ {
+ update();
+ }
+
+ virtual ~PluginViewItem()
+ {}
+
+ virtual void update()
+ {
+ setTitle("<h3>" + p->getGuiName() + "</h3>");
+ setDescription(
+ i18n("%1<br>Status: <b>%2</b><br>Author: %3").arg(p->getDescription())
+ .arg(p->isLoaded() ? i18n("Loaded") : i18n("Not loaded"))
+ .arg(p->getAuthor()));
+ }
+
+ QString pluginName() {return p->getName();}
+ };
+
+ PluginManagerPrefPage::PluginManagerPrefPage(PluginManager* pman)
+ : PrefPageInterface(i18n("Plugins"), i18n("Plugin Options"),KGlobal::iconLoader()->loadIcon("ktplugins",KIcon::NoGroup)),pman(pman)
+ {
+ pmw = 0;
+ }
+
+
+ PluginManagerPrefPage::~PluginManagerPrefPage()
+ {}
+
+ bool PluginManagerPrefPage::apply()
+ {
+ return true;
+ }
+
+ void PluginManagerPrefPage::createWidget(QWidget* parent)
+ {
+ pmw = new PluginManagerWidget(parent);
+
+ connect(pmw->load_btn,SIGNAL(clicked()),this,SLOT(onLoad()));
+ connect(pmw->unload_btn,SIGNAL(clicked()),this,SLOT(onUnload()));
+ connect(pmw->load_all_btn,SIGNAL(clicked()),this,SLOT(onLoadAll()));
+ connect(pmw->unload_all_btn,SIGNAL(clicked()),this,SLOT(onUnloadAll()));
+ LabelView* lv = pmw->plugin_view;
+ connect(lv,SIGNAL(currentChanged(LabelViewItem * )),this,SLOT(onCurrentChanged( LabelViewItem* )));
+ }
+
+ void PluginManagerPrefPage::updatePluginList()
+ {
+ LabelView* lv = pmw->plugin_view;
+ lv->clear();
+ // get list of plugins
+ QPtrList<Plugin> pl;
+ pman->fillPluginList(pl);
+
+ // Add them all
+ QPtrList<Plugin>::iterator i = pl.begin();
+ while (i != pl.end())
+ {
+ Plugin* p = *i;
+ lv->addItem(new PluginViewItem(p,lv));
+ i++;
+ }
+ lv->sort();
+ }
+
+ void PluginManagerPrefPage::updateData()
+ {
+ updateAllButtons();
+ }
+
+
+
+ void PluginManagerPrefPage::deleteWidget()
+ {
+ delete pmw;
+ pmw = 0;
+ }
+
+ void PluginManagerPrefPage::onCurrentChanged(LabelViewItem* item)
+ {
+ PluginViewItem* pvi = (PluginViewItem*)item;
+ if (!item)
+ {
+ pmw->load_btn->setEnabled(false);
+ pmw->unload_btn->setEnabled(false);
+ }
+ else
+ {
+ bool loaded = pman->isLoaded(pvi->pluginName());
+ pmw->load_btn->setEnabled(!loaded);
+ pmw->unload_btn->setEnabled(loaded);
+ }
+ }
+
+ void PluginManagerPrefPage::updateAllButtons()
+ {
+ Uint32 tot = 0;
+ Uint32 loaded = 0;
+ // get list of plugins
+ QPtrList<Plugin> pl;
+ pman->fillPluginList(pl);
+
+ QPtrList<Plugin>::iterator i = pl.begin();
+ while (i != pl.end())
+ {
+ Plugin* p = *i;
+ tot++;
+ if (p->isLoaded())
+ loaded++;
+ i++;
+ }
+
+ if (loaded == tot)
+ {
+ pmw->load_all_btn->setEnabled(false);
+ pmw->unload_all_btn->setEnabled(true);
+ }
+ else if (loaded < tot && loaded > 0)
+ {
+ pmw->unload_all_btn->setEnabled(true);
+ pmw->load_all_btn->setEnabled(true);
+ }
+ else
+ {
+ pmw->unload_all_btn->setEnabled(false);
+ pmw->load_all_btn->setEnabled(true);
+ }
+ onCurrentChanged(pmw->plugin_view->selectedItem());
+ }
+
+ void PluginManagerPrefPage::onLoad()
+ {
+ LabelView* lv = pmw->plugin_view;
+ PluginViewItem* vi = (PluginViewItem*)lv->selectedItem();
+ if (vi && !pman->isLoaded(vi->pluginName()))
+ {
+ pman->load(vi->pluginName());
+ vi->update();
+ updateAllButtons();
+ }
+ }
+
+ void PluginManagerPrefPage::onUnload()
+ {
+ LabelView* lv = pmw->plugin_view;
+ PluginViewItem* vi = (PluginViewItem*)lv->selectedItem();
+ if (vi && pman->isLoaded(vi->pluginName()))
+ {
+ pman->unload(vi->pluginName());
+ vi->update();
+ updateAllButtons();
+ }
+ }
+
+ void PluginManagerPrefPage::onLoadAll()
+ {
+ pman->loadAll();
+ LabelView* lv = pmw->plugin_view;
+ lv->update();
+ updateAllButtons();
+ }
+
+ void PluginManagerPrefPage::onUnloadAll()
+ {
+ pman->unloadAll();
+ LabelView* lv = pmw->plugin_view;
+ lv->update();
+ updateAllButtons();
+ }
+
+}
diff --git a/libktorrent/pluginmanagerprefpage.h b/libktorrent/pluginmanagerprefpage.h
new file mode 100644
index 0000000..47df97f
--- /dev/null
+++ b/libktorrent/pluginmanagerprefpage.h
@@ -0,0 +1,70 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef KTPLUGINMANAGERPREFPAGE_H
+#define KTPLUGINMANAGERPREFPAGE_H
+
+#include <qobject.h>
+#include <interfaces/prefpageinterface.h>
+
+class QListViewItem;
+class PluginManagerWidget;
+
+namespace kt
+{
+ class PluginManager;
+ class LabelViewItem;
+
+ /**
+ * @author Joris Guisson
+ *
+ * Pref page which allows to load and unload plugins.
+ */
+ class PluginManagerPrefPage : public QObject,public PrefPageInterface
+ {
+ Q_OBJECT
+ public:
+ PluginManagerPrefPage(PluginManager* pman);
+ virtual ~PluginManagerPrefPage();
+
+ virtual bool apply();
+ virtual void createWidget(QWidget* parent);
+ virtual void updateData();
+ virtual void deleteWidget();
+
+ void updatePluginList();
+
+ private slots:
+ void onLoad();
+ void onUnload();
+ void onLoadAll();
+ void onUnloadAll();
+ void onCurrentChanged(LabelViewItem* item);
+
+ private:
+ void updateAllButtons();
+
+ private:
+ PluginManager* pman;
+ PluginManagerWidget* pmw;
+ };
+
+}
+
+#endif
diff --git a/libktorrent/pluginmanagerwidget.ui b/libktorrent/pluginmanagerwidget.ui
new file mode 100644
index 0000000..f83efe0
--- /dev/null
+++ b/libktorrent/pluginmanagerwidget.ui
@@ -0,0 +1,127 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>PluginManagerWidget</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>PluginManagerWidget</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>600</width>
+ <height>320</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>Plugin Manager</string>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="kt::LabelView">
+ <property name="name">
+ <cstring>plugin_view</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout1</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>load_btn</cstring>
+ </property>
+ <property name="text">
+ <string>Load</string>
+ </property>
+ </widget>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>unload_btn</cstring>
+ </property>
+ <property name="text">
+ <string>U&amp;nload</string>
+ </property>
+ </widget>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>load_all_btn</cstring>
+ </property>
+ <property name="text">
+ <string>Load &amp;All</string>
+ </property>
+ </widget>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>unload_all_btn</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Unload All</string>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer1</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>31</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+ </widget>
+ </hbox>
+</widget>
+<customwidgets>
+ <customwidget>
+ <class>kt::LabelView</class>
+ <header location="global">labelview.h</header>
+ <sizehint>
+ <width>-1</width>
+ <height>-1</height>
+ </sizehint>
+ <container>0</container>
+ <sizepolicy>
+ <hordata>5</hordata>
+ <verdata>5</verdata>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ <pixmap>image0</pixmap>
+ </customwidget>
+</customwidgets>
+<images>
+ <image name="image0">
+ <data format="PNG" length="1125">89504e470d0a1a0a0000000d4948445200000016000000160806000000c4b46c3b0000042c49444154388db5954f6c14551cc73fefcd7476b65bdaae4bb78bb5502a14d404e4801c88182d1c4c2c693da847400f9c24c68b878684238660e2b1e01f12c19493012ef2478c814412d354a46017a8a564bb6da5bbedccee767776e63d0ffb073751d483bfe49799974c3eeffb7ebf37df9fd05a530b2184040cc0042420aaf9a4d0d554800f045a6b256ae0e1e1e1d6bebebe838ee31c48a7d39b5cd7fd075e251cc7617272f2ded8d8d819cff33e0316819259537aead4a9839d5dd6d1784f91f55b0a94830242088404d304292bef68a89f520802a598fecddaa04f1a876f5c250c7c0a64cdeac686e33807e23d45e6b297c8b877f1831542614550b6599835c83c2a81b6786a75134faf2f1169f12997350881d9021d0903e06de0745d3160a6d3e94dbd5b0a64dcbb94b5831d0e3375ab892b1772dcf9790528543f8dd0d367b36768153b5e31503a0f1aecb004580b44ffac58baae8b1714f0833c7638cc8dab303a320f4822ab4c7a37c69196203de3319d5ce1c4d13c733331dedc67a129a154fd128401ab0616d55a130ac3d42d93d1913940d13fd0c9ee0183685c60da01c5421bd72f7a8c8efccef9afd374267ad93d642365be0636a0d28ec7600941d9e6f23917f0e97f23ce5bef35d19ec863da0ed9059b2be70bec196c66dfa10ec0e49b338f7017258651bf95021035c595429bb0903248fe52a2b5b595dd7b4d945cc2340cdca536be389ee3f67886c5798f773fe8e0dac508c989659277a2180da4ca4ff07821058b8b251445d63d6b13ed1098a6417e39cac85197dbe31962ab9bd9f1f22a226d45366f6d0620fdb08c900d281af6110284b20085b414861d905d88f2e52739ee8cbb8022143259d3dd84691730aa2d52da441a8de0c6958068870022a41e9629ad3473fd3b8fdbe319dadb9b4924da994d2d716c7896fbe35152f78b48245d6b2da4507faf582be8eaf159b721cc837b05ae7debb1f79d08cb8b515edad942a22bc4b1c33eb3d34b1c797f06af90a72d16e2f96d9a74aa11dca8586b222d01af0fb60070f6c402d72f15d97f28c6f6d7027a5f5ce6c3233dc4e2ede496b278be4fff608cee8d3e1add806aeca51094cbb06397c1ecc328e746537c7e3ccdb5cb1136bf60635882d4d41c6ec6836ab37efa214f72208ed9f4d7cdd38ee310280542e38b1c43fb6de26b3672e1ec3cc99bcb246f66a938a3241ab3e91f7c861fbf77710b1e5e49915bae974203ba0e9e9c9cbc373d6d6d305a040a89c2a77f50b27d5782bbbf7acccf28349235dd16cf6dd374f7295e1de8a45c02d37499182b01cc0201a085d61a2144d8b2ac8fb6ed340e77240c4261890e04c250185262546d534a032154b59e0ad394e41c98182bf268ce6721ed9f064e0253356f6da2e24c1f030f783c15fe6da680af8021602bd051532ca9b8521488559f61aa86c29343578fbf0264a94c906c7d3409214c20043457a116ff6de6795578012889ff6b98fe016ea0ce1c6a2573410000000049454e44ae426082</data>
+ </image>
+</images>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>labelview.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+</includehints>
+</UI>
diff --git a/libktorrent/settings.kcfgc b/libktorrent/settings.kcfgc
new file mode 100644
index 0000000..f5a6064
--- /dev/null
+++ b/libktorrent/settings.kcfgc
@@ -0,0 +1,6 @@
+# Code generation options for kconfig_compiler
+File=ktorrent.kcfg
+ClassName=Settings
+Singleton=true
+Mutators=true
+# will create the necessary code for setting those variables
diff --git a/libktorrent/torrent/Makefile.am b/libktorrent/torrent/Makefile.am
new file mode 100644
index 0000000..d546228
--- /dev/null
+++ b/libktorrent/torrent/Makefile.am
@@ -0,0 +1,33 @@
+INCLUDES = -I$(top_builddir)/ktorrent/libktorrent -I$(top_builddir)/libktorrent \
+ -I$(srcdir)/.. $(all_includes)
+
+METASOURCES = AUTO
+
+noinst_LTLIBRARIES = libtorrent.la
+libtorrent_la_LDFLAGS = $(all_libraries)
+noinst_HEADERS = advancedchokealgorithm.h announcelist.h authenticate.h \
+ authenticatebase.h authenticationmonitor.h bdecoder.h bencoder.h bnode.h cache.h \
+ cachefile.h cap.h choker.h chunk.h chunkcounter.h chunkdownload.h chunkmanager.h \
+ chunkselector.h dndfile.h downloadcap.h downloader.h globals.h httptracker.h \
+ ipblocklist.h movedatafilesjob.h multifilecache.h newchokealgorithm.h \
+ oldchokealgorithm.h packet.h packetreader.h packetwriter.h peer.h peerdownloader.h peerid.h \
+ peermanager.h peersourcemanager.h peeruploader.h piece.h preallocationthread.h \
+ queuemanager.h request.h server.h serverauthenticate.h singlefilecache.h \
+ speedestimater.h statsfile.h timeestimator.h torrent.h torrentcontrol.h torrentcreator.h \
+ torrentfile.h tracker.h udptracker.h udptrackersocket.h uploadcap.h uploader.h \
+ upspeedestimater.h utpex.h value.h
+
+libtorrent_la_SOURCES = advancedchokealgorithm.cpp announcelist.cpp \
+ authenticate.cpp authenticatebase.cpp authenticationmonitor.cpp bdecoder.cpp \
+ bencoder.cpp bnode.cpp cache.cpp cachefile.cpp cap.cpp choker.cpp chunk.cpp \
+ chunkcounter.cpp chunkdownload.cpp chunkmanager.cpp chunkselector.cpp dndfile.cpp \
+ downloadcap.cpp downloader.cpp globals.cpp httptracker.cpp ipblocklist.cpp \
+ movedatafilesjob.cpp multifilecache.cpp newchokealgorithm.cpp packet.cpp packetreader.cpp \
+ packetwriter.cpp peer.cpp peerdownloader.cpp peerid.cpp peermanager.cpp \
+ peersourcemanager.cpp peeruploader.cpp piece.cpp preallocationthread.cpp queuemanager.cpp \
+ request.cpp server.cpp serverauthenticate.cpp singlefilecache.cpp \
+ speedestimater.cpp statsfile.cpp timeestimator.cpp torrent.cpp torrentcontrol.cpp \
+ torrentcreator.cpp torrentfile.cpp tracker.cpp udptracker.cpp udptrackersocket.cpp \
+ uploadcap.cpp uploader.cpp upspeedestimater.cpp utpex.cpp value.cpp
+
+KDE_CXXFLAGS = $(USE_EXCEPTIONS) $(USE_RTTI)
diff --git a/libktorrent/torrent/advancedchokealgorithm.cpp b/libktorrent/torrent/advancedchokealgorithm.cpp
new file mode 100644
index 0000000..7ca0578
--- /dev/null
+++ b/libktorrent/torrent/advancedchokealgorithm.cpp
@@ -0,0 +1,259 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <stdlib.h>
+#include <util/functions.h>
+#include <interfaces/torrentinterface.h>
+#include "chunkmanager.h"
+#include "peer.h"
+#include "peermanager.h"
+#include "packetwriter.h"
+#include "advancedchokealgorithm.h"
+
+using namespace kt;
+
+namespace bt
+{
+
+
+ const Uint32 OPT_SEL_INTERVAL = 30*1000; // we switch optimistic peer each 30 seconds
+ const double NEWBIE_BONUS = 1.0;
+ const double SNUB_PENALTY = 10.0;
+ const double ONE_MB = 1024*1024;
+
+
+ AdvancedChokeAlgorithm::AdvancedChokeAlgorithm()
+ : ChokeAlgorithm()
+ {
+ last_opt_sel_time = 0;
+ }
+
+
+ AdvancedChokeAlgorithm::~AdvancedChokeAlgorithm()
+ {}
+
+ bool AdvancedChokeAlgorithm::calcACAScore(Peer* p,ChunkManager & cman,const kt::TorrentStats & stats)
+ {
+ const PeerInterface::Stats & s = p->getStats();
+ if (p->isSeeder())
+ {
+ /*
+ double bd = 0;
+ if (stats.trk_bytes_downloaded > 0)
+ bd = s.bytes_downloaded / stats.trk_bytes_downloaded;
+ double ds = 0;
+ if (stats.download_rate > 0)
+ ds = s.download_rate/ stats.download_rate;
+ p->setACAScore(5*bd + 5*ds);
+ */
+ p->setACAScore(0.0);
+ return false;
+ }
+
+ bool should_be_interested = false;
+ bool should_we_be_interested = false;
+ // before we start calculating first check if we have piece that the peer doesn't have
+ const BitSet & ours = cman.getBitSet();
+ const BitSet & theirs = p->getBitSet();
+ for (Uint32 i = 0;i < ours.getNumBits();i++)
+ {
+ if (ours.get(i) && !theirs.get(i))
+ {
+ should_be_interested = true;
+ break;
+ }
+ }
+
+ if (!should_be_interested || !p->isInterested())
+ {
+ // not interseted so it doesn't make sense to unchoke it
+ p->setACAScore(-50.0);
+ return false;
+ }
+
+
+
+ double nb = 0.0; // newbie bonus
+ double cp = 0.0; // choke penalty
+ double sp = 0.0; // snubbing penalty
+ double lb = s.local ? 10.0 : 0.0; // local peers get a bonus of 10
+ double bd = s.bytes_downloaded; // bytes downloaded
+ double tbd = stats.trk_bytes_downloaded; // total bytes downloaded
+ double ds = s.download_rate; // current download rate
+ double tds = stats.download_rate; // total download speed
+
+ // if the peer has less than 1 MB or 0.5 % of the torrent it is a newbie
+ if (p->percentAvailable() < 0.5 && stats.total_bytes * p->percentAvailable() < 1024*1024)
+ {
+ nb = NEWBIE_BONUS;
+ }
+
+ if (p->isChoked())
+ {
+ cp = NEWBIE_BONUS; // cp cancels out newbie bonus
+ }
+
+ // if the evil bit is on (!choked, snubbed and requests have timed out)
+ if (s.evil)
+ {
+ sp = SNUB_PENALTY;
+ }
+
+ // NB + K * (BD/TBD) - CP - SP + L * (DS / TDS)
+ double K = 5.0;
+ double L = 5.0;
+ double aca = lb + nb + (tbd > 0 ? K * (bd/tbd) : 0.0) + (tds > 0 ? L* (ds / tds) : 0.0) - cp - sp;
+
+ p->setACAScore(aca);
+ return true;
+ }
+
+ static int ACACmp(Peer* a,Peer* b)
+ {
+ if (a->getStats().aca_score < b->getStats().aca_score)
+ return 1;
+ else if (a->getStats().aca_score > b->getStats().aca_score)
+ return -1;
+ else
+ return 0;
+ }
+
+
+ void AdvancedChokeAlgorithm::doChokingLeechingState(PeerManager & pman,ChunkManager & cman,const kt::TorrentStats & stats)
+ {
+ PeerPtrList ppl;
+ Uint32 np = pman.getNumConnectedPeers();
+ // add all non seeders
+ for (Uint32 i = 0;i < np;i++)
+ {
+ Peer* p = pman.getPeer(i);
+ if (p)
+ {
+ if (calcACAScore(p,cman,stats))
+ ppl.append(p);
+ else
+ // choke seeders they do not want to download from us anyway
+ p->choke();
+ }
+ }
+
+ // sort list by ACA score
+ ppl.setCompareFunc(ACACmp);
+ ppl.sort();
+
+ doUnchoking(ppl,updateOptimisticPeer(pman,ppl));
+ }
+
+ void AdvancedChokeAlgorithm::doUnchoking(PeerPtrList & ppl,Peer* poup)
+ {
+ // Get the number of upload slots
+ Uint32 num_slots = Choker::getNumUploadSlots();
+ // Do the choking and unchoking
+ Uint32 num_unchoked = 0;
+ for (Uint32 i = 0;i < ppl.count();i++)
+ {
+ Peer* p = ppl.at(i);
+ if (!poup && num_unchoked < num_slots)
+ {
+ p->getPacketWriter().sendUnchoke();
+ num_unchoked++;
+ }
+ else if (num_unchoked < num_slots -1 || p == poup)
+ {
+ p->getPacketWriter().sendUnchoke();
+ if (p != poup)
+ num_unchoked++;
+ }
+ else
+ {
+ p->choke();
+ }
+ }
+ }
+
+ static int UpRateCmp(Peer* a,Peer* b)
+ {
+ if (a->getStats().upload_rate < b->getStats().upload_rate)
+ return -1;
+ else if (a->getStats().upload_rate > b->getStats().upload_rate)
+ return 1;
+ else
+ return 0;
+ }
+
+ void AdvancedChokeAlgorithm::doChokingSeedingState(PeerManager & pman,ChunkManager & cman,const kt::TorrentStats & stats)
+ {
+ PeerPtrList ppl;
+ Uint32 np = pman.getNumConnectedPeers();
+ // add all non seeders
+ for (Uint32 i = 0;i < np;i++)
+ {
+ Peer* p = pman.getPeer(i);
+ if (p)
+ {
+ // update the ACA score in the process
+ if (calcACAScore(p,cman,stats))
+ ppl.append(p);
+ else
+ // choke seeders they do not want to download from us anyway
+ p->choke();
+ }
+ }
+
+ ppl.setCompareFunc(UpRateCmp);
+ ppl.sort();
+
+ doUnchoking(ppl,updateOptimisticPeer(pman,ppl));
+ }
+
+ static Uint32 FindPlannedOptimisticUnchokedPeer(PeerManager& pman,const PeerPtrList & ppl)
+ {
+ Uint32 num_peers = pman.getNumConnectedPeers();
+ if (num_peers == 0)
+ return UNDEFINED_ID;
+
+ // find a random peer that is choked and interested
+ Uint32 start = rand() % num_peers;
+ Uint32 i = (start + 1) % num_peers;
+ while (i != start)
+ {
+ Peer* p = pman.getPeer(i);
+ if (p && p->isChoked() && p->isInterested() && !p->isSeeder() && ppl.contains(p))
+ return p->getID();
+ i = (i + 1) % num_peers;
+ }
+
+ // we do not expect to have 4 billion peers
+ return UNDEFINED_ID;
+ }
+
+ Peer* AdvancedChokeAlgorithm::updateOptimisticPeer(PeerManager & pman,const PeerPtrList & ppl)
+ {
+ // get the planned optimistic unchoked peer and change it if necessary
+ Peer* poup = pman.findPeer(opt_unchoked_peer_id);
+ TimeStamp now = GetCurrentTime();
+ if (now - last_opt_sel_time > OPT_SEL_INTERVAL || !poup)
+ {
+ opt_unchoked_peer_id = FindPlannedOptimisticUnchokedPeer(pman,ppl);
+ last_opt_sel_time = now;
+ poup = pman.findPeer(opt_unchoked_peer_id);
+ }
+ return poup;
+ }
+}
diff --git a/libktorrent/torrent/advancedchokealgorithm.h b/libktorrent/torrent/advancedchokealgorithm.h
new file mode 100644
index 0000000..f8a1086
--- /dev/null
+++ b/libktorrent/torrent/advancedchokealgorithm.h
@@ -0,0 +1,52 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef BTADVANCEDCHOKEALGORITHM_H
+#define BTADVANCEDCHOKEALGORITHM_H
+
+#include "choker.h"
+
+namespace bt
+{
+ class Peer;
+ class PeerPtrList;
+
+
+ /**
+ @author Joris Guisson <joris.guisson@gmail.com>
+ */
+ class AdvancedChokeAlgorithm : public ChokeAlgorithm
+ {
+ TimeStamp last_opt_sel_time; // last time we updated the optimistic unchoked peer
+ public:
+ AdvancedChokeAlgorithm();
+ virtual ~AdvancedChokeAlgorithm();
+
+ virtual void doChokingLeechingState(PeerManager & pman,ChunkManager & cman,const kt::TorrentStats & stats);
+ virtual void doChokingSeedingState(PeerManager & pman,ChunkManager & cman,const kt::TorrentStats & stats);
+
+ private:
+ bool calcACAScore(Peer* p,ChunkManager & cman,const kt::TorrentStats & stats);
+ Peer* updateOptimisticPeer(PeerManager & pman,const PeerPtrList & ppl);
+ void doUnchoking(PeerPtrList & ppl,Peer* poup);
+ };
+
+}
+
+#endif
diff --git a/libktorrent/torrent/announcelist.cpp b/libktorrent/torrent/announcelist.cpp
new file mode 100644
index 0000000..74b3397
--- /dev/null
+++ b/libktorrent/torrent/announcelist.cpp
@@ -0,0 +1,195 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#if 0
+#include "announcelist.h"
+#include "bnode.h"
+#include <util/error.h>
+#include "globals.h"
+#include <util/log.h>
+
+#include <klocale.h>
+#include <qstringlist.h>
+#include <qfile.h>
+#include <qtextstream.h>
+
+namespace bt
+{
+
+ AnnounceList::AnnounceList()
+ :m_datadir(QString::null)
+ {
+ curr = 0;
+ }
+
+
+ AnnounceList::~AnnounceList()
+ {
+ saveTrackers();
+ }
+
+ void AnnounceList::load(BNode* node)
+ {
+ BListNode* ml = dynamic_cast<BListNode*>(node);
+ if (!ml)
+ return;
+
+ //ml->printDebugInfo();
+ for (Uint32 i = 0;i < ml->getNumChildren();i++)
+ {
+ BListNode* url = dynamic_cast<BListNode*>(ml->getChild(i));
+ if (!url)
+ throw Error(i18n("Parse Error"));
+
+ for (Uint32 j = 0;j < url->getNumChildren();j++)
+ {
+ BValueNode* vn = dynamic_cast<BValueNode*>(url->getChild(j));
+ if (!vn)
+ throw Error(i18n("Parse Error"));
+
+ KURL url(vn->data().toString().stripWhiteSpace());
+ trackers.append(url);
+ //Out() << "Added tracker " << url << endl;
+ }
+ }
+ }
+
+ const KURL::List AnnounceList::getTrackerURLs()
+ {
+ KURL::List complete(trackers);
+ complete += custom_trackers;
+ return complete;
+ }
+
+ void AnnounceList::addTracker(KURL url, bool custom)
+ {
+ if(custom)
+ custom_trackers.append(url);
+ else
+ trackers.append(url);
+ }
+
+ bool AnnounceList::removeTracker(KURL url)
+ {
+ KURL::List::iterator i = custom_trackers.find(url);
+ if(i != custom_trackers.end())
+ {
+ custom_trackers.remove(i);
+ return true;
+ }
+ else
+ return false;
+ }
+
+ KURL AnnounceList::getTrackerURL(bool last_was_succesfull) const
+ {
+ int defaults = trackers.count();
+ int customs = custom_trackers.count();
+ int total = defaults + customs;
+
+ if (total == 0)
+ return KURL(); // return invalid url is there are no trackers
+
+ if (last_was_succesfull)
+ return curr < defaults ? *trackers.at(curr) : *custom_trackers.at(curr % customs);
+
+ curr = (curr + 1) % total;
+ return curr < defaults ? *trackers.at(curr) : *custom_trackers.at(curr % customs);
+ }
+
+ void AnnounceList::debugPrintURLList()
+ {
+ Out() << "Announce List : " << endl;
+ for (KURL::List::iterator i = trackers.begin();i != trackers.end();i++)
+ Out() << "URL : " << *i << endl;
+ }
+
+ void AnnounceList::saveTrackers()
+ {
+ QFile file(m_datadir + "trackers");
+ if(!file.open(IO_WriteOnly))
+ return;
+
+ QTextStream stream(&file);
+ for (KURL::List::iterator i = custom_trackers.begin();i != custom_trackers.end();i++)
+ stream << (*i).prettyURL() << ::endl;
+ file.close();
+ }
+
+ void AnnounceList::loadTrackers()
+ {
+ QFile file(m_datadir + "trackers");
+ if(!file.open(IO_ReadOnly))
+ return;
+
+ QTextStream stream(&file);
+ while (!stream.atEnd())
+ {
+ KURL url(stream.readLine().stripWhiteSpace());
+ custom_trackers.append(url);
+ }
+
+ file.close();
+ }
+
+ void AnnounceList::setDatadir(const QString& theValue)
+ {
+ m_datadir = theValue;
+ loadTrackers();
+ }
+
+ void AnnounceList::setTracker(KURL url)
+ {
+ int defaults = trackers.count();
+ int customs = custom_trackers.count();
+ int total = defaults + customs;
+
+ int backup = curr;
+
+ for(curr=0; curr<defaults; ++curr)
+ {
+ if( *trackers.at(curr) == url )
+ return;
+ }
+
+ for( ; curr<total; ++curr)
+ {
+ if( *custom_trackers.at(curr % customs) == url )
+ return;
+ }
+
+ curr = backup;
+ }
+
+ void AnnounceList::restoreDefault()
+ {
+ curr = 0;
+ }
+
+ void AnnounceList::merge(const AnnounceList* al)
+ {
+ for (Uint32 i = 0;i < al->getNumTrackerURLs();i++)
+ {
+ KURL url = *al->trackers.at(i);
+ if (!trackers.contains(url) && !custom_trackers.contains(url))
+ custom_trackers.append(url);
+ }
+ }
+}
+#endif
diff --git a/libktorrent/torrent/announcelist.h b/libktorrent/torrent/announcelist.h
new file mode 100644
index 0000000..38f9e72
--- /dev/null
+++ b/libktorrent/torrent/announcelist.h
@@ -0,0 +1,107 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef BTANNOUNCELIST_H
+#define BTANNOUNCELIST_H
+
+#if 0
+#include <kurl.h>
+#include <qstring.h>
+#include <interfaces/trackerslist.h>
+
+namespace bt
+{
+ class BNode;
+
+ /**
+ * @author Joris Guisson
+ * @brief Keep track of a list of trackers
+ *
+ * This class keeps track of a list of tracker URL.
+ */
+ class AnnounceList : public kt::TrackersList
+ {
+ KURL::List trackers;
+ KURL::List custom_trackers;
+
+ public:
+ AnnounceList();
+ virtual ~AnnounceList();
+
+ /**
+ * Load the list from a bencoded list of lists.
+ * @param node The BNode
+ */
+ void load(BNode* node);
+
+ /**
+ * Get a new tracker url.
+ * @param last_was_succesfull Wether or not the last url was succesfull
+ * @return An URL
+ */
+ KURL getTrackerURL(bool last_was_succesfull) const;
+
+
+ ///Gets a list of trackers (URLs)
+ const KURL::List getTrackerURLs();
+
+ ///Adds new tracker URL to the list
+ void addTracker(KURL url, bool custom = true);
+
+ /**
+ * Removes a tracker from the list
+ * @param url Tracker URL to remove from custom trackers list.
+ * @returns TRUE if URL is in custom list and it is removed or FALSE if it could not be removed or it's a default tracker
+ */
+ bool removeTracker(KURL url);
+
+ ///Changes current tracker
+ void setTracker(KURL url);
+
+ ///Restores the default torrent tracker
+ void restoreDefault();
+
+ /// Get the number of tracker URLs
+ unsigned int getNumTrackerURLs() const {return trackers.count();}
+
+ void debugPrintURLList();
+
+ ///Saves custom trackers in a file
+ void saveTrackers();
+
+ ///Loads custom trackers from a file
+ void loadTrackers();
+
+ void setDatadir(const QString& theValue);
+
+ /**
+ * Merge an other announce list to this one.
+ * @param al The AnnounceList
+ */
+ void merge(const AnnounceList* al);
+
+ private:
+ QString m_datadir;
+
+ };
+
+}
+#endif
+
+#endif
diff --git a/libktorrent/torrent/authenticate.cpp b/libktorrent/torrent/authenticate.cpp
new file mode 100644
index 0000000..14e34ea
--- /dev/null
+++ b/libktorrent/torrent/authenticate.cpp
@@ -0,0 +1,156 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <util/log.h>
+#include <mse/streamsocket.h>
+#include "authenticate.h"
+#include "ipblocklist.h"
+#include "peermanager.h"
+
+namespace bt
+{
+
+ Authenticate::Authenticate(const QString & ip,Uint16 port,
+ const SHA1Hash & info_hash,const PeerID & peer_id,PeerManager* pman)
+ : info_hash(info_hash),our_peer_id(peer_id),pman(pman)
+ {
+ finished = succes = false;
+ sock = new mse::StreamSocket();
+ host = ip;
+ this->port = port;
+
+ Out(SYS_CON|LOG_NOTICE) << "Initiating connection to " << host << endl;
+
+ if (sock->connectTo(host,port))
+ {
+ connected();
+ }
+ else if (sock->connecting())
+ {
+ // do nothing the monitor will notify us when we are connected
+ }
+ else
+ {
+ onFinish(false);
+ }
+ }
+
+ Authenticate::~Authenticate()
+ {
+ }
+
+ void Authenticate::onReadyWrite()
+ {
+// Out() << "Authenticate::onReadyWrite()" << endl;
+ if (sock->connectSuccesFull())
+ {
+ connected();
+ }
+ else
+ {
+ onFinish(false);
+ }
+ }
+
+ void Authenticate::connected()
+ {
+ sendHandshake(info_hash,our_peer_id);
+ }
+
+ void Authenticate::onFinish(bool succes)
+ {
+ Out(SYS_CON|LOG_NOTICE) << "Authentication to " << host << " : " << (succes ? "ok" : "failure") << endl;
+ finished = true;
+ this->succes = succes;
+
+ if (!succes)
+ {
+ sock->deleteLater();
+ sock = 0;
+ }
+ timer.stop();
+ if (pman)
+ pman->peerAuthenticated(this,succes);
+ }
+
+ void Authenticate::handshakeRecieved(bool full)
+ {
+ const Uint8* hs = handshake;
+ // Out() << "Authenticate::handshakeRecieved" << endl;
+ IPBlocklist& ipfilter = IPBlocklist::instance();
+ //Out() << "Dodo " << pp.ip << endl;
+ if (ipfilter.isBlocked(host))
+ {
+ onFinish(false);
+ return;
+ }
+
+ SHA1Hash rh(hs+28);
+ if (rh != info_hash)
+ {
+ Out() << "Wrong info_hash : " << rh.toString() << endl;
+ onFinish(false);
+ return;
+ }
+
+ char tmp[21];
+ tmp[20] = '\0';
+ memcpy(tmp,hs+48,20);
+ peer_id = PeerID(tmp);
+
+ if (our_peer_id == peer_id /*|| peer_id.startsWith("Yoda")*/)
+ {
+ Out(SYS_CON|LOG_DEBUG) << "Lets not connect to our selves " << endl;
+ onFinish(false);
+ return;
+ }
+
+ // check if we aren't already connected to the client
+ if (pman->connectedTo(peer_id))
+ {
+ Out(SYS_CON|LOG_NOTICE) << "Already connected to " << peer_id.toString() << endl;
+ onFinish(false);
+ return;
+ }
+
+ // only finish when the handshake was fully received
+ if (full)
+ onFinish(true);
+ }
+
+
+ mse::StreamSocket* Authenticate::takeSocket()
+ {
+ mse::StreamSocket* s = sock;
+ sock = 0;
+ return s;
+ }
+
+ void Authenticate::onPeerManagerDestroyed()
+ {
+ // Out(SYS_CON|LOG_NOTICE) << "Authenticate::onPeerManagerDestroyed()" << endl;
+ pman = 0;
+ if (finished)
+ return;
+
+ onFinish(false);
+ }
+
+}
+#include "authenticate.moc"
diff --git a/libktorrent/torrent/authenticate.h b/libktorrent/torrent/authenticate.h
new file mode 100644
index 0000000..03c8d75
--- /dev/null
+++ b/libktorrent/torrent/authenticate.h
@@ -0,0 +1,98 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef BTAUTHENTICATE_H
+#define BTAUTHENTICATE_H
+
+
+#include <util/sha1hash.h>
+#include "authenticatebase.h"
+#include "globals.h"
+#include "peerid.h"
+
+
+namespace bt
+{
+
+
+ class PeerManager;
+
+
+ /**
+ * @author Joris Guisson
+ * @brief Authenicate a peer
+ *
+ * After we connect to a peer,
+ * we need to authenticate the peer. This class handles this.
+ */
+ class Authenticate : public AuthenticateBase
+ {
+ Q_OBJECT
+ public:
+
+ /**
+ * Connect to a remote host first and authenicate it.
+ * @param ip IP-address of host
+ * @param port Port of host
+ * @param info_hash Info hash
+ * @param peer_id Peer ID
+ * @param pman PeerManager
+ */
+ Authenticate(const QString & ip,Uint16 port,
+ const SHA1Hash & info_hash,const PeerID & peer_id,
+ PeerManager* pman);
+
+ virtual ~Authenticate();
+
+ /**
+ * Get a pointer to the socket, and set it internally
+ * to NULL. After a succesfull authentication, this is used
+ * to transfer ownership to a Peer object.
+ * @return The socket
+ */
+ mse::StreamSocket* takeSocket();
+
+ const PeerID & getPeerID() const {return peer_id;}
+
+ /// See if the authentication is succesfull
+ bool isSuccesfull() const {return succes;}
+
+ const QString & getIP() const {return host;}
+ Uint16 getPort() const {return port;}
+
+ protected slots:
+ void onReadyWrite();
+ void onPeerManagerDestroyed();
+
+ protected:
+ void onFinish(bool succes);
+ void handshakeRecieved(bool full);
+ virtual void connected();
+
+ protected:
+ SHA1Hash info_hash;
+ PeerID our_peer_id,peer_id;
+ QString host;
+ Uint16 port;
+ bool succes;
+ PeerManager* pman;
+ };
+}
+
+#endif
diff --git a/libktorrent/torrent/authenticatebase.cpp b/libktorrent/torrent/authenticatebase.cpp
new file mode 100644
index 0000000..9ee2ad7
--- /dev/null
+++ b/libktorrent/torrent/authenticatebase.cpp
@@ -0,0 +1,159 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <mse/streamsocket.h>
+#include <util/sha1hash.h>
+#include <util/log.h>
+#include <kademlia/dhtbase.h>
+#include "globals.h"
+#include "peerid.h"
+#include "authenticatebase.h"
+
+namespace bt
+{
+
+
+
+ AuthenticateBase::AuthenticateBase(mse::StreamSocket* s) : sock(s),finished(false),local(false)
+ {
+ connect(&timer,SIGNAL(timeout()),this,SLOT(onTimeout()));
+ timer.start(20000,true);
+ memset(handshake,0x00,68);
+ bytes_of_handshake_recieved = 0;
+ ext_support = 0;
+ poll_index = -1;
+ }
+
+
+ AuthenticateBase::~AuthenticateBase()
+ {
+ if (sock)
+ sock->deleteLater();
+ }
+
+ void AuthenticateBase::sendHandshake(const SHA1Hash & info_hash,const PeerID & our_peer_id)
+ {
+ // Out() << "AuthenticateBase::sendHandshake" << endl;
+ if (!sock) return;
+
+ Uint8 hs[68];
+ makeHandshake(hs,info_hash,our_peer_id);
+ sock->sendData(hs,68);
+ }
+
+ void AuthenticateBase::makeHandshake(Uint8* hs,const SHA1Hash & info_hash,const PeerID & our_peer_id)
+ {
+ const char* pstr = "BitTorrent protocol";
+ hs[0] = 19;
+ memcpy(hs+1,pstr,19);
+ memset(hs+20,0x00,8);
+ if (Globals::instance().getDHT().isRunning())
+ hs[27] |= 0x01; // DHT support
+
+ hs[25] |= 0x10; // extension protocol
+ hs[27] |= 0x04; // fast extensions
+ memcpy(hs+28,info_hash.getData(),20);
+ memcpy(hs+48,our_peer_id.data(),20);
+ }
+
+ void AuthenticateBase::onReadyRead()
+ {
+ Uint32 ba = sock->bytesAvailable();
+ // Out() << "AuthenticateBase::onReadyRead " << ba << endl;
+ if (ba == 0)
+ {
+ onFinish(false);
+ return;
+ }
+
+ if (!sock || finished || ba < 48)
+ return;
+
+ // first see if we already have some bytes from the handshake
+ if (bytes_of_handshake_recieved == 0)
+ {
+ if (ba < 68)
+ {
+ // read partial
+ sock->readData(handshake,ba);
+ bytes_of_handshake_recieved += ba;
+ if (ba >= 27 && handshake[27] & 0x01)
+ ext_support |= bt::DHT_SUPPORT;
+ // tell subclasses of a partial handshake
+ handshakeRecieved(false);
+ return;
+ }
+ else
+ {
+ // read full handshake
+ sock->readData(handshake,68);
+ }
+ }
+ else
+ {
+ // read remaining part
+ Uint32 to_read = 68 - bytes_of_handshake_recieved;
+ sock->readData(handshake + bytes_of_handshake_recieved,to_read);
+ }
+
+ if (handshake[0] != 19)
+ {
+ onFinish(false);
+ return;
+ }
+
+ const char* pstr = "BitTorrent protocol";
+ if (memcmp(pstr,handshake+1,19) != 0)
+ {
+ onFinish(false);
+ return;
+ }
+
+ if (Globals::instance().getDHT().isRunning() && (handshake[27] & 0x01))
+ ext_support |= bt::DHT_SUPPORT;
+
+ if (handshake[27] & 0x04)
+ ext_support |= bt::FAST_EXT_SUPPORT;
+
+ if (handshake[25] & 0x10)
+ ext_support |= bt::EXT_PROT_SUPPORT;
+
+ handshakeRecieved(true);
+ }
+
+ void AuthenticateBase::onError(int)
+ {
+ if (finished)
+ return;
+ onFinish(false);
+ }
+
+ void AuthenticateBase::onTimeout()
+ {
+ if (finished)
+ return;
+
+ Out(SYS_CON|LOG_DEBUG) << "Timeout occurred" << endl;
+ onFinish(false);
+ }
+
+ void AuthenticateBase::onReadyWrite()
+ {}
+}
+#include "authenticatebase.moc"
diff --git a/libktorrent/torrent/authenticatebase.h b/libktorrent/torrent/authenticatebase.h
new file mode 100644
index 0000000..fdab158
--- /dev/null
+++ b/libktorrent/torrent/authenticatebase.h
@@ -0,0 +1,125 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef BTAUTHENTICATEBASE_H
+#define BTAUTHENTICATEBASE_H
+
+#include <qobject.h>
+#include <qsocket.h>
+#include <qtimer.h>
+#include <util/constants.h>
+
+
+namespace mse
+{
+ class StreamSocket;
+}
+
+
+namespace bt
+{
+ class SHA1Hash;
+ class PeerID;
+
+ /**
+ * @author Joris Guisson
+ *
+ * Base class for authentication classes. This class just groups
+ * some common stuff between Authenticate and ServerAuthentciate.
+ * It has a socket, handles the timing out, provides a function to send
+ * the handshake.
+ */
+ class AuthenticateBase : public QObject
+ {
+ Q_OBJECT
+ public:
+ AuthenticateBase(mse::StreamSocket* s = 0);
+ virtual ~AuthenticateBase();
+
+ /// Set wether this is a local peer
+ void setLocal(bool loc) {local = loc;}
+
+ /// Is this a local peer
+ bool isLocal() const {return local;}
+
+ /// See if the authentication is finished
+ bool isFinished() const {return finished;}
+
+ /// Flags indicating which extensions are supported
+ Uint32 supportedExtensions() const {return ext_support;}
+
+ /// get teh socket
+ const mse::StreamSocket* getSocket() const {return sock;}
+
+ /// We can read from the socket
+ virtual void onReadyRead();
+
+ /// We can write to the socket (used to detect a succesfull connection)
+ virtual void onReadyWrite();
+
+ int getPollIndex() const {return poll_index;}
+ void setPollIndex(int pi) {poll_index = pi;}
+
+ protected:
+ /**
+ * Send a handshake
+ * @param info_hash The info_hash to include
+ * @param our_peer_id Our PeerID
+ */
+ void sendHandshake(const SHA1Hash & info_hash,const PeerID & our_peer_id);
+
+ /**
+ * Authentication finished.
+ * @param succes Succes or not
+ */
+ virtual void onFinish(bool succes) = 0;
+
+ /**
+ * The other side send a handshake. The first 20 bytes
+ * of the handshake will already have been checked.
+ * @param full Indicates wether we have a full handshake
+ * if this is not full, we should just send our own
+ */
+ virtual void handshakeRecieved(bool full) = 0;
+
+ /**
+ * Fill in the handshake in a buffer.
+ */
+ void makeHandshake(bt::Uint8* buf,const SHA1Hash & info_hash,const PeerID & our_peer_id);
+
+
+
+ protected slots:
+ void onTimeout();
+ void onError(int err);
+
+ protected:
+ mse::StreamSocket* sock;
+ QTimer timer;
+ bool finished;
+ Uint8 handshake[68];
+ Uint32 bytes_of_handshake_recieved;
+ Uint32 ext_support;
+ bool local;
+ int poll_index;
+ };
+
+}
+
+#endif
diff --git a/libktorrent/torrent/authenticationmonitor.cpp b/libktorrent/torrent/authenticationmonitor.cpp
new file mode 100644
index 0000000..08215d0
--- /dev/null
+++ b/libktorrent/torrent/authenticationmonitor.cpp
@@ -0,0 +1,149 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <math.h>
+#include <unistd.h>
+#include <sys/poll.h>
+#include <util/functions.h>
+#include <util/log.h>
+#include <mse/streamsocket.h>
+#include "authenticationmonitor.h"
+#include "authenticatebase.h"
+
+#include <util/profiler.h>
+
+
+namespace bt
+{
+ AuthenticationMonitor AuthenticationMonitor::self;
+
+ AuthenticationMonitor::AuthenticationMonitor()
+ {}
+
+
+ AuthenticationMonitor::~AuthenticationMonitor()
+ {
+
+ }
+
+ void AuthenticationMonitor::clear()
+ {
+ std::list<AuthenticateBase*>::iterator itr = auths.begin();
+ while (itr != auths.end())
+ {
+ AuthenticateBase* ab = *itr;
+ if (ab)
+ ab->deleteLater();
+ itr++;
+ }
+ auths.clear();
+ }
+
+
+ void AuthenticationMonitor::add(AuthenticateBase* s)
+ {
+ auths.push_back(s);
+ }
+
+ void AuthenticationMonitor::remove(AuthenticateBase* s)
+ {
+ auths.remove(s);
+ }
+
+ void AuthenticationMonitor::update()
+ {
+ if (auths.size() == 0)
+ return;
+
+ int i = 0;
+
+ std::list<AuthenticateBase*>::iterator itr = auths.begin();
+ while (itr != auths.end())
+ {
+ AuthenticateBase* ab = *itr;
+ if (!ab || ab->isFinished())
+ {
+ if (ab)
+ ab->deleteLater();
+
+ itr = auths.erase(itr);
+ }
+ else
+ {
+ ab->setPollIndex(-1);
+ if (ab->getSocket() && ab->getSocket()->fd() >= 0)
+ {
+ int fd = ab->getSocket()->fd();
+ if (i >= fd_vec.size())
+ {
+ struct pollfd pfd = {-1,0,0};
+ fd_vec.push_back(pfd);
+ }
+
+ struct pollfd & pfd = fd_vec[i];
+ pfd.fd = fd;
+ pfd.revents = 0;
+ if (!ab->getSocket()->connecting())
+ pfd.events = POLLIN;
+ else
+ pfd.events = POLLOUT;
+ ab->setPollIndex(i);
+ i++;
+ }
+ itr++;
+ }
+ }
+
+ if (poll(&fd_vec[0],i,1) > 0)
+ {
+ handleData();
+ }
+ }
+
+ void AuthenticationMonitor::handleData()
+ {
+ std::list<AuthenticateBase*>::iterator itr = auths.begin();
+ while (itr != auths.end())
+ {
+ AuthenticateBase* ab = *itr;
+ if (ab && ab->getSocket() && ab->getSocket()->fd() >= 0 && ab->getPollIndex() >= 0)
+ {
+ int pi = ab->getPollIndex();
+ if (fd_vec[pi].revents & POLLIN)
+ {
+ ab->onReadyRead();
+ }
+ else if (fd_vec[pi].revents & POLLOUT)
+ {
+ ab->onReadyWrite();
+ }
+ }
+
+ if (!ab || ab->isFinished())
+ {
+ if (ab)
+ ab->deleteLater();
+ itr = auths.erase(itr);
+ }
+ else
+ itr++;
+ }
+ }
+
+}
diff --git a/libktorrent/torrent/authenticationmonitor.h b/libktorrent/torrent/authenticationmonitor.h
new file mode 100644
index 0000000..43a4ebb
--- /dev/null
+++ b/libktorrent/torrent/authenticationmonitor.h
@@ -0,0 +1,80 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef BTAUTHENTICATIONMONITOR_H
+#define BTAUTHENTICATIONMONITOR_H
+
+#include <list>
+#include <vector>
+
+struct pollfd;
+
+namespace bt
+{
+ class AuthenticateBase;
+
+ /**
+ @author Joris Guisson <joris.guisson@gmail.com>
+
+ Monitors ongoing authentication attempts. This class is a singleton.
+ */
+ class AuthenticationMonitor
+ {
+ std::list<AuthenticateBase*> auths;
+ std::vector<struct pollfd> fd_vec;
+
+ static AuthenticationMonitor self;
+
+ AuthenticationMonitor();
+ public:
+
+ virtual ~AuthenticationMonitor();
+
+
+ /**
+ * Add a new AuthenticateBase object.
+ * @param s
+ */
+ void add(AuthenticateBase* s);
+
+ /**
+ * Remove an AuthenticateBase object
+ * @param s
+ */
+ void remove(AuthenticateBase* s);
+
+ /**
+ * Check all AuthenticateBase objects.
+ */
+ void update();
+
+ /**
+ * Clear all AuthenticateBase objects, also delets them
+ */
+ void clear();
+
+ static AuthenticationMonitor & instance() {return self;}
+
+ private:
+ void handleData();
+ };
+
+}
+
+#endif
diff --git a/libktorrent/torrent/bdecoder.cpp b/libktorrent/torrent/bdecoder.cpp
new file mode 100644
index 0000000..6c5a179
--- /dev/null
+++ b/libktorrent/torrent/bdecoder.cpp
@@ -0,0 +1,224 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <util/log.h>
+#include <util/error.h>
+#include <klocale.h>
+#include "bdecoder.h"
+#include "bnode.h"
+#include "globals.h"
+
+namespace bt
+{
+
+ BDecoder::BDecoder(const QByteArray & data,bool verbose,Uint32 off)
+ : data(data),pos(off),verbose(verbose)
+ {
+ }
+
+
+ BDecoder::~BDecoder()
+ {}
+
+ BNode* BDecoder::decode()
+ {
+ if (pos >= data.size())
+ return 0;
+
+ if (data[pos] == 'd')
+ {
+ return parseDict();
+ }
+ else if (data[pos] == 'l')
+ {
+ return parseList();
+ }
+ else if (data[pos] == 'i')
+ {
+ return parseInt();
+ }
+ else if (data[pos] >= '0' && data[pos] <= '9')
+ {
+ return parseString();
+ }
+ else
+ {
+ throw Error(i18n("Illegal token: %1").arg(data[pos]));
+ }
+ }
+
+ BDictNode* BDecoder::parseDict()
+ {
+ Uint32 off = pos;
+ // we're now entering a dictionary
+ BDictNode* curr = new BDictNode(off);
+ pos++;
+ if (verbose) Out() << "DICT" << endl;
+ try
+ {
+ while (pos < data.size() && data[pos] != 'e')
+ {
+ if (verbose) Out() << "Key : " << endl;
+ BNode* kn = decode();
+ BValueNode* k = dynamic_cast<BValueNode*>(kn);
+ if (!k || k->data().getType() != Value::STRING)
+ {
+ delete kn;
+ throw Error(i18n("Decode error"));
+ }
+
+ QByteArray key = k->data().toByteArray();
+ delete kn;
+
+ BNode* data = decode();
+ curr->insert(key,data);
+ }
+ pos++;
+ }
+ catch (...)
+ {
+ delete curr;
+ throw;
+ }
+ if (verbose) Out() << "END" << endl;
+ curr->setLength(pos - off);
+ return curr;
+ }
+
+ BListNode* BDecoder::parseList()
+ {
+ Uint32 off = pos;
+ if (verbose) Out() << "LIST" << endl;
+ BListNode* curr = new BListNode(off);
+ pos++;
+ try
+ {
+ while (pos < data.size() && data[pos] != 'e')
+ {
+ BNode* n = decode();
+ curr->append(n);
+ }
+ pos++;
+ }
+ catch (...)
+ {
+ delete curr;
+ throw;
+ }
+ if (verbose) Out() << "END" << endl;
+ curr->setLength(pos - off);
+ return curr;
+ }
+
+ BValueNode* BDecoder::parseInt()
+ {
+ Uint32 off = pos;
+ pos++;
+ QString n;
+ // look for e and add everything between i and e to n
+ while (pos < data.size() && data[pos] != 'e')
+ {
+ n += data[pos];
+ pos++;
+ }
+
+ // check if we aren't at the end of the data
+ if (pos >= data.size())
+ {
+ throw Error(i18n("Unexpected end of input"));
+ }
+
+ // try to decode the int
+ bool ok = true;
+ int val = 0;
+ val = n.toInt(&ok);
+ if (ok)
+ {
+ pos++;
+ if (verbose) Out() << "INT = " << val << endl;
+ BValueNode* vn = new BValueNode(Value(val),off);
+ vn->setLength(pos - off);
+ return vn;
+ }
+ else
+ {
+ Int64 bi = 0LL;
+ bi = n.toLongLong(&ok);
+ if (!ok)
+ throw Error(i18n("Cannot convert %1 to an int").arg(n));
+
+ pos++;
+ if (verbose) Out() << "INT64 = " << n << endl;
+ BValueNode* vn = new BValueNode(Value(bi),off);
+ vn->setLength(pos - off);
+ return vn;
+ }
+ }
+
+ BValueNode* BDecoder::parseString()
+ {
+ Uint32 off = pos;
+ // string are encoded 4:spam (length:string)
+
+ // first get length by looking for the :
+ QString n;
+ while (pos < data.size() && data[pos] != ':')
+ {
+ n += data[pos];
+ pos++;
+ }
+ // check if we aren't at the end of the data
+ if (pos >= data.size())
+ {
+ throw Error(i18n("Unexpected end of input"));
+ }
+
+ // try to decode length
+ bool ok = true;
+ int len = 0;
+ len = n.toInt(&ok);
+ if (!ok)
+ {
+ throw Error(i18n("Cannot convert %1 to an int").arg(n));
+ }
+ // move pos to the first part of the string
+ pos++;
+ if (pos + len > data.size())
+ throw Error(i18n("Torrent is incomplete!"));
+
+ QByteArray arr(len);
+ for (unsigned int i = pos;i < pos + len;i++)
+ arr.at(i-pos) = data[i];
+ pos += len;
+ // read the string into n
+
+ // pos should be positioned right after the string
+ BValueNode* vn = new BValueNode(Value(arr),off);
+ vn->setLength(pos - off);
+ if (verbose)
+ {
+ if (arr.size() < 200)
+ Out() << "STRING " << QString(arr) << endl;
+ else
+ Out() << "STRING " << "really long string" << endl;
+ }
+ return vn;
+ }
+}
+
diff --git a/libktorrent/torrent/bdecoder.h b/libktorrent/torrent/bdecoder.h
new file mode 100644
index 0000000..dfffce5
--- /dev/null
+++ b/libktorrent/torrent/bdecoder.h
@@ -0,0 +1,70 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef BTBDECODER_H
+#define BTBDECODER_H
+
+#include <qstring.h>
+#include <util/constants.h>
+
+namespace bt
+{
+
+ class BNode;
+ class BListNode;
+ class BDictNode;
+ class BValueNode;
+
+ /**
+ * @author Joris Guisson
+ * @brief Decodes b-encoded data
+ *
+ * Class to decode b-encoded data.
+ */
+ class BDecoder
+ {
+ const QByteArray & data;
+ Uint32 pos;
+ bool verbose;
+ public:
+ /**
+ * Constructor, passes in the data to decode.
+ * @param data The data
+ * @param verbose Verbose output to the log
+ * @param off Offset to start parsing
+ */
+ BDecoder(const QByteArray & data,bool verbose,Uint32 off = 0);
+ virtual ~BDecoder();
+
+ /**
+ * Decode the data, the root node gets
+ * returned. (Note that the caller must delete this node)
+ * @return The root node
+ */
+ BNode* decode();
+ private:
+ BDictNode* parseDict();
+ BListNode* parseList();
+ BValueNode* parseInt();
+ BValueNode* parseString();
+ };
+
+}
+
+#endif
diff --git a/libktorrent/torrent/bencoder.cpp b/libktorrent/torrent/bencoder.cpp
new file mode 100644
index 0000000..e4a80a0
--- /dev/null
+++ b/libktorrent/torrent/bencoder.cpp
@@ -0,0 +1,137 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include "bencoder.h"
+#include <util/file.h>
+
+namespace bt
+{
+
+
+ BEncoderFileOutput::BEncoderFileOutput(File* fptr) : fptr(fptr)
+ {
+ }
+
+ void BEncoderFileOutput::write(const char* str,Uint32 len)
+ {
+ if (!fptr)
+ return;
+
+ fptr->write(str,len);
+ }
+
+ ////////////////////////////////////
+
+ BEncoderBufferOutput::BEncoderBufferOutput(QByteArray & data) : data(data),ptr(0)
+ {
+ }
+
+ void BEncoderBufferOutput::write(const char* str,Uint32 len)
+ {
+ if (ptr + len > data.size())
+ data.resize(ptr + len);
+
+ for (Uint32 i = 0;i < len;i++)
+ data[ptr++] = str[i];
+ }
+
+ ////////////////////////////////////
+
+ BEncoder::BEncoder(File* fptr) : out(0),del(true)
+ {
+ out = new BEncoderFileOutput(fptr);
+ }
+
+ BEncoder::BEncoder(BEncoderOutput* out) : out(out),del(true)
+ {
+ }
+
+
+ BEncoder::~BEncoder()
+ {
+ if (del)
+ delete out;
+ }
+
+ void BEncoder::beginDict()
+ {
+ if (!out) return;
+
+ out->write("d",1);
+ }
+
+ void BEncoder::beginList()
+ {
+ if (!out) return;
+
+ out->write("l",1);
+ }
+
+ void BEncoder::write(Uint32 val)
+ {
+ if (!out) return;
+
+ QCString s = QString("i%1e").arg(val).utf8();
+ out->write(s,s.length());
+ }
+
+ void BEncoder::write(Uint64 val)
+ {
+ if (!out) return;
+
+ QCString s = QString("i%1e").arg(val).utf8();
+ out->write(s,s.length());
+ }
+
+ void BEncoder::write(const QString & str)
+ {
+ if (!out) return;
+
+ QCString u = str.utf8();
+ QCString s = QString("%1:").arg(u.length()).utf8();
+ out->write(s,s.length());
+ out->write(u,u.length());
+ }
+
+ void BEncoder::write(const QByteArray & data)
+ {
+ if (!out) return;
+
+ QCString s = QString::number(data.size()).utf8();
+ out->write(s,s.length());
+ out->write(":",1);
+ out->write(data.data(),data.size());
+ }
+
+ void BEncoder::write(const Uint8* data,Uint32 size)
+ {
+ if (!out) return;
+
+ QCString s = QString("%1:").arg(size).utf8();
+ out->write(s,s.length());
+ out->write((const char*)data,size);
+ }
+
+ void BEncoder::end()
+ {
+ if (!out) return;
+
+ out->write("e",1);
+ }
+}
diff --git a/libktorrent/torrent/bencoder.h b/libktorrent/torrent/bencoder.h
new file mode 100644
index 0000000..8760d14
--- /dev/null
+++ b/libktorrent/torrent/bencoder.h
@@ -0,0 +1,150 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef BTBENCODER_H
+#define BTBENCODER_H
+
+
+#include <util/file.h>
+
+
+namespace bt
+{
+ class File;
+
+ /**
+ * @author Joris Guisson
+ *
+ * Interface for classes which wish to receive the output from a BEncoder.
+ */
+ class BEncoderOutput
+ {
+ public:
+ virtual ~BEncoderOutput() {}
+ /**
+ * Write a string of characters.
+ * @param str The string
+ * @param len The length of the string
+ */
+ virtual void write(const char* str,Uint32 len) = 0;
+ };
+
+ /**
+ * Writes the output of a bencoder to a file
+ */
+ class BEncoderFileOutput : public BEncoderOutput
+ {
+ File* fptr;
+ public:
+ BEncoderFileOutput(File* fptr);
+
+ void write(const char* str,Uint32 len);
+ };
+
+ /**
+ * Write the output of a BEncoder to a QByteArray
+ */
+ class BEncoderBufferOutput : public BEncoderOutput
+ {
+ QByteArray & data;
+ Uint32 ptr;
+ public:
+ BEncoderBufferOutput(QByteArray & data);
+
+ void write(const char* str,Uint32 len);
+ };
+
+
+ /**
+ * @author Joris Guisson
+ * @brief Helper class to b-encode stuff.
+ *
+ * This class b-encodes data. For more details about b-encoding, see
+ * the BitTorrent protocol docs. The data gets written to a BEncoderOutput
+ * thing.
+ */
+ class BEncoder
+ {
+ BEncoderOutput* out;
+ bool del;
+ public:
+ /**
+ * Constructor, output gets written to a file.
+ * @param fptr The File to write to
+ */
+ BEncoder(File* fptr);
+
+
+ /**
+ * Constructor, output gets written to a BEncoderOutput object.
+ * @param out The BEncoderOutput
+ */
+ BEncoder(BEncoderOutput* out);
+ virtual ~BEncoder();
+
+ /**
+ * Begin a dictionary.Should have a corresponding end call.
+ */
+ void beginDict();
+
+ /**
+ * Begin a list. Should have a corresponding end call.
+ */
+ void beginList();
+
+ /**
+ * Write an int
+ * @param val
+ */
+ void write(Uint32 val);
+
+ /**
+ * Write an int64
+ * @param val
+ */
+ void write(Uint64 val);
+
+ /**
+ * Write a string
+ * @param str
+ */
+ void write(const QString & str);
+
+ /**
+ * Write a QByteArray
+ * @param data
+ */
+ void write(const QByteArray & data);
+
+ /**
+ * Write a data array
+ * @param data
+ * @param size of data
+ */
+ void write(const Uint8* data,Uint32 size);
+
+ /**
+ * End a beginDict or beginList call.
+ */
+ void end();
+ };
+
+}
+
+#endif
diff --git a/libktorrent/torrent/bnode.cpp b/libktorrent/torrent/bnode.cpp
new file mode 100644
index 0000000..e76dcf3
--- /dev/null
+++ b/libktorrent/torrent/bnode.cpp
@@ -0,0 +1,177 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <util/log.h>
+#include "bnode.h"
+#include "globals.h"
+
+namespace bt
+{
+
+ BNode::BNode(Type type,Uint32 off) : type(type),off(off),len(0)
+ {
+ }
+
+
+ BNode::~BNode()
+ {}
+
+ ////////////////////////////////////////////////
+
+ BValueNode::BValueNode(const Value & v,Uint32 off) : BNode(VALUE,off),v(v)
+ {}
+
+ BValueNode::~BValueNode()
+ {}
+
+ void BValueNode::printDebugInfo()
+ {
+ if (v.getType() == Value::INT)
+ Out() << "Value = " << v.toInt() << endl;
+ else
+ Out() << "Value = " << v.toString() << endl;
+ }
+
+ ////////////////////////////////////////////////
+
+ BDictNode::BDictNode(Uint32 off) : BNode(DICT,off)
+ {
+ }
+
+ BDictNode::~BDictNode()
+ {
+ QValueList<DictEntry>::iterator i = children.begin();
+ while (i != children.end())
+ {
+ DictEntry & e = *i;
+ delete e.node;
+ i++;
+ }
+ }
+
+ void BDictNode::insert(const QByteArray & key,BNode* node)
+ {
+ DictEntry entry;
+ entry.key = key;
+ entry.node = node;
+ children.append(entry);
+ }
+
+ BNode* BDictNode::getData(const QString & key)
+ {
+ QValueList<DictEntry>::iterator i = children.begin();
+ while (i != children.end())
+ {
+ DictEntry & e = *i;
+ if (QString(e.key) == key)
+ return e.node;
+ i++;
+ }
+ return 0;
+ }
+
+ BDictNode* BDictNode::getDict(const QByteArray & key)
+ {
+ QValueList<DictEntry>::iterator i = children.begin();
+ while (i != children.end())
+ {
+ DictEntry & e = *i;
+ if (e.key == key)
+ return dynamic_cast<BDictNode*>(e.node);
+ i++;
+ }
+ return 0;
+ }
+
+ BListNode* BDictNode::getList(const QString & key)
+ {
+ BNode* n = getData(key);
+ return dynamic_cast<BListNode*>(n);
+ }
+
+ BDictNode* BDictNode::getDict(const QString & key)
+ {
+ BNode* n = getData(key);
+ return dynamic_cast<BDictNode*>(n);
+ }
+
+ BValueNode* BDictNode::getValue(const QString & key)
+ {
+ BNode* n = getData(key);
+ return dynamic_cast<BValueNode*>(n);
+ }
+
+ void BDictNode::printDebugInfo()
+ {
+ Out() << "DICT" << endl;
+ QValueList<DictEntry>::iterator i = children.begin();
+ while (i != children.end())
+ {
+ DictEntry & e = *i;
+ Out() << QString(e.key) << ": " << endl;
+ e.node->printDebugInfo();
+ i++;
+ }
+ Out() << "END" << endl;
+ }
+
+ ////////////////////////////////////////////////
+
+ BListNode::BListNode(Uint32 off) : BNode(LIST,off)
+ {
+ children.setAutoDelete(true);
+ }
+
+
+ BListNode::~BListNode()
+ {}
+
+
+ void BListNode::append(BNode* node)
+ {
+ children.append(node);
+ }
+
+ BListNode* BListNode::getList(Uint32 idx)
+ {
+ return dynamic_cast<BListNode*>(getChild(idx));
+ }
+
+ BDictNode* BListNode::getDict(Uint32 idx)
+ {
+ return dynamic_cast<BDictNode*>(getChild(idx));
+ }
+
+ BValueNode* BListNode::getValue(Uint32 idx)
+ {
+ return dynamic_cast<BValueNode*>(getChild(idx));
+ }
+
+ void BListNode::printDebugInfo()
+ {
+ Out() << "LIST " << children.count() << endl;
+ for (Uint32 i = 0;i < children.count();i++)
+ {
+ BNode* n = children.at(i);
+ n->printDebugInfo();
+ }
+ Out() << "END" << endl;
+ }
+}
+
diff --git a/libktorrent/torrent/bnode.h b/libktorrent/torrent/bnode.h
new file mode 100644
index 0000000..685291c
--- /dev/null
+++ b/libktorrent/torrent/bnode.h
@@ -0,0 +1,210 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef BTBNODE_H
+#define BTBNODE_H
+
+#include <qptrlist.h>
+#include <qvaluelist.h>
+#include <util/constants.h>
+#include "value.h"
+
+
+namespace bt
+{
+ class BListNode;
+
+ /**
+ * @author Joris Guisson
+ * @brief Base class for a node in a b-encoded piece of data
+ *
+ * There are 3 possible pieces of data in b-encoded piece of data.
+ * This is the base class for all those 3 things.
+ */
+ class BNode
+ {
+ public:
+ enum Type
+ {
+ VALUE,DICT,LIST
+ };
+
+ /**
+ * Constructor, sets the Type, and the offset into
+ * the data.
+ * @param type Type of node
+ * @param off The offset into the data
+ */
+ BNode(Type type,Uint32 off);
+ virtual ~BNode();
+
+ /// Get the type of node
+ Type getType() const {return type;}
+
+ /// Get the offset in the bytearray where this node starts.
+ Uint32 getOffset() const {return off;}
+
+ /// Get the length this node takes up in the bytearray.
+ Uint32 getLength() const {return len;}
+
+ /// Set the length
+ void setLength(Uint32 l) {len = l;}
+
+ /// Print some debugging info
+ virtual void printDebugInfo() = 0;
+ private:
+ Type type;
+ Uint32 off,len;
+ };
+
+ /**
+ * @author Joris Guisson
+ * @brief Represents a value (string,bytearray or int) in bencoded data
+ *
+ * @todo Use QVariant
+ */
+ class BValueNode : public BNode
+ {
+ Value v;
+ public:
+ BValueNode(const Value & v,Uint32 off);
+ virtual ~BValueNode();
+
+ const Value & data() const {return v;}
+ void printDebugInfo();
+ };
+
+ /**
+ * @author Joris Guisson
+ * @brief Represents a dictionary in bencoded data
+ *
+ */
+ class BDictNode : public BNode
+ {
+ struct DictEntry
+ {
+ QByteArray key;
+ BNode* node;
+ };
+ QValueList<DictEntry> children;
+ public:
+ BDictNode(Uint32 off);
+ virtual ~BDictNode();
+
+ /**
+ * Insert a BNode in the dictionary.
+ * @param key The key
+ * @param node The node
+ */
+ void insert(const QByteArray & key,BNode* node);
+
+ /**
+ * Get a BNode.
+ * @param key The key
+ * @return The node or 0 if there is no node with has key @a key
+ */
+ BNode* getData(const QString & key);
+
+ /**
+ * Get a BListNode.
+ * @param key The key
+ * @return The node or 0 if there is no list node with has key @a key
+ */
+ BListNode* getList(const QString & key);
+
+ /**
+ * Get a BDictNode.
+ * @param key The key
+ * @return The node or 0 if there is no dict node with has key @a key
+ */
+ BDictNode* getDict(const QString & key);
+
+ /**
+ * Get a BDictNode.
+ * @param key The key
+ * @return The node or 0 if there is no dict node with has key @a key
+ */
+ BDictNode* getDict(const QByteArray & key);
+
+ /**
+ * Get a BValueNode.
+ * @param key The key
+ * @return The node or 0 if there is no value node with has key @a key
+ */
+ BValueNode* getValue(const QString & key);
+
+ void printDebugInfo();
+ };
+
+ /**
+ * @author Joris Guisson
+ * @brief Represents a list in bencoded data
+ *
+ */
+ class BListNode : public BNode
+ {
+ QPtrList<BNode> children;
+ public:
+ BListNode(Uint32 off);
+ virtual ~BListNode();
+
+ /**
+ * Append a node to the list.
+ * @param node The node
+ */
+ void append(BNode* node);
+ void printDebugInfo();
+
+ /// Get the number of nodes in the list.
+ Uint32 getNumChildren() const {return children.count();}
+
+ /**
+ * Get a node from the list
+ * @param idx The index
+ * @return The node or 0 if idx is out of bounds
+ */
+ BNode* getChild(Uint32 idx) {return children.at(idx);}
+
+ /**
+ * Get a BListNode.
+ * @param idx The index
+ * @return The node or 0 if the index is out of bounds or the element
+ * at postion @a idx isn't a BListNode.
+ */
+ BListNode* getList(Uint32 idx);
+
+ /**
+ * Get a BDictNode.
+ * @param idx The index
+ * @return The node or 0 if the index is out of bounds or the element
+ * at postion @a idx isn't a BDictNode.
+ */
+ BDictNode* getDict(Uint32 idx);
+
+ /**
+ * Get a BValueNode.
+ * @param idx The index
+ * @return The node or 0 if the index is out of bounds or the element
+ * at postion @a idx isn't a BValueNode.
+ */
+ BValueNode* getValue(Uint32 idx);
+ };
+}
+
+#endif
diff --git a/libktorrent/torrent/cache.cpp b/libktorrent/torrent/cache.cpp
new file mode 100644
index 0000000..dcf9a77
--- /dev/null
+++ b/libktorrent/torrent/cache.cpp
@@ -0,0 +1,55 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include "torrent.h"
+#include "chunk.h"
+#include "cache.h"
+#include "peermanager.h"
+#include <util/functions.h>
+
+namespace bt
+{
+
+ Cache::Cache(Torrent & tor,const QString & tmpdir,const QString & datadir)
+ : tor(tor),tmpdir(tmpdir),datadir(datadir),mmap_failures(0)
+ {
+ if (!datadir.endsWith(bt::DirSeparator()))
+ this->datadir += bt::DirSeparator();
+
+ if (!tmpdir.endsWith(bt::DirSeparator()))
+ this->tmpdir += bt::DirSeparator();
+
+ preexisting_files = false;
+ }
+
+
+ Cache::~Cache()
+ {}
+
+
+ void Cache::changeTmpDir(const QString & ndir)
+ {
+ tmpdir = ndir;
+ }
+
+ bool Cache::mappedModeAllowed()
+ {
+ return MaxOpenFiles() - bt::PeerManager::getTotalConnections() < 100;
+ }
+}
diff --git a/libktorrent/torrent/cache.h b/libktorrent/torrent/cache.h
new file mode 100644
index 0000000..4c373ee
--- /dev/null
+++ b/libktorrent/torrent/cache.h
@@ -0,0 +1,165 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef BTCACHE_H
+#define BTCACHE_H
+
+#include <kio/job.h>
+
+class QStringList;
+
+namespace bt
+{
+ class Torrent;
+ class TorrentFile;
+ class Chunk;
+ class PreallocationThread;
+
+
+ /**
+ * @author Joris Guisson
+ * @brief Manages the temporary data
+ *
+ * Interface for a class which manages downloaded data.
+ * Subclasses should implement the load and save methods.
+ */
+ class Cache
+ {
+ protected:
+ Torrent & tor;
+ QString tmpdir;
+ QString datadir;
+ bool preexisting_files;
+ Uint32 mmap_failures;
+ public:
+ Cache(Torrent & tor,const QString & tmpdir,const QString & datadir);
+ virtual ~Cache();
+
+ /// Get the datadir
+ QString getDataDir() const {return datadir;}
+
+ /**
+ * Get the actual output path.
+ * @return The output path
+ */
+ virtual QString getOutputPath() const = 0;
+
+ /**
+ * Changes the tmp dir. All data files should already been moved.
+ * This just modifies the tmpdir variable.
+ * @param ndir The new tmpdir
+ */
+ virtual void changeTmpDir(const QString & ndir);
+
+ /**
+ * Move the data files to a new directory.
+ * @param ndir The directory
+ * @return The KIO::Job doing the move
+ */
+ virtual KIO::Job* moveDataFiles(const QString & ndir) = 0;
+
+ /**
+ * The move data files job is done.
+ * @param job The job that did it
+ */
+ virtual void moveDataFilesCompleted(KIO::Job* job) = 0;
+
+ /**
+ * Changes output path. All data files should already been moved.
+ * This just modifies the datadir variable.
+ * @param outputpath New output path
+ */
+ virtual void changeOutputPath(const QString & outputpath) = 0;
+
+ /**
+ * Load a chunk into memory. If something goes wrong,
+ * an Error should be thrown.
+ * @param c The Chunk
+ */
+ virtual void load(Chunk* c) = 0;
+
+ /**
+ * Save a chunk to disk. If something goes wrong,
+ * an Error should be thrown.
+ * @param c The Chunk
+ */
+ virtual void save(Chunk* c) = 0;
+
+ /**
+ * Prepare a chunk for downloading.
+ * @param c The Chunk
+ * @return true if ok, false otherwise
+ */
+ virtual bool prep(Chunk* c) = 0;
+
+ /**
+ * Create all the data files to store the data.
+ */
+ virtual void create() = 0;
+
+ /**
+ * Close the cache file(s).
+ */
+ virtual void close() = 0;
+
+ /**
+ * Open the cache file(s)
+ */
+ virtual void open() = 0;
+
+ /// Does nothing, can be overridden to be alerted of download status changes of a TorrentFile
+ virtual void downloadStatusChanged(TorrentFile*, bool) {};
+
+ /**
+ * Preallocate diskspace for all files
+ * @param prealloc The thread doing the preallocation
+ */
+ virtual void preallocateDiskSpace(PreallocationThread* prealloc) = 0;
+
+ /// See if the download has existing files
+ bool hasExistingFiles() const {return preexisting_files;}
+
+
+ /**
+ * Test all files and see if they are not missing.
+ * If so put them in a list
+ */
+ virtual bool hasMissingFiles(QStringList & sl) = 0;
+
+ /**
+ * Delete all data files, in case of multi file torrents
+ * empty directories should also be deleted.
+ */
+ virtual void deleteDataFiles() = 0;
+
+ /**
+ * See if we are allowed to use mmap, when loading chunks.
+ * This will return false if we are close to system limits.
+ */
+ static bool mappedModeAllowed();
+
+ /**
+ * Get the number of bytes all the files of this torrent are currently using on disk.
+ * */
+ virtual Uint64 diskUsage() = 0;
+ };
+
+}
+
+#endif
diff --git a/libktorrent/torrent/cachefile.cpp b/libktorrent/torrent/cachefile.cpp
new file mode 100644
index 0000000..6367b7f
--- /dev/null
+++ b/libktorrent/torrent/cachefile.cpp
@@ -0,0 +1,507 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#include <errno.h>
+#include <qfile.h>
+#include <kio/netaccess.h>
+#include <klocale.h>
+#include <kfileitem.h>
+#include <util/array.h>
+#include <util/fileops.h>
+#include <torrent/globals.h>
+#include <interfaces/functions.h>
+#include <kapplication.h>
+#include <util/log.h>
+#include <util/error.h>
+#include "cachefile.h"
+#include "preallocationthread.h"
+#include "settings.h"
+
+
+// Not all systems have an O_LARGEFILE - Solaris depending
+// on command-line defines, FreeBSD never - so in those cases,
+// make it a zero bitmask. As long as it's only OR'ed into
+// open(2) flags, that's fine.
+//
+#ifndef O_LARGEFILE
+#define O_LARGEFILE (0)
+#endif
+
+
+
+
+namespace bt
+{
+
+ CacheFile::CacheFile() : fd(-1),max_size(0),file_size(0),mutex(true)
+ {
+ read_only = false;
+ }
+
+
+ CacheFile::~CacheFile()
+ {
+ if (fd != -1)
+ close();
+ }
+
+ void CacheFile::changePath(const QString & npath)
+ {
+ path = npath;
+ }
+
+ void CacheFile::openFile(Mode mode)
+ {
+ int flags = O_LARGEFILE;
+
+ // by default allways try read write
+ fd = ::open(QFile::encodeName(path),flags | O_RDWR);
+ if (fd < 0 && mode == READ)
+ {
+ // in case RDWR fails, try readonly if possible
+ fd = ::open(QFile::encodeName(path),flags | O_RDONLY);
+ if (fd >= 0)
+ read_only = true;
+ }
+
+ if (fd < 0)
+ {
+ throw Error(i18n("Cannot open %1 : %2").arg(path).arg(strerror(errno)));
+ }
+
+ file_size = FileSize(fd);
+ }
+
+ void CacheFile::open(const QString & path,Uint64 size)
+ {
+ QMutexLocker lock(&mutex);
+ // only set the path and the max size, we only open the file when it is needed
+ this->path = path;
+ max_size = size;
+ }
+
+ void* CacheFile::map(MMappeable* thing,Uint64 off,Uint32 size,Mode mode)
+ {
+ QMutexLocker lock(&mutex);
+ // reopen the file if necessary
+ if (fd == -1)
+ {
+ // Out() << "Reopening " << path << endl;
+ openFile(mode);
+ }
+
+ if (read_only && mode != READ)
+ {
+ throw Error(i18n("Cannot open %1 for writing : readonly filesystem").arg(path));
+ }
+
+ if (off + size > max_size)
+ {
+ Out() << "Warning : writing past the end of " << path << endl;
+ Out() << (off + size) << " " << max_size << endl;
+ return 0;
+ }
+
+ int mmap_flag = 0;
+ switch (mode)
+ {
+ case READ:
+ mmap_flag = PROT_READ;
+ break;
+ case WRITE:
+ mmap_flag = PROT_WRITE;
+ break;
+ case RW:
+ mmap_flag = PROT_READ|PROT_WRITE;
+ break;
+ }
+
+ if (off + size > file_size)
+ {
+ Uint64 to_write = (off + size) - file_size;
+ // Out() << "Growing file with " << to_write << " bytes" << endl;
+ growFile(to_write);
+ }
+
+ Uint32 page_size = sysconf(_SC_PAGESIZE);
+ if (off % page_size > 0)
+ {
+ // off is not a multiple of the page_size
+ // so we play around a bit
+ Uint32 diff = (off % page_size);
+ Uint64 noff = off - diff;
+ // Out() << "Offsetted mmap : " << diff << endl;
+#if HAVE_MMAP64
+ char* ptr = (char*)mmap64(0, size + diff, mmap_flag, MAP_SHARED, fd, noff);
+#else
+ char* ptr = (char*)mmap(0, size + diff, mmap_flag, MAP_SHARED, fd, noff);
+#endif
+ if (ptr == MAP_FAILED)
+ {
+ Out() << "mmap failed : " << QString(strerror(errno)) << endl;
+ return 0;
+ }
+ else
+ {
+ CacheFile::Entry e;
+ e.thing = thing;
+ e.offset = off;
+ e.diff = diff;
+ e.ptr = ptr;
+ e.size = size + diff;
+ e.mode = mode;
+ mappings.insert((void*)(ptr + diff),e);
+ return ptr + diff;
+ }
+ }
+ else
+ {
+#if HAVE_MMAP64
+ void* ptr = mmap64(0, size, mmap_flag, MAP_SHARED, fd, off);
+#else
+ void* ptr = mmap(0, size, mmap_flag, MAP_SHARED, fd, off);
+#endif
+ if (ptr == MAP_FAILED)
+ {
+ Out() << "mmap failed : " << QString(strerror(errno)) << endl;
+ return 0;
+ }
+ else
+ {
+ CacheFile::Entry e;
+ e.thing = thing;
+ e.offset = off;
+ e.ptr = ptr;
+ e.diff = 0;
+ e.size = size;
+ e.mode = mode;
+ mappings.insert(ptr,e);
+ return ptr;
+ }
+ }
+ }
+
+ void CacheFile::growFile(Uint64 to_write)
+ {
+ // reopen the file if necessary
+ if (fd == -1)
+ {
+ // Out() << "Reopening " << path << endl;
+ openFile(RW);
+ }
+
+ if (read_only)
+ throw Error(i18n("Cannot open %1 for writing : readonly filesystem").arg(path));
+
+ // jump to the end of the file
+ SeekFile(fd,0,SEEK_END);
+
+ if (file_size + to_write > max_size)
+ {
+ Out() << "Warning : writing past the end of " << path << endl;
+ Out() << (file_size + to_write) << " " << max_size << endl;
+ }
+
+ Uint8 buf[1024];
+ memset(buf,0,1024);
+ Uint64 num = to_write;
+ // write data until to_write is 0
+ while (to_write > 0)
+ {
+ int nb = to_write > 1024 ? 1024 : to_write;
+ int ret = ::write(fd,buf,nb);
+ if (ret < 0)
+ throw Error(i18n("Cannot expand file %1 : %2").arg(path).arg(strerror(errno)));
+ else if (ret != nb)
+ throw Error(i18n("Cannot expand file %1 : incomplete write").arg(path));
+ to_write -= nb;
+ }
+ file_size += num;
+//
+ // Out() << QString("growing %1 = %2").arg(path).arg(kt::BytesToString(file_size)) << endl;
+
+ if (file_size != FileSize(fd))
+ {
+// Out() << QString("Homer Simpson %1 %2").arg(file_size).arg(sb.st_size) << endl;
+ fsync(fd);
+ if (file_size != FileSize(fd))
+ {
+ throw Error(i18n("Cannot expand file %1").arg(path));
+ }
+ }
+ }
+
+ void CacheFile::unmap(void* ptr,Uint32 size)
+ {
+ int ret = 0;
+ QMutexLocker lock(&mutex);
+ // see if it wasn't an offsetted mapping
+ if (mappings.contains(ptr))
+ {
+ CacheFile::Entry & e = mappings[ptr];
+#if HAVE_MUNMAP64
+ if (e.diff > 0)
+ ret = munmap64((char*)ptr - e.diff,e.size);
+ else
+ ret = munmap64(ptr,e.size);
+#else
+ if (e.diff > 0)
+ ret = munmap((char*)ptr - e.diff,e.size);
+ else
+ ret = munmap(ptr,e.size);
+#endif
+ mappings.erase(ptr);
+ // no mappings, close temporary
+ if (mappings.count() == 0)
+ closeTemporary();
+ }
+ else
+ {
+#if HAVE_MUNMAP64
+ ret = munmap64(ptr,size);
+#else
+ ret = munmap(ptr,size);
+#endif
+ }
+
+ if (ret < 0)
+ {
+ Out(SYS_DIO|LOG_IMPORTANT) << QString("Munmap failed with error %1 : %2").arg(errno).arg(strerror(errno)) << endl;
+ }
+ }
+
+ void CacheFile::close()
+ {
+ QMutexLocker lock(&mutex);
+
+ if (fd == -1)
+ return;
+
+ QMap<void*,Entry>::iterator i = mappings.begin();
+ while (i != mappings.end())
+ {
+ int ret = 0;
+ CacheFile::Entry & e = i.data();
+#if HAVE_MUNMAP64
+ if (e.diff > 0)
+ ret = munmap64((char*)e.ptr - e.diff,e.size);
+ else
+ ret = munmap64(e.ptr,e.size);
+#else
+ if (e.diff > 0)
+ ret = munmap((char*)e.ptr - e.diff,e.size);
+ else
+ ret = munmap(e.ptr,e.size);
+#endif
+ e.thing->unmapped();
+
+ i++;
+ mappings.erase(e.ptr);
+
+ if (ret < 0)
+ {
+ Out(SYS_DIO|LOG_IMPORTANT) << QString("Munmap failed with error %1 : %2").arg(errno).arg(strerror(errno)) << endl;
+ }
+ }
+ ::close(fd);
+ fd = -1;
+ }
+
+ void CacheFile::read(Uint8* buf,Uint32 size,Uint64 off)
+ {
+ QMutexLocker lock(&mutex);
+ bool close_again = false;
+
+ // reopen the file if necessary
+ if (fd == -1)
+ {
+ // Out() << "Reopening " << path << endl;
+ openFile(READ);
+ close_again = true;
+ }
+
+ if (off >= file_size || off >= max_size)
+ {
+ throw Error(i18n("Error : Reading past the end of the file %1").arg(path));
+ }
+
+ // jump to right position
+ SeekFile(fd,(Int64)off,SEEK_SET);
+ if ((Uint32)::read(fd,buf,size) != size)
+ {
+ if (close_again)
+ closeTemporary();
+
+ throw Error(i18n("Error reading from %1").arg(path));
+ }
+
+ if (close_again)
+ closeTemporary();
+ }
+
+ void CacheFile::write(const Uint8* buf,Uint32 size,Uint64 off)
+ {
+ QMutexLocker lock(&mutex);
+ bool close_again = false;
+
+ // reopen the file if necessary
+ if (fd == -1)
+ {
+ // Out() << "Reopening " << path << endl;
+ openFile(RW);
+ close_again = true;
+ }
+
+ if (read_only)
+ throw Error(i18n("Cannot open %1 for writing : readonly filesystem").arg(path));
+
+ if (off + size > max_size)
+ {
+ Out() << "Warning : writing past the end of " << path << endl;
+ Out() << (off + size) << " " << max_size << endl;
+ }
+
+ if (file_size < off)
+ {
+ //Out() << QString("Writing %1 bytes at %2").arg(size).arg(off) << endl;
+ growFile(off - file_size);
+ }
+
+ // jump to right position
+ SeekFile(fd,(Int64)off,SEEK_SET);
+ int ret = ::write(fd,buf,size);
+ if (close_again)
+ closeTemporary();
+
+ if (ret == -1)
+ throw Error(i18n("Error writing to %1 : %2").arg(path).arg(strerror(errno)));
+ else if ((Uint32)ret != size)
+ {
+ Out() << QString("Incomplete write of %1 bytes, should be %2").arg(ret).arg(size) << endl;
+ throw Error(i18n("Error writing to %1").arg(path));
+ }
+
+ if (off + size > file_size)
+ file_size = off + size;
+ }
+
+ void CacheFile::closeTemporary()
+ {
+ if (fd == -1 || mappings.count() > 0)
+ return;
+
+ ::close(fd);
+ fd = -1;
+ }
+
+
+
+ void CacheFile::preallocate(PreallocationThread* prealloc)
+ {
+ QMutexLocker lock(&mutex);
+
+ if (FileSize(path) == max_size)
+ {
+ Out(SYS_GEN|LOG_NOTICE) << "File " << path << " already big enough" << endl;
+ return;
+ }
+
+ Out(SYS_GEN|LOG_NOTICE) << "Preallocating file " << path << " (" << max_size << " bytes)" << endl;
+ bool close_again = false;
+ if (fd == -1)
+ {
+ openFile(RW);
+ close_again = true;
+ }
+
+ if (read_only)
+ {
+ if (close_again)
+ closeTemporary();
+
+ throw Error(i18n("Cannot open %1 for writing : readonly filesystem").arg(path));
+ }
+
+ try
+ {
+ bool res = false;
+
+ #ifdef HAVE_XFS_XFS_H
+ if( (! res) && Settings::fullDiskPrealloc() && (Settings::fullDiskPreallocMethod() == 1) )
+ {
+ res = XfsPreallocate(fd, max_size);
+ }
+ #endif
+
+ if(! res)
+ {
+ bt::TruncateFile(fd,max_size,!Settings::fullDiskPrealloc());
+ }
+ }
+ catch (bt::Error & e)
+ {
+ // first attempt failed, must be fat so try that
+ if (!FatPreallocate(fd,max_size))
+ {
+ if (close_again)
+ closeTemporary();
+
+ throw Error(i18n("Cannot preallocate diskspace : %1").arg(strerror(errno)));
+ }
+ }
+
+ file_size = FileSize(fd);
+ Out(SYS_GEN|LOG_DEBUG) << "file_size = " << file_size << endl;
+ if (close_again)
+ closeTemporary();
+ }
+
+ Uint64 CacheFile::diskUsage()
+ {
+ Uint64 ret = 0;
+ bool close_again = false;
+ if (fd == -1)
+ {
+ openFile(READ);
+ close_again = true;
+ }
+
+ struct stat sb;
+ if (fstat(fd,&sb) == 0)
+ {
+ ret = (Uint64)sb.st_blocks * 512;
+ }
+
+ // Out(SYS_GEN|LOG_NOTICE) << "CF: " << path << " is taking up " << ret << " bytes" << endl;
+ if (close_again)
+ closeTemporary();
+
+ return ret;
+ }
+}
diff --git a/libktorrent/torrent/cachefile.h b/libktorrent/torrent/cachefile.h
new file mode 100644
index 0000000..9c4ebc6
--- /dev/null
+++ b/libktorrent/torrent/cachefile.h
@@ -0,0 +1,149 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef BTCACHEFILE_H
+#define BTCACHEFILE_H
+
+#include <qmap.h>
+#include <qmutex.h>
+#include <qstring.h>
+#include <util/constants.h>
+
+namespace bt
+{
+ class PreallocationThread;
+
+
+ /**
+ * Interface which classes must implement to be able to map something from a CacheFile
+ * It will also be used to notify when things get unmapped or remapped
+ */
+ class MMappeable
+ {
+ public:
+ virtual ~MMappeable() {}
+
+ /**
+ * When a CacheFile is closed, this will be called on all existing mappings.
+ */
+ virtual void unmapped() = 0;
+ };
+
+ /**
+ @author Joris Guisson <joris.guisson@gmail.com>
+
+ Used by Single and MultiFileCache to write to disk.
+ */
+ class CacheFile
+ {
+ public:
+ CacheFile();
+ virtual ~CacheFile();
+
+ enum Mode
+ {
+ READ,WRITE,RW
+ };
+
+
+ /**
+ * Open the file.
+ * @param path Path of the file
+ * @param size Max size of the file
+ * @throw Error when something goes wrong
+ */
+ void open(const QString & path,Uint64 size);
+
+ /// Change the path of the file
+ void changePath(const QString & npath);
+
+ /**
+ * Map a part of the file into memory, will expand the file
+ * if it is to small, but will not go past the limit set in open.
+ * @param thing The thing that wishes to map the mmapping
+ * @param off Offset into the file
+ * @param size Size of the region to map
+ * @param mode How the region will be mapped
+ * @return A ptr to the mmaped region, or 0 if something goes wrong
+ */
+ void* map(MMappeable* thing,Uint64 off,Uint32 size,Mode mode);
+
+ /**
+ * Unmap a previously mapped region.
+ * @param ptr Ptr to the region
+ * @param size Size of the region
+ */
+ void unmap(void* ptr,Uint32 size);
+
+ /**
+ * Close the file, everything will be unmapped.
+ * @param to_be_reopened Indicates if the close is temporarely (i.e. it will be reopened)
+ */
+ void close();
+
+ /**
+ * Read from the file.
+ * @param buf Buffer to store data
+ * @param size Size to read
+ * @param off Offset to read from in file
+ */
+ void read(Uint8* buf,Uint32 size,Uint64 off);
+
+ /**
+ * Write to the file.
+ * @param buf Buffer to write
+ * @param size Size to read
+ * @param off Offset to read from in file
+ */
+ void write(const Uint8* buf,Uint32 size,Uint64 off);
+
+ /**
+ * Preallocate disk space
+ */
+ void preallocate(PreallocationThread* prealloc);
+
+ /// Get the number of bytes this cache file is taking up
+ Uint64 diskUsage();
+
+ private:
+ void growFile(Uint64 to_write);
+ void closeTemporary();
+ void openFile(Mode mode);
+
+ private:
+ int fd;
+ bool read_only;
+ Uint64 max_size,file_size;
+ QString path;
+ struct Entry
+ {
+ MMappeable* thing;
+ void* ptr;
+ Uint32 size;
+ Uint64 offset;
+ Uint32 diff;
+ Mode mode;
+ };
+ QMap<void*,Entry> mappings; // mappings where offset wasn't a multiple of 4K
+ mutable QMutex mutex;
+ };
+
+}
+
+#endif
diff --git a/libktorrent/torrent/cap.cpp b/libktorrent/torrent/cap.cpp
new file mode 100644
index 0000000..a785520
--- /dev/null
+++ b/libktorrent/torrent/cap.cpp
@@ -0,0 +1,123 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#if 0
+#include <math.h>
+#include "cap.h"
+
+namespace bt
+{
+ typedef QValueList<Cap::Entry>::iterator CapItr;
+
+ Cap::Cap(bool percentage_check) : max_bytes_per_sec(0),leftover(0),current_speed(0),percentage_check(percentage_check)
+ {
+ timer.update();
+ }
+
+
+ Cap::~Cap()
+ {}
+
+ void Cap::setMaxSpeed(Uint32 max)
+ {
+ max_bytes_per_sec = max;
+ // tell everybody to go wild
+ if (max_bytes_per_sec == 0)
+ {
+ CapItr i = entries.begin();
+ while (i != entries.end())
+ {
+ Cap::Entry & e = *i;
+ e.obj->proceed(0);
+ i++;
+ }
+ entries.clear();
+ leftover = 0;
+ }
+ }
+
+ bool Cap::allow(Cappable* pd,Uint32 bytes)
+ {
+ if (max_bytes_per_sec == 0 || (percentage_check && (double)current_speed / (double)max_bytes_per_sec < 0.75))
+ {
+ timer.update();
+ return true;
+ }
+
+ // append pd to queue
+ entries.append(Cap::Entry(pd,bytes));
+ return false;
+ }
+
+ void Cap::killed(Cappable* pd)
+ {
+ CapItr i = entries.begin();
+ while (i != entries.end())
+ {
+ Cap::Entry & e = *i;
+ if (e.obj == pd)
+ i = entries.erase(i);
+ else
+ i++;
+ }
+ }
+
+ void Cap::update()
+ {
+ if (entries.count() == 0)
+ {
+ timer.update();
+ return;
+ }
+
+ // first calculate the time since the last update
+ double el = timer.getElapsedSinceUpdate();
+
+ // calculate the number of bytes we can send, including those leftover from the last time
+ Uint32 nb = (Uint32)round((el / 1000.0) * max_bytes_per_sec) + leftover;
+ leftover = 0;
+ // Out() << "nb = " << nb << endl;
+
+ while (entries.count() > 0 && nb > 0)
+ {
+ // get the first
+ Cap::Entry & e = entries.first();
+
+ if (e.num_bytes <= nb)
+ {
+ nb -= e.num_bytes;
+ // we can send all remaining bytes of the packet
+ e.obj->proceed(e.num_bytes);
+ entries.pop_front();
+ }
+ else
+ {
+ // sent nb bytes of the packets
+ e.obj->proceed(nb);
+ e.num_bytes -= nb;
+ nb = 0;
+ }
+ }
+
+ leftover = nb;
+ timer.update();
+ }
+
+}
+#endif
diff --git a/libktorrent/torrent/cap.h b/libktorrent/torrent/cap.h
new file mode 100644
index 0000000..a3a365e
--- /dev/null
+++ b/libktorrent/torrent/cap.h
@@ -0,0 +1,113 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef BTCAP_H
+#define BTCAP_H
+
+#if 0
+#include <qvaluelist.h>
+#include <util/timer.h>
+#include <util/constants.h>
+
+namespace bt
+{
+ /**
+ * Base class for all cappable objects.
+ */
+ class Cappable
+ {
+ public:
+ /**
+ * Proceed with doing some bytes
+ * @param bytes The number of bytes it can do (0 = no limit)
+ * @return true if finished, false otherwise
+ */
+ virtual void proceed(Uint32 bytes) = 0;
+ };
+
+ /**
+ * @author Joris Guisson <joris.guisson@gmail.com>
+ *
+ * A Cap is something which caps something.
+ */
+ class Cap
+ {
+ public:
+ Cap(bool percentage_check);
+ virtual ~Cap();
+
+ struct Entry
+ {
+ Cappable* obj;
+ Uint32 num_bytes;
+
+ Entry() : obj(0),num_bytes(0) {}
+ Entry(Cappable* obj,Uint32 nb) : obj(obj),num_bytes(nb) {}
+ };
+
+ /**
+ * Set the speed cap in bytes per second. 0 indicates
+ * no limit.
+ * @param max Maximum number of bytes per second.
+ */
+ void setMaxSpeed(Uint32 max);
+
+ /// Get max bytes/sec
+ Uint32 getMaxSpeed() const {return max_bytes_per_sec;}
+
+ /// Set the current speed
+ void setCurrentSpeed(Uint32 cs) {current_speed = cs;}
+
+ /// Get the current speed
+ Uint32 getCurrrentSpeed() const {return current_speed;}
+
+ /**
+ * Allow or disallow somebody from proceeding. If somebody
+ * is disallowed they will be stored in a queue, and will be notified
+ * when there turn is up.
+ * @param pd Thing which is doing the request
+ * @param bytes Bytes it wants to send
+ * @return true if the piece is allowed or not
+ */
+ bool allow(Cappable* pd,Uint32 bytes);
+
+ /**
+ * A thing in the queue should call this when it get destroyed. To
+ * remove them from the queue.
+ * @param pd The Cappable thing
+ */
+ void killed(Cappable* pd);
+
+ /**
+ * Update the downloadcap.
+ */
+ void update();
+
+ private:
+ QValueList<Entry> entries;
+ Uint32 max_bytes_per_sec;
+ Timer timer;
+ Uint32 leftover;
+ Uint32 current_speed;
+ bool percentage_check;
+ };
+
+}
+#endif
+#endif
diff --git a/libktorrent/torrent/choker.cpp b/libktorrent/torrent/choker.cpp
new file mode 100644
index 0000000..0cb08e9
--- /dev/null
+++ b/libktorrent/torrent/choker.cpp
@@ -0,0 +1,86 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+
+
+#include <qptrlist.h>
+#include <interfaces/functions.h>
+#include "choker.h"
+#include "peermanager.h"
+#include "newchokealgorithm.h"
+#include "advancedchokealgorithm.h"
+
+using namespace kt;
+
+namespace bt
+{
+
+ PeerPtrList::PeerPtrList(PeerCompareFunc pcmp) : pcmp(pcmp)
+ {}
+
+ PeerPtrList::~PeerPtrList()
+ {}
+
+ int PeerPtrList::compareItems(QPtrCollection::Item a, QPtrCollection::Item b)
+ {
+ if (pcmp)
+ return pcmp((Peer*)a,(Peer*)b);
+ else
+ return CompareVal(a,b);
+ }
+
+ ////////////////////////////////////////////
+
+ ChokeAlgorithm::ChokeAlgorithm() : opt_unchoked_peer_id(0)
+ {
+ }
+
+ ChokeAlgorithm::~ChokeAlgorithm()
+ {
+ }
+
+
+ /////////////////////////////////
+
+ Uint32 Choker::num_upload_slots = 2;
+
+ Choker::Choker(PeerManager & pman,ChunkManager & cman) : pman(pman),cman(cman)
+ {
+#ifdef USE_OLD_CHOKE
+ choke = new NewChokeAlgorithm();
+#else
+ choke = new AdvancedChokeAlgorithm();
+#endif
+ }
+
+
+ Choker::~Choker()
+ {
+ delete choke;
+ }
+
+ void Choker::update(bool have_all,const kt::TorrentStats & stats)
+ {
+ if (have_all)
+ choke->doChokingSeedingState(pman,cman,stats);
+ else
+ choke->doChokingLeechingState(pman,cman,stats);
+ }
+
+}
diff --git a/libktorrent/torrent/choker.h b/libktorrent/torrent/choker.h
new file mode 100644
index 0000000..ba78f3c
--- /dev/null
+++ b/libktorrent/torrent/choker.h
@@ -0,0 +1,123 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef BTCHOKER_H
+#define BTCHOKER_H
+
+#include <qptrlist.h>
+#include <util/constants.h>
+#include "peer.h"
+
+namespace kt
+{
+ struct TorrentStats;
+}
+
+namespace bt
+{
+ const Uint32 UNDEFINED_ID = 0xFFFFFFFF;
+
+ class PeerManager;
+ class ChunkManager;
+
+
+ typedef int (*PeerCompareFunc)(Peer* a,Peer* b);
+
+ class PeerPtrList : public QPtrList<Peer>
+ {
+ PeerCompareFunc pcmp;
+ public:
+ PeerPtrList(PeerCompareFunc pcmp = NULL);
+ virtual ~PeerPtrList();
+
+ void setCompareFunc(PeerCompareFunc p) {pcmp = p;}
+
+ virtual int compareItems(QPtrCollection::Item a, QPtrCollection::Item b);
+ };
+
+ /**
+ * Base class for all choke algorithms.
+ */
+ class ChokeAlgorithm
+ {
+ protected:
+ Uint32 opt_unchoked_peer_id;
+ public:
+ ChokeAlgorithm();
+ virtual ~ChokeAlgorithm();
+
+ /**
+ * Do the actual choking when we are still downloading.
+ * @param pman The PeerManager
+ * @param cman The ChunkManager
+ * @param stats The torrent stats
+ */
+ virtual void doChokingLeechingState(PeerManager & pman,ChunkManager & cman,const kt::TorrentStats & stats) = 0;
+
+ /**
+ * Do the actual choking when we are seeding
+ * @param pman The PeerManager
+ * @param cman The ChunkManager
+ * @param stats The torrent stats
+ */
+ virtual void doChokingSeedingState(PeerManager & pman,ChunkManager & cman,const kt::TorrentStats & stats) = 0;
+
+ /// Get the optimisticly unchoked peer ID
+ Uint32 getOptimisticlyUnchokedPeerID() const {return opt_unchoked_peer_id;}
+ };
+
+
+
+ /**
+ * @author Joris Guisson
+ * @brief Handles the choking
+ *
+ * This class handles the choking and unchoking of Peer's.
+ * This class needs to be updated every 10 seconds.
+ */
+ class Choker
+ {
+ ChokeAlgorithm* choke;
+ PeerManager & pman;
+ ChunkManager & cman;
+ static Uint32 num_upload_slots;
+ public:
+ Choker(PeerManager & pman,ChunkManager & cman);
+ virtual ~Choker();
+
+ /**
+ * Update which peers are choked or not.
+ * @param have_all Indicates wether we have the entire file
+ * @param stats Statistic of the torrent
+ */
+ void update(bool have_all,const kt::TorrentStats & stats);
+
+ /// Get the PeerID of the optimisticly unchoked peer.
+ Uint32 getOptimisticlyUnchokedPeerID() const {return choke->getOptimisticlyUnchokedPeerID();}
+
+ /// Set the number of upload slots
+ static void setNumUploadSlots(Uint32 n) {num_upload_slots = n;}
+
+ /// Get the number of upload slots
+ static Uint32 getNumUploadSlots() {return num_upload_slots;}
+ };
+
+}
+
+#endif
diff --git a/libktorrent/torrent/chunk.cpp b/libktorrent/torrent/chunk.cpp
new file mode 100644
index 0000000..6873713
--- /dev/null
+++ b/libktorrent/torrent/chunk.cpp
@@ -0,0 +1,81 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <util/sha1hash.h>
+#include "chunk.h"
+#include "globals.h"
+
+
+namespace bt
+{
+
+ Chunk::Chunk(unsigned int index,Uint32 size)
+ : status(Chunk::NOT_DOWNLOADED),index(index),
+ data(0),size(size),ref_count(0),priority(NORMAL_PRIORITY)
+ {
+ }
+
+
+ Chunk::~Chunk()
+ {
+ clear();
+ }
+
+ void Chunk::setData(Uint8* d,Status nstatus)
+ {
+ clear();
+ status = nstatus;
+ data = d;
+ }
+
+ void Chunk::allocate()
+ {
+ clear();
+ status = BUFFERED;
+ data = new Uint8[size];
+ }
+
+ void Chunk::clear()
+ {
+ if (data)
+ {
+ if (status == BUFFERED)
+ delete [] data;
+ data = 0;
+ }
+ }
+
+ void Chunk::unmapped()
+ {
+ setData(0,Chunk::ON_DISK);
+ }
+
+ bool Chunk::checkHash(const SHA1Hash & h) const
+ {
+ if (status != BUFFERED && status != MMAPPED)
+ {
+ return false;
+ }
+ else
+ {
+ return SHA1Hash::generate(data,size) == h;
+ }
+ }
+
+}
diff --git a/libktorrent/torrent/chunk.h b/libktorrent/torrent/chunk.h
new file mode 100644
index 0000000..0896e96
--- /dev/null
+++ b/libktorrent/torrent/chunk.h
@@ -0,0 +1,165 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef BTCHUNK_H
+#define BTCHUNK_H
+
+#include <util/constants.h>
+#include "cachefile.h"
+
+namespace bt
+{
+ class SHA1Hash;
+
+ /**
+ * @author Joris Guisson
+ * @brief Keep track of a piece of the file
+ *
+ * Keeps track of a piece of the file. The Chunk has 3 possible states :
+ * - MMAPPED : It is memory mapped
+ * - BUFFERED : It is in a buffer in dynamically allocated memory
+ * (because the chunk is located in 2 or more separate files, so we cannot just set a pointer
+ * to a region of mmapped memory)
+ * - ON_DISK : On disk
+ * - NOT_DOWNLOADED : It hasn't been dowloaded yet, and there is no buffer allocated
+ */
+ class Chunk : public MMappeable
+ {
+ public:
+ Chunk(unsigned int index,Uint32 size);
+ ~Chunk();
+
+ enum Status
+ {
+ MMAPPED,
+ BUFFERED,
+ ON_DISK,
+ NOT_DOWNLOADED
+ };
+
+ /// Get the chunks status.
+ Status getStatus() const;
+
+ /**
+ * Set the chunks status
+ * @param s
+ */
+ void setStatus(Status s);
+
+ /// Get the data
+ const Uint8* getData() const;
+
+ /// Get the data
+ Uint8* getData();
+
+ /// Set the data and the new status
+ void setData(Uint8* d,Status nstatus);
+
+ /// Clear the chunk (delete data depending on the mode)
+ void clear();
+
+ /// Get the chunk's index
+ Uint32 getIndex() const;
+
+ /// Get the chunk's size
+ Uint32 getSize() const;
+
+ /// Add one to the reference counter
+ void ref();
+
+ /// --reference counter
+ void unref();
+
+ /// reference coun > 0
+ bool taken() const;
+
+ /// allocate data if not already done, sets the status to buffered
+ void allocate();
+
+ /// get chunk priority
+ Priority getPriority() const;
+
+ /// set chunk priority
+ void setPriority(Priority newpriority = NORMAL_PRIORITY);
+
+ /// Is chunk excluded
+ bool isExcluded() const;
+
+ /// Is this a seed only chunk
+ bool isExcludedForDownloading() const;
+
+ /// In/Exclude chunk
+ void setExclude(bool yes);
+
+ /**
+ * Check wehter the chunk matches it's hash.
+ * @param h The hash
+ * @return true if the data matches the hash
+ */
+ bool checkHash(const SHA1Hash & h) const;
+
+ private:
+ virtual void unmapped();
+
+ private:
+ Status status;
+ Uint32 index;
+ Uint8* data;
+ Uint32 size;
+ int ref_count;
+ Priority priority;
+ };
+
+ inline Chunk::Status Chunk::getStatus() const
+ {
+ return status;
+ }
+
+ inline void Chunk::setStatus(Chunk::Status s)
+ {
+ status = s;
+ }
+
+ inline const Uint8* Chunk::getData() const {return data;}
+ inline Uint8* Chunk::getData() {return data;}
+
+ inline Uint32 Chunk::getIndex() const {return index;}
+ inline Uint32 Chunk::getSize() const {return size;}
+
+ inline void Chunk::ref() {ref_count++;}
+ inline void Chunk::unref() {ref_count--;}
+ inline bool Chunk::taken() const {return ref_count > 0;}
+
+ inline Priority Chunk::getPriority() const {return priority;}
+ inline void Chunk::setPriority(Priority newpriority) {priority = newpriority;}
+ inline bool Chunk::isExcluded() const
+ {
+ return priority == EXCLUDED;
+ }
+
+ inline bool Chunk::isExcludedForDownloading() const
+ {
+ return priority == ONLY_SEED_PRIORITY;
+ }
+
+ inline void Chunk::setExclude(bool yes)
+ {if(yes) priority = EXCLUDED; else priority = NORMAL_PRIORITY;}
+}
+
+#endif
diff --git a/libktorrent/torrent/chunkcounter.cpp b/libktorrent/torrent/chunkcounter.cpp
new file mode 100644
index 0000000..95b7535
--- /dev/null
+++ b/libktorrent/torrent/chunkcounter.cpp
@@ -0,0 +1,80 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <util/bitset.h>
+#include "chunkcounter.h"
+
+namespace bt
+{
+
+ ChunkCounter::ChunkCounter(Uint32 num_chunks) : cnt(num_chunks)
+ {
+ // fill with 0
+ cnt.fill(0);
+ }
+
+
+ ChunkCounter::~ChunkCounter()
+ {
+ }
+
+ void ChunkCounter::reset()
+ {
+ cnt.fill(0);
+ }
+
+ void ChunkCounter::incBitSet(const BitSet & bs)
+ {
+ for (Uint32 i = 0;i < cnt.size();i++)
+ {
+ if(bs.get(i))
+ cnt[i]++;
+ }
+ }
+
+ void ChunkCounter::decBitSet(const BitSet & bs)
+ {
+ for (Uint32 i = 0;i < cnt.size();i++)
+ {
+ if(bs.get(i))
+ dec(i);
+ }
+ }
+
+ void ChunkCounter::inc(Uint32 idx)
+ {
+ if (idx < cnt.size())
+ cnt[idx]++;
+ }
+
+ void ChunkCounter::dec(Uint32 idx)
+ {
+ if (idx < cnt.size() && cnt[idx] > 0)
+ cnt[idx]--;
+ }
+
+ Uint32 ChunkCounter::get(Uint32 idx) const
+ {
+ if (idx < cnt.size())
+ return cnt[idx];
+ else
+ return 0;
+ }
+
+}
diff --git a/libktorrent/torrent/chunkcounter.h b/libktorrent/torrent/chunkcounter.h
new file mode 100644
index 0000000..ac2ec49
--- /dev/null
+++ b/libktorrent/torrent/chunkcounter.h
@@ -0,0 +1,83 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef BTCHUNKCOUNTER_H
+#define BTCHUNKCOUNTER_H
+
+#include <util/constants.h>
+#include <util/array.h>
+
+namespace bt
+{
+ class BitSet;
+
+ /**
+ * @author Joris Guisson
+ *
+ * Class to keep track of how many peers have a chunk.
+ */
+ class ChunkCounter
+ {
+ Array<Uint32> cnt;
+ public:
+ ChunkCounter(Uint32 num_chunks);
+ virtual ~ChunkCounter();
+
+ /**
+ * If a bit in the bitset is one, increment the corresponding counter.
+ * @param bs The BitSet
+ */
+ void incBitSet(const BitSet & bs);
+
+
+ /**
+ * If a bit in the bitset is one, decrement the corresponding counter.
+ * @param bs The BitSet
+ */
+ void decBitSet(const BitSet & bs);
+
+ /**
+ * Increment the counter for the idx'th chunk
+ * @param idx Index of the chunk
+ */
+ void inc(Uint32 idx);
+
+
+ /**
+ * Decrement the counter for the idx'th chunk
+ * @param idx Index of the chunk
+ */
+ void dec(Uint32 idx);
+
+
+ /**
+ * Get the counter for the idx'th chunk
+ * @param idx Index of the chunk
+ */
+ Uint32 get(Uint32 idx) const;
+
+ /**
+ * Reset all values to 0
+ */
+ void reset();
+ };
+
+}
+
+#endif
diff --git a/libktorrent/torrent/chunkdownload.cpp b/libktorrent/torrent/chunkdownload.cpp
new file mode 100644
index 0000000..51e9db9
--- /dev/null
+++ b/libktorrent/torrent/chunkdownload.cpp
@@ -0,0 +1,484 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+
+#include <algorithm>
+#include <util/file.h>
+#include <util/log.h>
+#include <util/array.h>
+#include "chunkdownload.h"
+#include "downloader.h"
+#include "chunk.h"
+#include "peer.h"
+#include "peermanager.h"
+#include "piece.h"
+#include "peerdownloader.h"
+
+#include <klocale.h>
+
+namespace bt
+{
+
+ class DownloadStatus : public std::set<Uint32>
+ {
+ public:
+ // typedef std::set<Uint32>::iterator iterator;
+
+ DownloadStatus()
+ {
+
+ }
+
+ ~DownloadStatus()
+ {
+ }
+
+ void add(Uint32 p)
+ {
+ insert(p);
+ }
+
+ void remove(Uint32 p)
+ {
+ erase(p);
+ }
+
+ bool contains(Uint32 p)
+ {
+ return count(p) > 0;
+ }
+ };
+
+ ChunkDownload::ChunkDownload(Chunk* chunk) : chunk(chunk)
+ {
+ num = num_downloaded = 0;
+
+ num = chunk->getSize() / MAX_PIECE_LEN;
+
+ if (chunk->getSize() % MAX_PIECE_LEN != 0)
+ {
+ last_size = chunk->getSize() % MAX_PIECE_LEN;
+ num++;
+ }
+ else
+ {
+ last_size = MAX_PIECE_LEN;
+ }
+
+ pieces = BitSet(num);
+ pieces.clear();
+
+ for (Uint32 i = 0;i < num;i++)
+ piece_queue.append(i);
+
+ dstatus.setAutoDelete(true);
+ chunk->ref();
+
+ num_pieces_in_hash = 0;
+ if (usingContinuousHashing())
+ hash_gen.start();
+
+ }
+
+ ChunkDownload::~ChunkDownload()
+ {
+ chunk->unref();
+ }
+
+ bool ChunkDownload::piece(const Piece & p,bool & ok)
+ {
+ ok = false;
+ timer.update();
+
+ Uint32 pp = p.getOffset() / MAX_PIECE_LEN;
+ if (pieces.get(pp))
+ return false;
+
+
+ DownloadStatus* ds = dstatus.find(p.getPeer());
+ if (ds)
+ ds->remove(pp);
+
+ Uint8* buf = chunk->getData();
+ if (buf)
+ {
+ ok = true;
+ memcpy(buf + p.getOffset(),p.getData(),p.getLength());
+ pieces.set(pp,true);
+ piece_queue.remove(pp);
+ piece_providers.insert(p.getPeer());
+ num_downloaded++;
+ if (pdown.count() > 1)
+ {
+ endgameCancel(p);
+ }
+
+ if (usingContinuousHashing())
+ updateHash();
+
+ if (num_downloaded >= num)
+ {
+ // finalize hash
+ if (usingContinuousHashing())
+ hash_gen.end();
+
+ releaseAllPDs();
+ return true;
+ }
+ }
+
+ for (QPtrList<PeerDownloader>::iterator i = pdown.begin();i != pdown.end();++i)
+ sendRequests(*i);
+
+ return false;
+ }
+
+ void ChunkDownload::releaseAllPDs()
+ {
+ for (Uint32 i = 0;i < pdown.count();i++)
+ {
+ PeerDownloader* pd = pdown.at(i);
+ pd->release();
+ disconnect(pd,SIGNAL(timedout(const Request& )),this,SLOT(onTimeout(const Request& )));
+ disconnect(pd,SIGNAL(rejected( const Request& )),this,SLOT(onRejected( const Request& )));
+ }
+ dstatus.clear();
+ pdown.clear();
+ }
+
+ bool ChunkDownload::assignPeer(PeerDownloader* pd)
+ {
+ if (!pd || pdown.contains(pd))
+ return false;
+
+ pd->grab();
+ pdown.append(pd);
+ dstatus.insert(pd->getPeer()->getID(),new DownloadStatus());
+ sendRequests(pd);
+ connect(pd,SIGNAL(timedout(const Request& )),this,SLOT(onTimeout(const Request& )));
+ connect(pd,SIGNAL(rejected( const Request& )),this,SLOT(onRejected( const Request& )));
+ return true;
+ }
+
+ void ChunkDownload::notDownloaded(const Request & r,bool reject)
+ {
+ // find the peer
+ DownloadStatus* ds = dstatus.find(r.getPeer());
+ if (ds)
+ {
+ // Out() << "ds != 0" << endl;
+ Uint32 p = r.getOffset() / MAX_PIECE_LEN;
+ ds->remove(p);
+ }
+
+ // go over all PD's and do requets again
+ for (QPtrList<PeerDownloader>::iterator i = pdown.begin();i != pdown.end();++i)
+ sendRequests(*i);
+ }
+
+ void ChunkDownload::onRejected(const Request & r)
+ {
+ if (chunk->getIndex() == r.getIndex())
+ {
+// Out(SYS_CON|LOG_DEBUG) << QString("Request rejected %1 %2 %3 %4").arg(r.getIndex()).arg(r.getOffset()).arg(r.getLength()).arg(r.getPeer()) << endl;
+
+ notDownloaded(r,true);
+ }
+ }
+
+ void ChunkDownload::onTimeout(const Request & r)
+ {
+ // see if we are dealing with a piece of ours
+ if (chunk->getIndex() == r.getIndex())
+ {
+ Out(SYS_CON|LOG_DEBUG) << QString("Request timed out %1 %2 %3 %4").arg(r.getIndex()).arg(r.getOffset()).arg(r.getLength()).arg(r.getPeer()) << endl;
+
+ notDownloaded(r,false);
+ }
+ }
+
+ void ChunkDownload::sendRequests(PeerDownloader* pd)
+ {
+ timer.update();
+ DownloadStatus* ds = dstatus.find(pd->getPeer()->getID());
+ if (!ds)
+ return;
+
+ // if the peer is choked and we are not downloading an allowed fast chunk
+ if (pd->isChoked())
+ return;
+
+ Uint32 num_visited = 0;
+ while (num_visited < piece_queue.count() && pd->canAddRequest())
+ {
+ // get the first one in the queue
+ Uint32 i = piece_queue.first();
+ if (!ds->contains(i))
+ {
+ // send request
+ pd->download(
+ Request(
+ chunk->getIndex(),
+ i*MAX_PIECE_LEN,
+ i+1<num ? MAX_PIECE_LEN : last_size,
+ pd->getPeer()->getID()));
+ ds->add(i);
+ }
+ // move to the back so that it will take a while before it's turn is up
+ piece_queue.pop_front();
+ piece_queue.append(i);
+ num_visited++;
+ }
+
+ if (piece_queue.count() < 2 && piece_queue.count() > 0)
+ pd->setNearlyDone(true);
+ }
+
+
+
+ void ChunkDownload::update()
+ {
+ // go over all PD's and do requets again
+ for (QPtrList<PeerDownloader>::iterator i = pdown.begin();i != pdown.end();++i)
+ sendRequests(*i);
+ }
+
+
+ void ChunkDownload::sendCancels(PeerDownloader* pd)
+ {
+ DownloadStatus* ds = dstatus.find(pd->getPeer()->getID());
+ if (!ds)
+ return;
+
+ DownloadStatus::iterator itr = ds->begin();
+ while (itr != ds->end())
+ {
+ Uint32 i = *itr;
+ pd->cancel(
+ Request(
+ chunk->getIndex(),
+ i*MAX_PIECE_LEN,
+ i+1<num ? MAX_PIECE_LEN : last_size,0));
+ itr++;
+ }
+ ds->clear();
+ timer.update();
+ }
+
+ void ChunkDownload::endgameCancel(const Piece & p)
+ {
+ QPtrList<PeerDownloader>::iterator i = pdown.begin();
+ while (i != pdown.end())
+ {
+ PeerDownloader* pd = *i;
+ DownloadStatus* ds = dstatus.find(pd->getPeer()->getID());
+ Uint32 pp = p.getOffset() / MAX_PIECE_LEN;
+ if (ds && ds->contains(pp))
+ {
+ pd->cancel(Request(p));
+ ds->remove(pp);
+ }
+ i++;
+ }
+ }
+
+ void ChunkDownload::peerKilled(PeerDownloader* pd)
+ {
+ if (!pdown.contains(pd))
+ return;
+
+ dstatus.erase(pd->getPeer()->getID());
+ pdown.remove(pd);
+ disconnect(pd,SIGNAL(timedout(const Request& )),this,SLOT(onTimeout(const Request& )));
+ disconnect(pd,SIGNAL(rejected( const Request& )),this,SLOT(onRejected( const Request& )));
+ }
+
+
+ const Peer* ChunkDownload::getCurrentPeer() const
+ {
+ if (pdown.count() == 0)
+ return 0;
+ else
+ return pdown.getFirst()->getPeer();
+ }
+
+ Uint32 ChunkDownload::getChunkIndex() const
+ {
+ return chunk->getIndex();
+ }
+
+ QString ChunkDownload::getCurrentPeerID() const
+ {
+ if (pdown.count() == 0)
+ {
+ return QString::null;
+ }
+ else if (pdown.count() == 1)
+ {
+ const Peer* p = pdown.getFirst()->getPeer();
+ return p->getPeerID().identifyClient();
+ }
+ else
+ {
+ return i18n("1 peer","%n peers",pdown.count());
+ }
+ }
+
+ Uint32 ChunkDownload::getDownloadSpeed() const
+ {
+ Uint32 r = 0;
+ QPtrList<PeerDownloader>::const_iterator i = pdown.begin();
+ while (i != pdown.end())
+ {
+ const PeerDownloader* pd = *i;
+ r += pd->getPeer()->getDownloadRate();
+ i++;
+ }
+ return r;
+ }
+
+
+
+ void ChunkDownload::save(File & file)
+ {
+ ChunkDownloadHeader hdr;
+ hdr.index = chunk->getIndex();
+ hdr.num_bits = pieces.getNumBits();
+ hdr.buffered = chunk->getStatus() == Chunk::BUFFERED ? 1 : 0;
+ // save the chunk header
+ file.write(&hdr,sizeof(ChunkDownloadHeader));
+ // save the bitset
+ file.write(pieces.getData(),pieces.getNumBytes());
+ if (hdr.buffered)
+ {
+ // if it's a buffered chunk, save the contents to
+ file.write(chunk->getData(),chunk->getSize());
+ chunk->clear();
+ chunk->setStatus(Chunk::ON_DISK);
+ }
+ }
+
+ bool ChunkDownload::load(File & file,ChunkDownloadHeader & hdr)
+ {
+ // read pieces
+ if (hdr.num_bits != num)
+ return false;
+
+ pieces = BitSet(hdr.num_bits);
+ Array<Uint8> data(pieces.getNumBytes());
+ file.read(data,pieces.getNumBytes());
+ pieces = BitSet(data,hdr.num_bits);
+ num_downloaded = pieces.numOnBits();
+ if (hdr.buffered)
+ {
+ // if it's a buffered chunk, load the data to
+ if (file.read(chunk->getData(),chunk->getSize()) != chunk->getSize())
+ return false;
+ }
+
+ for (Uint32 i = 0;i < pieces.getNumBits();i++)
+ if (pieces.get(i))
+ piece_queue.remove(i);
+
+ updateHash();
+ return true;
+ }
+
+ Uint32 ChunkDownload::bytesDownloaded() const
+ {
+ Uint32 num_bytes = 0;
+ for (Uint32 i = 0;i < num;i++)
+ {
+ if (pieces.get(i))
+ {
+ num_bytes += i == num-1 ? last_size : MAX_PIECE_LEN;
+ }
+ }
+ return num_bytes;
+ }
+
+ void ChunkDownload::cancelAll()
+ {
+ QPtrList<PeerDownloader>::iterator i = pdown.begin();
+ while (i != pdown.end())
+ {
+ sendCancels(*i);
+ i++;
+ }
+ }
+
+ bool ChunkDownload::getOnlyDownloader(Uint32 & pid)
+ {
+ if (piece_providers.size() == 1)
+ {
+ pid = *piece_providers.begin();
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ void ChunkDownload::getStats(Stats & s)
+ {
+ s.chunk_index = chunk->getIndex();
+ s.current_peer_id = getCurrentPeerID();
+ s.download_speed = getDownloadSpeed();
+ s.num_downloaders = getNumDownloaders();
+ s.pieces_downloaded = num_downloaded;
+ s.total_pieces = num;
+ }
+
+ bool ChunkDownload::isChoked() const
+ {
+ QPtrList<PeerDownloader>::const_iterator i = pdown.begin();
+ while (i != pdown.end())
+ {
+ const PeerDownloader* pd = *i;
+ // if there is one which isn't choked
+ if (!pd->isChoked())
+ return false;
+ i++;
+ }
+ return true;
+ }
+
+ void ChunkDownload::updateHash()
+ {
+ // update the hash until where we can
+ Uint32 nn = num_pieces_in_hash;
+ while (pieces.get(nn) && nn < num)
+ nn++;
+
+ for (Uint32 i = num_pieces_in_hash;i < nn;i++)
+ {
+ const Uint8* data = chunk->getData() + i * MAX_PIECE_LEN;
+ hash_gen.update(data,i == num - 1 ? last_size : MAX_PIECE_LEN);
+ }
+ num_pieces_in_hash = nn;
+ }
+
+ bool ChunkDownload::usingContinuousHashing() const
+ {
+ // if the pieces are larger then 1 MB we will be using the continuous hashing feature
+ return pieces.getNumBits() > 64;
+ }
+}
+#include "chunkdownload.moc"
diff --git a/libktorrent/torrent/chunkdownload.h b/libktorrent/torrent/chunkdownload.h
new file mode 100644
index 0000000..4119a5b
--- /dev/null
+++ b/libktorrent/torrent/chunkdownload.h
@@ -0,0 +1,207 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef BTCHUNKDOWNLOAD_H
+#define BTCHUNKDOWNLOAD_H
+
+#include <set>
+#include <qobject.h>
+#include <qptrlist.h>
+#include <util/timer.h>
+#include <util/ptrmap.h>
+#include <util/sha1hashgen.h>
+#include <interfaces/chunkdownloadinterface.h>
+#include <util/bitset.h>
+#include "globals.h"
+#include "peerid.h"
+
+
+namespace bt
+{
+
+ class File;
+ class Chunk;
+ class Piece;
+ class Peer;
+ class Request;
+ class PeerDownloader;
+ class DownloadStatus;
+
+ struct ChunkDownloadHeader
+ {
+ Uint32 index;
+ Uint32 num_bits;
+ Uint32 buffered;
+ };
+
+
+
+
+ /**
+ * @author Joris Guisson
+ * @brief Handles the download off one Chunk off a Peer
+ *
+ * This class handles the download of one Chunk.
+ */
+ class ChunkDownload : public QObject,public kt::ChunkDownloadInterface
+ {
+ Q_OBJECT
+ public:
+ /**
+ * Constructor, set the chunk and the PeerManager.
+ * @param chunk The Chunk
+ */
+ ChunkDownload(Chunk* chunk);
+
+ virtual ~ChunkDownload();
+
+ /// Get the chunk
+ Chunk* getChunk() {return chunk;}
+
+ /// Get the total number of pieces
+ Uint32 getTotalPieces() const {return num;}
+
+ /// Get the number of pieces downloaded
+ Uint32 getPiecesDownloaded() const {return num_downloaded;}
+
+ /// Get the number of bytes downloaded.
+ Uint32 bytesDownloaded() const;
+
+ /// Get the index of the chunk
+ Uint32 getChunkIndex() const;
+
+ /// Get the current peer
+ const Peer* getCurrentPeer() const;
+
+ /// Get the PeerID of the current peer
+ QString getCurrentPeerID() const;
+
+ /// Get the download speed
+ Uint32 getDownloadSpeed() const;
+
+ /// Get download stats
+ void getStats(Stats & s);
+
+ /// See if a chunkdownload is idle (i.e. has no downloaders)
+ bool isIdle() const {return pdown.count() == 0;}
+
+ /**
+ * A Piece has arived.
+ * @param p The Piece
+ * @param ok Wether or not the piece was needed
+ * @return true If Chunk is complete
+ */
+ bool piece(const Piece & p,bool & ok);
+
+ /**
+ * Assign the downloader to download from.
+ * @param pd The downloader
+ * @return true if the peer was asigned, false if not
+ */
+ bool assignPeer(PeerDownloader* pd);
+
+ Uint32 getNumDownloaders() const {return pdown.count();}
+
+ /**
+ * A Peer has been killed. We need to remove it's
+ * PeerDownloader.
+ * @param pd The PeerDownloader
+ */
+ void peerKilled(PeerDownloader* pd);
+
+ /**
+ * Save to a File
+ * @param file The File
+ */
+ void save(File & file);
+
+ /**
+ * Load from a File
+ * @param file The File
+ */
+ bool load(File & file,ChunkDownloadHeader & hdr);
+
+ /**
+ * Cancel all requests.
+ */
+ void cancelAll();
+
+ /**
+ * When a Chunk is downloaded, this function checks if all
+ * pieces are delivered by the same peer and if so sets
+ * that peers' ID.
+ * @param pid The peers' ID (!= PeerID)
+ * @return true if there is only one downloader
+ */
+ bool getOnlyDownloader(Uint32 & pid);
+
+ /// See if a PeerDownloader is assigned to this chunk
+ bool containsPeer(PeerDownloader *pd) {return pdown.contains(pd);}
+
+ /// See if the download is choked (i.e. all downloaders are choked)
+ bool isChoked() const;
+
+ /// Release all PD's and clear the requested chunks
+ void releaseAllPDs();
+
+ /// Send requests to peers
+ void update();
+
+ /// See if this CD hasn't been active in the last update
+ bool needsToBeUpdated() const {return timer.getElapsedSinceUpdate() > 60 * 1000;}
+
+ /// Get the SHA1 hash of the downloaded chunk
+ SHA1Hash getHash() const {return hash_gen.get();}
+
+ /// Are we using the continous hashing feature for this chunk
+ bool usingContinuousHashing() const;
+
+ private slots:
+ void sendRequests(PeerDownloader* pd);
+ void sendCancels(PeerDownloader* pd);
+ void endgameCancel(const Piece & p);
+ void onTimeout(const Request & r);
+ void onRejected(const Request & r);
+
+ private:
+ void notDownloaded(const Request & r,bool reject);
+ void updateHash();
+
+ private:
+ BitSet pieces;
+ QValueList<Uint32> piece_queue;
+ Chunk* chunk;
+ Uint32 num;
+ Uint32 num_downloaded;
+ Uint32 last_size;
+ Timer timer;
+ QPtrList<PeerDownloader> pdown;
+ PtrMap<Uint32,DownloadStatus> dstatus;
+ std::set<Uint32> piece_providers;
+
+
+ SHA1HashGen hash_gen;
+ Uint32 num_pieces_in_hash;
+
+ friend File & operator << (File & out,const ChunkDownload & cd);
+ friend File & operator >> (File & in,ChunkDownload & cd);
+ };
+}
+
+#endif
diff --git a/libktorrent/torrent/chunkmanager.cpp b/libktorrent/torrent/chunkmanager.cpp
new file mode 100644
index 0000000..08aac97
--- /dev/null
+++ b/libktorrent/torrent/chunkmanager.cpp
@@ -0,0 +1,1157 @@
+/***************************************************************************
+ * Copyright (C) 2005 by *
+ * Joris Guisson <joris.guisson@gmail.com> *
+ * Ivan Vasic <ivasic@gmail.com> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <algorithm>
+#include <util/file.h>
+#include <util/array.h>
+#include <qstringlist.h>
+#include "chunkmanager.h"
+#include "torrent.h"
+#include <util/error.h>
+#include <util/bitset.h>
+#include <util/fileops.h>
+#include "singlefilecache.h"
+#include "multifilecache.h"
+#include <util/log.h>
+#include <util/functions.h>
+#include "globals.h"
+
+#include <klocale.h>
+
+namespace bt
+{
+
+ Uint32 ChunkManager::max_chunk_size_for_data_check = 0;
+
+
+ ChunkManager::ChunkManager(
+ Torrent & tor,
+ const QString & tmpdir,
+ const QString & datadir,
+ bool custom_output_name)
+ : tor(tor),chunks(tor.getNumChunks()),
+ bitset(tor.getNumChunks()),excluded_chunks(tor.getNumChunks()),only_seed_chunks(tor.getNumChunks()),todo(tor.getNumChunks())
+ {
+ during_load = false;
+ only_seed_chunks.setAll(false);
+ todo.setAll(true);
+ if (tor.isMultiFile())
+ cache = new MultiFileCache(tor,tmpdir,datadir,custom_output_name);
+ else
+ cache = new SingleFileCache(tor,tmpdir,datadir);
+
+ index_file = tmpdir + "index";
+ file_info_file = tmpdir + "file_info";
+ file_priority_file = tmpdir + "file_priority";
+ Uint64 tsize = tor.getFileLength(); // total size
+ Uint64 csize = tor.getChunkSize(); // chunk size
+ Uint64 lsize = tsize - (csize * (tor.getNumChunks() - 1)); // size of last chunk
+
+ for (Uint32 i = 0;i < tor.getNumChunks();i++)
+ {
+ if (i + 1 < tor.getNumChunks())
+ chunks.insert(i,new Chunk(i,csize));
+ else
+ chunks.insert(i,new Chunk(i,lsize));
+ }
+ chunks.setAutoDelete(true);
+ chunks_left = 0;
+ recalc_chunks_left = true;
+ corrupted_count = recheck_counter = 0;
+
+ for (Uint32 i = 0;i < tor.getNumFiles();i++)
+ {
+ TorrentFile & tf = tor.getFile(i);
+ connect(&tf,SIGNAL(downloadPriorityChanged(TorrentFile*, Priority, Priority )),
+ this,SLOT(downloadPriorityChanged(TorrentFile*, Priority, Priority )));
+
+ if (tf.getPriority() != NORMAL_PRIORITY)
+ {
+ downloadPriorityChanged(&tf,tf.getPriority(),tf.getOldPriority());
+ }
+ }
+
+ if(tor.isMultiFile())
+ {
+ for(Uint32 i=0; i<tor.getNumFiles(); ++i)
+ {
+ bt::TorrentFile & file = tor.getFile(i);
+ if (!file.isMultimedia() || file.getPriority() == bt::ONLY_SEED_PRIORITY)
+ continue;
+
+ if (file.getFirstChunk() == file.getLastChunk())
+ {
+ // prioritise whole file
+ prioritise(file.getFirstChunk(),file.getLastChunk(),PREVIEW_PRIORITY);
+ }
+ else
+ {
+ Uint32 chunkOffset;
+ chunkOffset = ((file.getLastChunk() - file.getFirstChunk()) / 100) + 1;
+ prioritise(file.getFirstChunk(), file.getFirstChunk()+chunkOffset, PREVIEW_PRIORITY);
+ if (file.getLastChunk() - file.getFirstChunk() > chunkOffset)
+ {
+ prioritise(file.getLastChunk() - chunkOffset, file.getLastChunk(), PREVIEW_PRIORITY);
+ }
+ }
+ }
+ }
+ else
+ {
+ if(tor.isMultimedia())
+ {
+ Uint32 chunkOffset;
+ chunkOffset = (tor.getNumChunks() / 100) + 1;
+
+ prioritise(0,chunkOffset,PREVIEW_PRIORITY);
+ if (tor.getNumChunks() > chunkOffset)
+ {
+ prioritise(tor.getNumChunks() - chunkOffset, tor.getNumChunks() - 1,PREVIEW_PRIORITY);
+ }
+ }
+ }
+ }
+
+
+ ChunkManager::~ChunkManager()
+ {
+ delete cache;
+ }
+
+ QString ChunkManager::getDataDir() const
+ {
+ return cache->getDataDir();
+ }
+
+ void ChunkManager::changeDataDir(const QString & data_dir)
+ {
+ cache->changeTmpDir(data_dir);
+ index_file = data_dir + "index";
+ file_info_file = data_dir + "file_info";
+ file_priority_file = data_dir + "file_priority";
+ }
+
+ KIO::Job* ChunkManager::moveDataFiles(const QString & ndir)
+ {
+ return cache->moveDataFiles(ndir);
+ }
+
+ void ChunkManager::moveDataFilesCompleted(KIO::Job* job)
+ {
+ cache->moveDataFilesCompleted(job);
+ }
+
+ void ChunkManager::changeOutputPath(const QString & output_path)
+ {
+ cache->changeOutputPath(output_path);
+ }
+
+ void ChunkManager::loadIndexFile()
+ {
+ during_load = true;
+ loadPriorityInfo();
+
+ File fptr;
+ if (!fptr.open(index_file,"rb"))
+ {
+ // no index file, so assume it's empty
+ bt::Touch(index_file,true);
+ Out(SYS_DIO|LOG_IMPORTANT) << "Can't open index file : " << fptr.errorString() << endl;
+ during_load = false;
+ return;
+ }
+
+ if (fptr.seek(File::END,0) != 0)
+ {
+ fptr.seek(File::BEGIN,0);
+
+ while (!fptr.eof())
+ {
+ NewChunkHeader hdr;
+ fptr.read(&hdr,sizeof(NewChunkHeader));
+ Chunk* c = getChunk(hdr.index);
+ if (c)
+ {
+ c->setStatus(Chunk::ON_DISK);
+ bitset.set(hdr.index,true);
+ todo.set(hdr.index,false);
+ recalc_chunks_left = true;
+ }
+ }
+ }
+ tor.updateFilePercentage(bitset);
+ during_load = false;
+ }
+
+ void ChunkManager::saveIndexFile()
+ {
+ File fptr;
+ if (!fptr.open(index_file,"wb"))
+ throw Error(i18n("Cannot open index file %1 : %2").arg(index_file).arg(fptr.errorString()));
+
+ for (unsigned int i = 0;i < tor.getNumChunks();i++)
+ {
+ Chunk* c = getChunk(i);
+ if (c->getStatus() != Chunk::NOT_DOWNLOADED)
+ {
+ NewChunkHeader hdr;
+ hdr.index = i;
+ fptr.write(&hdr,sizeof(NewChunkHeader));
+ }
+ }
+ savePriorityInfo();
+ }
+
+ void ChunkManager::createFiles(bool check_priority)
+ {
+ if (!bt::Exists(index_file))
+ {
+ File fptr;
+ fptr.open(index_file,"wb");
+ }
+ cache->create();
+ if (check_priority)
+ {
+ for (Uint32 i = 0;i < tor.getNumFiles();i++)
+ {
+ TorrentFile & tf = tor.getFile(i);
+ connect(&tf,SIGNAL(downloadPriorityChanged(TorrentFile*, Priority, Priority )),
+ this,SLOT(downloadPriorityChanged(TorrentFile*, Priority, Priority )));
+
+ if (tf.getPriority() != NORMAL_PRIORITY)
+ {
+ downloadPriorityChanged(&tf,tf.getPriority(),tf.getOldPriority());
+ }
+ }
+ }
+ }
+
+ bool ChunkManager::hasMissingFiles(QStringList & sl)
+ {
+ return cache->hasMissingFiles(sl);
+ }
+
+ Chunk* ChunkManager::getChunk(unsigned int i)
+ {
+ if (i >= chunks.count())
+ return 0;
+ else
+ return chunks[i];
+ }
+
+ void ChunkManager::start()
+ {
+ cache->open();
+ }
+
+ void ChunkManager::stop()
+ {
+ // unmmap all chunks which can
+ for (Uint32 i = 0;i < bitset.getNumBits();i++)
+ {
+ Chunk* c = chunks[i];
+ if (c->getStatus() == Chunk::MMAPPED)
+ {
+ cache->save(c);
+ c->clear();
+ c->setStatus(Chunk::ON_DISK);
+ }
+ else if (c->getStatus() == Chunk::BUFFERED)
+ {
+ c->clear();
+ c->setStatus(Chunk::ON_DISK);
+ }
+ }
+ cache->close();
+ }
+
+ Chunk* ChunkManager::grabChunk(unsigned int i)
+ {
+ if (i >= chunks.size())
+ return 0;
+
+ Chunk* c = chunks[i];
+ if (c->getStatus() == Chunk::NOT_DOWNLOADED || c->isExcluded())
+ {
+ return 0;
+ }
+ else if (c->getStatus() == Chunk::ON_DISK)
+ {
+ // load the chunk if it is on disk
+ cache->load(c);
+ loaded.insert(i,bt::GetCurrentTime());
+ bool check_allowed = (max_chunk_size_for_data_check == 0 || tor.getChunkSize() <= max_chunk_size_for_data_check);
+
+ // when no corruptions have been found, only check once every 5 chunks
+ if (check_allowed && recheck_counter < 5 && corrupted_count == 0)
+ check_allowed = false;
+
+ if (c->getData() && check_allowed)
+ {
+ recheck_counter = 0;
+ if (!c->checkHash(tor.getHash(i)))
+ {
+ Out(SYS_DIO|LOG_IMPORTANT) << "Chunk " << i
+ << " has been found invalid, redownloading" << endl;
+
+ resetChunk(i);
+ tor.updateFilePercentage(i,bitset);
+ saveIndexFile();
+ recalc_chunks_left = true;
+ corrupted_count++;
+ corrupted(i);
+ return 0;
+ }
+ }
+ else
+ {
+ recheck_counter++;
+ }
+ }
+
+ loaded.insert(i,bt::GetCurrentTime());
+ return c;
+ }
+
+ void ChunkManager::releaseChunk(unsigned int i)
+ {
+ if (i >= chunks.size())
+ return;
+
+ Chunk* c = chunks[i];
+ if (!c->taken())
+ {
+ if (c->getStatus() == Chunk::MMAPPED)
+ cache->save(c);
+ c->clear();
+ c->setStatus(Chunk::ON_DISK);
+ loaded.remove(i);
+ }
+ }
+
+ void ChunkManager::resetChunk(unsigned int i)
+ {
+ if (i >= chunks.size())
+ return;
+
+ Chunk* c = chunks[i];
+ if (c->getStatus() == Chunk::MMAPPED)
+ cache->save(c);
+ c->clear();
+ c->setStatus(Chunk::NOT_DOWNLOADED);
+ bitset.set(i,false);
+ todo.set(i,!excluded_chunks.get(i) && !only_seed_chunks.get(i));
+ loaded.remove(i);
+ tor.updateFilePercentage(i,bitset);
+ }
+
+ void ChunkManager::checkMemoryUsage()
+ {
+ Uint32 num_removed = 0;
+ QMap<Uint32,TimeStamp>::iterator i = loaded.begin();
+ while (i != loaded.end())
+ {
+ Chunk* c = chunks[i.key()];
+ // get rid of chunk if nobody asked for it in the last 5 seconds
+ if (!c->taken() && bt::GetCurrentTime() - i.data() > 5000)
+ {
+ if (c->getStatus() == Chunk::MMAPPED)
+ cache->save(c);
+ c->clear();
+ c->setStatus(Chunk::ON_DISK);
+ QMap<Uint32,TimeStamp>::iterator j = i;
+ i++;
+ loaded.erase(j);
+ num_removed++;
+ }
+ else
+ {
+ i++;
+ }
+ }
+ // Uint32 num_in_mem = loaded.count();
+ // Out() << QString("Cleaned %1 chunks, %2 still in memory").arg(num_removed).arg(num_in_mem) << endl;
+ }
+
+ void ChunkManager::saveChunk(unsigned int i,bool update_index)
+ {
+ if (i >= chunks.size())
+ return;
+
+ Chunk* c = chunks[i];
+ if (!c->isExcluded())
+ {
+ cache->save(c);
+
+ // update the index file
+ if (update_index)
+ {
+ bitset.set(i,true);
+ todo.set(i,false);
+ recalc_chunks_left = true;
+ writeIndexFileEntry(c);
+ tor.updateFilePercentage(i,bitset);
+ }
+ }
+ else
+ {
+ c->clear();
+ c->setStatus(Chunk::NOT_DOWNLOADED);
+ Out(SYS_DIO|LOG_IMPORTANT) << "Warning: attempted to save a chunk which was excluded" << endl;
+ }
+ }
+
+ void ChunkManager::writeIndexFileEntry(Chunk* c)
+ {
+ File fptr;
+ if (!fptr.open(index_file,"r+b"))
+ {
+ // no index file, so assume it's empty
+ bt::Touch(index_file,true);
+ Out(SYS_DIO|LOG_IMPORTANT) << "Can't open index file : " << fptr.errorString() << endl;
+ // try again
+ if (!fptr.open(index_file,"r+b"))
+ // panick if it failes
+ throw Error(i18n("Cannot open index file %1 : %2").arg(index_file).arg(fptr.errorString()));
+ }
+
+
+ fptr.seek(File::END,0);
+ NewChunkHeader hdr;
+ hdr.index = c->getIndex();
+ fptr.write(&hdr,sizeof(NewChunkHeader));
+ }
+
+ Uint32 ChunkManager::onlySeedChunks() const
+ {
+ return only_seed_chunks.numOnBits();
+ }
+
+ bool ChunkManager::completed() const
+ {
+ return todo.numOnBits() == 0 && bitset.numOnBits() > 0;
+ }
+
+ Uint64 ChunkManager::bytesLeft() const
+ {
+ Uint32 num_left = bitset.getNumBits() - bitset.numOnBits();
+ Uint32 last = chunks.size() - 1;
+ if (last < chunks.size() && !bitset.get(last))
+ {
+ Chunk* c = chunks[last];
+ if (c)
+ return (num_left - 1)*tor.getChunkSize() + c->getSize();
+ else
+ return num_left*tor.getChunkSize();
+ }
+ else
+ {
+ return num_left*tor.getChunkSize();
+ }
+ }
+
+ Uint64 ChunkManager::bytesLeftToDownload() const
+ {
+ Uint32 num_left = todo.numOnBits();
+ Uint32 last = chunks.size() - 1;
+ if (last < chunks.size() && todo.get(last))
+ {
+ Chunk* c = chunks[last];
+ if (c)
+ return (num_left - 1)*tor.getChunkSize() + c->getSize();
+ else
+ return num_left*tor.getChunkSize();
+ }
+ else
+ {
+ return num_left*tor.getChunkSize();
+ }
+ }
+
+ Uint32 ChunkManager::chunksLeft() const
+ {
+ if (!recalc_chunks_left)
+ return chunks_left;
+
+ Uint32 num = 0;
+ Uint32 tot = chunks.size();
+ for (Uint32 i = 0;i < tot;i++)
+ {
+ const Chunk* c = chunks[i];
+ if (!bitset.get(i) && !c->isExcluded())
+ num++;
+ }
+ chunks_left = num;
+ recalc_chunks_left = false;
+ return num;
+ }
+
+ bool ChunkManager::haveAllChunks() const
+ {
+ return bitset.numOnBits() == bitset.getNumBits();
+ }
+
+ Uint64 ChunkManager::bytesExcluded() const
+ {
+ Uint64 excl = 0;
+ if (excluded_chunks.get(tor.getNumChunks() - 1))
+ {
+ Chunk* c = chunks[tor.getNumChunks() - 1];
+ Uint32 num = excluded_chunks.numOnBits() - 1;
+ excl = tor.getChunkSize() * num + c->getSize();
+ }
+ else
+ {
+ excl = tor.getChunkSize() * excluded_chunks.numOnBits();
+ }
+
+ if (only_seed_chunks.get(tor.getNumChunks() - 1))
+ {
+ Chunk* c = chunks[tor.getNumChunks() - 1];
+ Uint32 num = only_seed_chunks.numOnBits() - 1;
+ excl += tor.getChunkSize() * num + c->getSize();
+ }
+ else
+ {
+ excl += tor.getChunkSize() * only_seed_chunks.numOnBits();
+ }
+ return excl;
+ }
+
+ Uint32 ChunkManager::chunksExcluded() const
+ {
+ return excluded_chunks.numOnBits() + only_seed_chunks.numOnBits();
+ }
+
+ Uint32 ChunkManager::chunksDownloaded() const
+ {
+ return bitset.numOnBits();
+ }
+
+ void ChunkManager::debugPrintMemUsage()
+ {
+ Out(SYS_DIO|LOG_DEBUG) << "Active Chunks : " << loaded.count()<< endl;
+ }
+
+ void ChunkManager::prioritise(Uint32 from,Uint32 to,Priority priority)
+ {
+ if (from > to)
+ std::swap(from,to);
+
+ Uint32 i = from;
+ while (i <= to && i < chunks.count())
+ {
+ Chunk* c = chunks[i];
+ c->setPriority(priority);
+
+ if (priority == ONLY_SEED_PRIORITY)
+ {
+ only_seed_chunks.set(i,true);
+ todo.set(i,false);
+ }
+ else if (priority == EXCLUDED)
+ {
+ only_seed_chunks.set(i,false);
+ todo.set(i,false);
+ }
+ else
+ {
+ only_seed_chunks.set(i,false);
+ todo.set(i,!bitset.get(i));
+ }
+
+ i++;
+ }
+ updateStats();
+ }
+
+ void ChunkManager::exclude(Uint32 from,Uint32 to)
+ {
+ if (from > to)
+ std::swap(from,to);
+
+ Uint32 i = from;
+ while (i <= to && i < chunks.count())
+ {
+ Chunk* c = chunks[i];
+ c->setExclude(true);
+ excluded_chunks.set(i,true);
+ only_seed_chunks.set(i,false);
+ todo.set(i,false);
+ bitset.set(i,false);
+ i++;
+ }
+ recalc_chunks_left = true;
+ excluded(from,to);
+ updateStats();
+ }
+
+ void ChunkManager::include(Uint32 from,Uint32 to)
+ {
+ if (from > to)
+ std::swap(from,to);
+
+ Uint32 i = from;
+ while (i <= to && i < chunks.count())
+ {
+ Chunk* c = chunks[i];
+ c->setExclude(false);
+ excluded_chunks.set(i,false);
+ if (!bitset.get(i))
+ todo.set(i,true);
+ i++;
+ }
+ recalc_chunks_left = true;
+ updateStats();
+ included(from,to);
+ }
+
+ void ChunkManager::saveFileInfo()
+ {
+ // saves which TorrentFiles do not need to be downloaded
+ File fptr;
+ if (!fptr.open(file_info_file,"wb"))
+ {
+ Out(SYS_DIO|LOG_IMPORTANT) << "Warning : Can't save chunk_info file : " << fptr.errorString() << endl;
+ return;
+ }
+
+ // first write the number of excluded ones
+ // don't know this yet, so write 0 for the time being
+ Uint32 tmp = 0;
+ fptr.write(&tmp,sizeof(Uint32));
+
+ Uint32 i = 0;
+ Uint32 cnt = 0;
+ while (i < tor.getNumFiles())
+ {
+ if (tor.getFile(i).doNotDownload())
+ {
+ fptr.write(&i,sizeof(Uint32));
+ cnt++;
+ }
+ i++;
+ }
+
+ // go back to the beginning and write the number of files
+ fptr.seek(File::BEGIN,0);
+ fptr.write(&cnt,sizeof(Uint32));
+ fptr.flush();
+ }
+
+ void ChunkManager::loadFileInfo()
+ {
+ if (during_load)
+ return;
+
+ File fptr;
+ if (!fptr.open(file_info_file,"rb"))
+ return;
+
+ Uint32 num = 0,tmp = 0;
+ // first read the number of dnd files
+ if (fptr.read(&num,sizeof(Uint32)) != sizeof(Uint32))
+ {
+ Out(SYS_DIO|LOG_IMPORTANT) << "Warning : error reading chunk_info file" << endl;
+ return;
+ }
+
+ for (Uint32 i = 0;i < num;i++)
+ {
+ if (fptr.read(&tmp,sizeof(Uint32)) != sizeof(Uint32))
+ {
+ Out(SYS_DIO|LOG_IMPORTANT) << "Warning : error reading chunk_info file" << endl;
+ return;
+ }
+
+ bt::TorrentFile & tf = tor.getFile(tmp);
+ if (!tf.isNull())
+ {
+ Out(SYS_DIO|LOG_DEBUG) << "Excluding : " << tf.getPath() << endl;
+ tf.setDoNotDownload(true);
+ }
+ }
+ }
+
+ void ChunkManager::savePriorityInfo()
+ {
+ if (during_load)
+ return;
+
+ //save priority info and call saveFileInfo
+ saveFileInfo();
+ File fptr;
+ if (!fptr.open(file_priority_file,"wb"))
+ {
+ Out(SYS_DIO|LOG_IMPORTANT) << "Warning : Can't save chunk_info file : " << fptr.errorString() << endl;
+ return;
+ }
+
+ try
+ {
+ // first write the number of excluded ones
+ // don't know this yet, so write 0 for the time being
+ Uint32 tmp = 0;
+ fptr.write(&tmp,sizeof(Uint32));
+
+ Uint32 i = 0;
+ Uint32 cnt = 0;
+ while (i < tor.getNumFiles())
+ {
+ const TorrentFile & tf = tor.getFile(i);
+ if (tf.getPriority() != NORMAL_PRIORITY)
+ {
+ tmp = tf.getPriority();
+ fptr.write(&i,sizeof(Uint32));
+ fptr.write(&tmp,sizeof(Uint32));
+ cnt+=2;
+ }
+ i++;
+ }
+
+ // go back to the beginning and write the number of items
+ fptr.seek(File::BEGIN,0);
+ fptr.write(&cnt,sizeof(Uint32));
+ fptr.flush();
+ }
+ catch (bt::Error & err)
+ {
+ Out(SYS_DIO|LOG_IMPORTANT) << "Failed to save priority file " << err.toString() << endl;
+ bt::Delete(file_priority_file,true);
+ }
+ }
+
+ void ChunkManager::loadPriorityInfo()
+ {
+ //load priority info and if that fails load file info
+ File fptr;
+ if (!fptr.open(file_priority_file,"rb"))
+ {
+ loadFileInfo();
+ return;
+ }
+
+ Uint32 num = 0;
+ // first read the number of lines
+ if (fptr.read(&num,sizeof(Uint32)) != sizeof(Uint32) || num > 2*tor.getNumFiles())
+ {
+ Out(SYS_DIO|LOG_IMPORTANT) << "Warning : error reading chunk_info file" << endl;
+ loadFileInfo();
+ return;
+ }
+
+ Array<Uint32> buf(num);
+ if (fptr.read(buf,sizeof(Uint32)*num) != sizeof(Uint32)*num)
+ {
+ Out(SYS_DIO|LOG_IMPORTANT) << "Warning : error reading chunk_info file" << endl;
+ loadFileInfo();
+ return;
+ }
+
+ fptr.close();
+
+ for (Uint32 i = 0;i < num;i += 2)
+ {
+ Uint32 idx = buf[i];
+ if (idx >= tor.getNumFiles())
+ {
+ Out(SYS_DIO|LOG_IMPORTANT) << "Warning : error reading chunk_info file" << endl;
+ loadFileInfo();
+ return;
+ }
+
+ bt::TorrentFile & tf = tor.getFile(idx);
+
+ if (!tf.isNull())
+ {
+ // numbers are to be compatible with old chunk info files
+ switch(buf[i+1])
+ {
+ case FIRST_PRIORITY:
+ case 3:
+ tf.setPriority(FIRST_PRIORITY);
+ break;
+ case NORMAL_PRIORITY:
+ case 2:
+ tf.setPriority(NORMAL_PRIORITY);
+ break;
+ case EXCLUDED:
+ case 0:
+ //tf.setDoNotDownload(true);
+ tf.setPriority(EXCLUDED);
+ break;
+ case ONLY_SEED_PRIORITY:
+ case -1:
+ tf.setPriority(ONLY_SEED_PRIORITY);
+ break;
+ default:
+ tf.setPriority(LAST_PRIORITY);
+ break;
+ }
+ }
+ }
+ }
+
+ void ChunkManager::downloadStatusChanged(TorrentFile* tf,bool download)
+ {
+ Uint32 first = tf->getFirstChunk();
+ Uint32 last = tf->getLastChunk();
+ if (download)
+ {
+ // include the chunks
+ include(first,last);
+
+ // if it is a multimedia file, prioritise first and last chunks of file
+ if (tf->isMultimedia())
+ {
+ Uint32 chunkOffset;
+ chunkOffset = ((last - first) / 100) + 1;
+
+ prioritise(first,first+chunkOffset,PREVIEW_PRIORITY);
+ if (last - first > 2)
+ {
+ prioritise(last - chunkOffset, last, PREVIEW_PRIORITY);
+ //prioritise(last -1,last, PREVIEW_PRIORITY);
+ }
+ }
+ }
+ else
+ {
+ // Out(SYS_DIO|LOG_DEBUG) << "Excluding chunks " << first << " to " << last << endl;
+ // first and last chunk may be part of multiple files
+ // so we can't just exclude them
+ QValueList<Uint32> files,last_files;
+
+ // get list of files where first chunk lies in
+ tor.calcChunkPos(first,files);
+ tor.calcChunkPos(last,last_files);
+ // check for exceptional case which causes very long loops
+ if (first == last && files.count() > 1)
+ {
+ cache->downloadStatusChanged(tf,download);
+ savePriorityInfo();
+ return;
+ }
+
+ // go over all chunks from first to last and mark them as not downloaded
+ // (first and last not included)
+ for (Uint32 i = first + 1;i < last;i++)
+ resetChunk(i);
+
+ // if the first chunk only lies in one file, reset it
+ if (files.count() == 1 && first != 0)
+ {
+ // Out(SYS_DIO|LOG_DEBUG) << "Resetting first " << first << endl;
+ resetChunk(first);
+ }
+
+ // if the last chunk only lies in one file reset it
+ if (last != first && last_files.count() == 1)
+ {
+ // Out(SYS_DIO|LOG_DEBUG) << "Resetting last " << last << endl;
+ resetChunk(last);
+ }
+
+ Priority maxp = ONLY_SEED_PRIORITY;
+ bool reprioritise_border_chunk = false;
+ bool modified = false;
+
+ // if one file in the list needs to be downloaded,increment first
+ for (QValueList<Uint32>::iterator i = files.begin();i != files.end();i++)
+ {
+ if (*i == tf->getIndex())
+ continue;
+
+ const TorrentFile & other = tor.getFile(*i);
+ if (!other.doNotDownload())
+ {
+ if (first != last && !modified)
+ {
+ first++;
+ reprioritise_border_chunk = true;
+ modified = true;
+ }
+
+ if (other.getPriority() > maxp)
+ maxp = other.getPriority();
+ }
+ }
+
+ // in case we have incremented first, we better reprioritise the border chunk
+ if (reprioritise_border_chunk)
+ prioritise(first-1,first-1,maxp);
+
+ maxp = ONLY_SEED_PRIORITY;
+ reprioritise_border_chunk = false;
+ modified = false;
+
+ // if one file in the list needs to be downloaded,decrement last
+ for (QValueList<Uint32>::iterator i = last_files.begin();i != last_files.end();i++)
+ {
+ if (*i == tf->getIndex())
+ continue;
+
+ const TorrentFile & other = tor.getFile(*i);
+ if (!other.doNotDownload())
+ {
+ if (first != last && last > 0 && !modified)
+ {
+ last--;
+ reprioritise_border_chunk = true;
+ modified = true;
+ }
+
+ if (other.getPriority() > maxp)
+ maxp = other.getPriority();
+ }
+ }
+
+ if (reprioritise_border_chunk)
+ prioritise(last+1,last+1,maxp);
+
+ // last smaller then first is not normal, so just return
+ if (last < first)
+ {
+ cache->downloadStatusChanged(tf,download);
+ savePriorityInfo();
+ return;
+ }
+
+ // Out(SYS_DIO|LOG_DEBUG) << "exclude " << first << " to " << last << endl;
+ exclude(first,last);
+ }
+ // alert the cache but first put things in critical operation mode
+ cache->downloadStatusChanged(tf,download);
+ savePriorityInfo();
+ }
+
+ void ChunkManager::downloadPriorityChanged(TorrentFile* tf,Priority newpriority,Priority oldpriority)
+ {
+ if (newpriority == EXCLUDED)
+ {
+ downloadStatusChanged(tf, false);
+ return;
+ }
+ if (oldpriority == EXCLUDED)
+ {
+ downloadStatusChanged(tf, true);
+ return;
+ }
+
+ savePriorityInfo();
+
+ Uint32 first = tf->getFirstChunk();
+ Uint32 last = tf->getLastChunk();
+
+ // first and last chunk may be part of multiple files
+ // so we can't just exclude them
+ QValueList<Uint32> files;
+
+ // get list of files where first chunk lies in
+ tor.calcChunkPos(first,files);
+
+ Chunk* c = chunks[first];
+ // if one file in the list needs to be downloaded,increment first
+ for (QValueList<Uint32>::iterator i = files.begin();i != files.end();i++)
+ {
+ Priority np = tor.getFile(*i).getPriority();
+ if (np > newpriority && *i != tf->getIndex())
+ {
+ // make sure we don't go past last
+ if (first == last)
+ return;
+
+ first++;
+ break;
+ }
+ }
+
+ files.clear();
+ // get list of files where last chunk lies in
+ tor.calcChunkPos(last,files);
+ c = chunks[last];
+ // if one file in the list needs to be downloaded,decrement last
+ for (QValueList<Uint32>::iterator i = files.begin();i != files.end();i++)
+ {
+ Priority np = tor.getFile(*i).getPriority();
+ if (np > newpriority && *i != tf->getIndex())
+ {
+ // make sure we don't wrap around
+ if (last == 0 || last == first)
+ return;
+
+ last--;
+ break;
+ }
+ }
+
+ // last smaller then first is not normal, so just return
+ if (last < first)
+ {
+ return;
+ }
+
+
+ prioritise(first,last,newpriority);
+ if (newpriority == ONLY_SEED_PRIORITY)
+ excluded(first,last);
+ }
+
+ bool ChunkManager::prepareChunk(Chunk* c,bool allways)
+ {
+ if (!allways && c->getStatus() != Chunk::NOT_DOWNLOADED)
+ return false;
+
+ return cache->prep(c);
+ }
+
+ QString ChunkManager::getOutputPath() const
+ {
+ return cache->getOutputPath();
+ }
+
+ void ChunkManager::preallocateDiskSpace(PreallocationThread* prealloc)
+ {
+ cache->preallocateDiskSpace(prealloc);
+ }
+
+ void ChunkManager::dataChecked(const BitSet & ok_chunks)
+ {
+ // go over all chunks at check each of them
+ for (Uint32 i = 0;i < chunks.count();i++)
+ {
+ Chunk* c = chunks[i];
+ if (ok_chunks.get(i) && !bitset.get(i))
+ {
+ // We think we do not hae a chunk, but we do have it
+ bitset.set(i,true);
+ todo.set(i,false);
+ // the chunk must be on disk
+ c->setStatus(Chunk::ON_DISK);
+ tor.updateFilePercentage(i,bitset);
+ }
+ else if (!ok_chunks.get(i) && bitset.get(i))
+ {
+ Out(SYS_DIO|LOG_IMPORTANT) << "Previously OK chunk " << i << " is corrupt !!!!!" << endl;
+ // We think we have a chunk, but we don't
+ bitset.set(i,false);
+ todo.set(i,!only_seed_chunks.get(i) && !excluded_chunks.get(i));
+ if (c->getStatus() == Chunk::ON_DISK)
+ {
+ c->setStatus(Chunk::NOT_DOWNLOADED);
+ tor.updateFilePercentage(i,bitset);
+ }
+ else if (c->getStatus() == Chunk::MMAPPED || c->getStatus() == Chunk::BUFFERED)
+ {
+ resetChunk(i);
+ }
+ else
+ {
+ tor.updateFilePercentage(i,bitset);
+ }
+ }
+ }
+ recalc_chunks_left = true;
+ try
+ {
+ saveIndexFile();
+ }
+ catch (bt::Error & err)
+ {
+ Out(SYS_DIO|LOG_DEBUG) << "Failed to save index file : " << err.toString() << endl;
+ }
+ catch (...)
+ {
+ Out(SYS_DIO|LOG_DEBUG) << "Failed to save index file : unkown exception" << endl;
+ }
+ chunksLeft();
+ corrupted_count = 0;
+ }
+
+ bool ChunkManager::hasExistingFiles() const
+ {
+ return cache->hasExistingFiles();
+ }
+
+
+ void ChunkManager::recreateMissingFiles()
+ {
+ createFiles();
+ if (tor.isMultiFile())
+ {
+ // loop over all files and mark all chunks of all missing files as
+ // not downloaded
+ for (Uint32 i = 0;i < tor.getNumFiles();i++)
+ {
+ TorrentFile & tf = tor.getFile(i);
+ if (!tf.isMissing())
+ continue;
+
+ for (Uint32 j = tf.getFirstChunk(); j <= tf.getLastChunk();j++)
+ resetChunk(j);
+ tf.setMissing(false);
+ }
+ }
+ else
+ {
+ // reset all chunks in case of single file torrent
+ for (Uint32 j = 0; j < tor.getNumChunks();j++)
+ resetChunk(j);
+ }
+ saveIndexFile();
+ recalc_chunks_left = true;
+ chunksLeft();
+ }
+
+ void ChunkManager::dndMissingFiles()
+ {
+ // createFiles(); // create them again
+ // loop over all files and mark all chunks of all missing files as
+ // not downloaded
+ for (Uint32 i = 0;i < tor.getNumFiles();i++)
+ {
+ TorrentFile & tf = tor.getFile(i);
+ if (!tf.isMissing())
+ continue;
+
+ for (Uint32 j = tf.getFirstChunk(); j <= tf.getLastChunk();j++)
+ resetChunk(j);
+ tf.setMissing(false);
+ tf.setDoNotDownload(true); // set do not download
+ }
+ savePriorityInfo();
+ saveIndexFile();
+ recalc_chunks_left = true;
+ chunksLeft();
+ }
+
+ void ChunkManager::deleteDataFiles()
+ {
+ cache->deleteDataFiles();
+ }
+
+ Uint64 ChunkManager::diskUsage()
+ {
+ return cache->diskUsage();
+ }
+
+}
+
+#include "chunkmanager.moc"
diff --git a/libktorrent/torrent/chunkmanager.h b/libktorrent/torrent/chunkmanager.h
new file mode 100644
index 0000000..daa2300
--- /dev/null
+++ b/libktorrent/torrent/chunkmanager.h
@@ -0,0 +1,366 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef BTCHUNKMANAGER_H
+#define BTCHUNKMANAGER_H
+
+#include <qmap.h>
+#include <qstring.h>
+#include <qobject.h>
+#include <qptrvector.h>
+#include <util/bitset.h>
+#include "chunk.h"
+#include "globals.h"
+
+class QStringList;
+
+namespace KIO
+{
+ class Job;
+}
+
+namespace bt
+{
+ class Torrent;
+ class Cache;
+ class TorrentFile;
+ class PreallocationThread;
+
+ struct NewChunkHeader
+ {
+ unsigned int index; // the Chunks index
+ unsigned int deprecated; // offset in cache file
+ };
+
+ /**
+ * @author Joris Guisson
+ *
+ * Manages all Chunk's and the cache file, where all the chunk's are stored.
+ * It also manages a separate index file, where the position of each piece
+ * in the cache file is stored.
+ *
+ * The chunks are stored in the cache file in the correct order. Eliminating
+ * the need for a file reconstruction algorithm for single files.
+ */
+ class ChunkManager : public QObject
+ {
+ Q_OBJECT
+
+ Torrent & tor;
+ QString index_file,file_info_file,file_priority_file;
+ QPtrVector<Chunk> chunks;
+ Cache* cache;
+ QMap<Uint32,TimeStamp> loaded; // loaded chunks and when they were loaded
+ BitSet bitset;
+ BitSet excluded_chunks;
+ BitSet only_seed_chunks;
+ BitSet todo;
+ mutable Uint32 chunks_left;
+ mutable bool recalc_chunks_left;
+ Uint32 corrupted_count;
+ Uint32 recheck_counter;
+ bool during_load;
+ public:
+ ChunkManager(Torrent & tor,
+ const QString & tmpdir,
+ const QString & datadir,
+ bool custom_output_name);
+ virtual ~ChunkManager();
+
+ /// Get the torrent
+ const Torrent & getTorrent() const {return tor;}
+
+ /// Get the data dir
+ QString getDataDir() const;
+
+ /// Get the actual output path
+ QString getOutputPath() const;
+
+ void changeOutputPath(const QString& output_path);
+
+ /// Remove obsolete chunks
+ void checkMemoryUsage();
+
+ /**
+ * Change the data dir.
+ * @param data_dir
+ */
+ void changeDataDir(const QString & data_dir);
+
+ /**
+ * Move the data files of the torrent.
+ * @param ndir The new directory
+ * @return The job doing the move
+ */
+ KIO::Job* moveDataFiles(const QString & ndir);
+
+ /**
+ * The move data files job has finished
+ * @param job The move job
+ */
+ void moveDataFilesCompleted(KIO::Job* job);
+
+ /**
+ * Loads the index file.
+ * @throw Error When it can be loaded
+ */
+ void loadIndexFile();
+
+ /**
+ * Create the cache file, and index files.
+ * @param check_priority Make sure chunk priorities and dnd status of files match
+ * @throw Error When it can be created
+ */
+ void createFiles(bool check_priority = false);
+
+ /**
+ * Test all files and see if they are not missing.
+ * If so put them in a list
+ */
+ bool hasMissingFiles(QStringList & sl);
+
+ /**
+ * Preallocate diskspace for all files
+ * @param prealloc The thread doing the preallocation
+ */
+ void preallocateDiskSpace(PreallocationThread* prealloc);
+
+ /**
+ * Open the necessary files when the download gets started.
+ */
+ void start();
+
+ /**
+ * Closes files when the download gets stopped.
+ */
+ void stop();
+
+ /**
+ * Get's the i'th Chunk.
+ * @param i The Chunk's index
+ * @return The Chunk, or 0 when i is out of bounds
+ */
+ Chunk* getChunk(unsigned int i);
+
+ /**
+ * Get's the i'th Chunk. Makes sure that the Chunk's data
+ * is in memory. If the Chunk hasn't been downloaded yet 0
+ * is returned. Whenever the Chunk needs to be uploaded, call
+ * this function. This changes the status to MMAPPED or BUFFERED.
+ * @param i The Chunk's index
+ * @return The Chunk, or 0 when i is out of bounds
+ */
+ Chunk* grabChunk(unsigned int i);
+
+ /**
+ * Prepare a chunk for downloading
+ * @param c The Chunk
+ * @param allways Always do this, even if the chunk is not NOT_DOWNLOADED
+ * @return true if ok, false if the chunk is not NOT_DOWNLOADED
+ */
+ bool prepareChunk(Chunk* c,bool allways = false);
+
+ /**
+ * The upload is done, and the Chunk is no longer needed.
+ * The Chunk's data might be cleared, if we are using up to much
+ * memory.
+ * @param i The Chunk's index
+ */
+ void releaseChunk(unsigned int i);
+
+ /**
+ * Reset a chunk as if it were never downloaded.
+ * @param i The chunk
+ */
+ void resetChunk(unsigned int i);
+
+ /**
+ * Save the i'th Chunk to the cache_file.
+ * Also changes the Chunk's status to ON_DISK.
+ * The Chunk's data is immediately cleared.
+ * @param i The Chunk's index
+ * @param update_index Update the index or not
+ */
+ void saveChunk(unsigned int i,bool update_index = true);
+
+ /**
+ * Calculates the number of bytes left for the tracker. Does include
+ * excluded chunks (this should be used for the tracker).
+ * @return The number of bytes to download + the number of bytes excluded
+ */
+ Uint64 bytesLeft() const;
+
+ /**
+ * Calculates the number of bytes left to download.
+ */
+ Uint64 bytesLeftToDownload() const;
+
+ /**
+ * Calculates the number of bytes which have been excluded.
+ * @return The number of bytes excluded
+ */
+ Uint64 bytesExcluded() const;
+
+ /**
+ * Calculates the number of chunks left to download.
+ * Does not include excluded chunks.
+ * @return The number of chunks to download
+ */
+ Uint32 chunksLeft() const;
+
+ /**
+ * Check if we have all chunks, this is not the same as
+ * chunksLeft() == 0, it does not look at excluded chunks.
+ * @return true if all chunks have been downloaded
+ */
+ bool haveAllChunks() const;
+
+ /**
+ * Get the number of chunks which have been excluded.
+ * @return The number of excluded chunks
+ */
+ Uint32 chunksExcluded() const;
+
+ /**
+ * Get the number of downloaded chunks
+ * @return
+ */
+ Uint32 chunksDownloaded() const;
+
+ /**
+ * Get the number of only seed chunks.
+ */
+ Uint32 onlySeedChunks() const;
+
+ /**
+ * Get a BitSet of the status of all Chunks
+ */
+ const BitSet & getBitSet() const {return bitset;}
+
+ /**
+ * Get the excluded bitset
+ */
+ const BitSet & getExcludedBitSet() const {return excluded_chunks;}
+
+ /**
+ * Get the only seed bitset.
+ */
+ const BitSet & getOnlySeedBitSet() const {return only_seed_chunks;}
+
+ /// Get the number of chunks into the file.
+ Uint32 getNumChunks() const {return chunks.count();}
+
+ /// Print memory usage to log file
+ void debugPrintMemUsage();
+
+ /**
+ * Make sure that a range will get priority over other chunks.
+ * @param from First chunk in range
+ * @param to Last chunk in range
+ */
+ void prioritise(Uint32 from,Uint32 to, Priority priority);
+
+ /**
+ * Make sure that a range will not be downloaded.
+ * @param from First chunk in range
+ * @param to Last chunk in range
+ */
+ void exclude(Uint32 from,Uint32 to);
+
+ /**
+ * Make sure that a range will be downloaded.
+ * Does the opposite of exclude.
+ * @param from First chunk in range
+ * @param to Last chunk in range
+ */
+ void include(Uint32 from,Uint32 to);
+
+
+ /**
+ * Data has been checked, and these chunks are OK.
+ * The ChunkManager will update it's internal structures
+ * @param ok_chunks The ok_chunks
+ */
+ void dataChecked(const BitSet & ok_chunks);
+
+ /// Test if the torrent has existing files, only works the first time a torrent is loaded
+ bool hasExistingFiles() const;
+
+ /// Recreates missing files
+ void recreateMissingFiles();
+
+ /// Set missing files as do not download
+ void dndMissingFiles();
+
+ /// Delete all data files
+ void deleteDataFiles();
+
+ /// Are all not deselected chunks downloaded.
+ bool completed() const;
+
+ /// Set the maximum chunk size for a data check, 0 means alllways check
+ static void setMaxChunkSizeForDataCheck(Uint32 mcs) {max_chunk_size_for_data_check = mcs;}
+
+ /// Get the current disk usage of all the files in this torrent
+ Uint64 diskUsage();
+ signals:
+ /**
+ * Emitted when a range of chunks has been excluded
+ * @param from First chunk in range
+ * @param to Last chunk in range
+ */
+ void excluded(Uint32 from,Uint32 to);
+
+ /**
+ * Emitted when a range of chunks has been included back.
+ * @param from First chunk in range
+ * @param to Last chunk in range
+ */
+ void included(Uint32 from,Uint32 to);
+
+ /**
+ * Emitted when chunks get excluded or included, so
+ * that the statistics can be updated.
+ */
+ void updateStats();
+
+ /**
+ * A corrupted chunk has been found during uploading.
+ * @param chunk The chunk
+ */
+ void corrupted(Uint32 chunk);
+
+ private:
+ void saveIndexFile();
+ void writeIndexFileEntry(Chunk* c);
+ void saveFileInfo();
+ void loadFileInfo();
+ void savePriorityInfo();
+ void loadPriorityInfo();
+
+ private slots:
+ void downloadStatusChanged(TorrentFile* tf,bool download);
+ void downloadPriorityChanged(TorrentFile* tf,Priority newpriority,Priority oldpriority);
+
+ static Uint32 max_chunk_size_for_data_check;
+ };
+
+}
+
+#endif
diff --git a/libktorrent/torrent/chunkselector.cpp b/libktorrent/torrent/chunkselector.cpp
new file mode 100644
index 0000000..b1c42fa
--- /dev/null
+++ b/libktorrent/torrent/chunkselector.cpp
@@ -0,0 +1,185 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <stdlib.h>
+#include <vector>
+#include <algorithm>
+#include <util/log.h>
+#include <util/bitset.h>
+#include "chunkcounter.h"
+#include "chunkselector.h"
+#include "chunkmanager.h"
+#include "downloader.h"
+#include "peerdownloader.h"
+#include "globals.h"
+#include "peer.h"
+#include "peermanager.h"
+
+namespace bt
+{
+ struct RareCmp
+ {
+ ChunkManager & cman;
+ ChunkCounter & cc;
+ bool warmup;
+
+ RareCmp(ChunkManager & cman,ChunkCounter & cc,bool warmup) : cman(cman),cc(cc),warmup(warmup) {}
+
+ bool operator()(Uint32 a,Uint32 b)
+ {
+ // do some sanity checks
+ if (a >= cman.getNumChunks() || b >= cman.getNumChunks())
+ return false;
+
+ // the sorting is done on two criteria, priority and rareness
+ Priority pa = cman.getChunk(a)->getPriority();
+ Priority pb = cman.getChunk(b)->getPriority();
+ if (pa == pb)
+ return normalCmp(a,b); // if both have same priority compare on rareness
+ else if (pa > pb) // pa has priority over pb, so select pa
+ return true;
+ else // pb has priority over pa, so select pb
+ return false;
+ }
+
+ bool normalCmp(Uint32 a,Uint32 b)
+ {
+ // during warmup mode choose most common chunks
+ if (!warmup)
+ return cc.get(a) < cc.get(b);
+ else
+ return cc.get(a) > cc.get(b);
+ }
+ };
+
+ ChunkSelector::ChunkSelector(ChunkManager & cman,Downloader & downer,PeerManager & pman)
+ : cman(cman),downer(downer),pman(pman)
+ {
+ std::vector<Uint32> tmp;
+ for (Uint32 i = 0;i < cman.getNumChunks();i++)
+ {
+ if (!cman.getBitSet().get(i))
+ {
+ tmp.push_back(i);
+ }
+ }
+ std::random_shuffle(tmp.begin(),tmp.end());
+ // std::list does not support random_shuffle so we use a vector as a temporary storage
+ // for the random_shuffle
+ chunks.insert(chunks.begin(),tmp.begin(),tmp.end());
+ sort_timer.update();
+ }
+
+
+ ChunkSelector::~ChunkSelector()
+ {}
+
+
+ bool ChunkSelector::select(PeerDownloader* pd,Uint32 & chunk)
+ {
+ const BitSet & bs = cman.getBitSet();
+
+
+ // sort the chunks every 2 seconds
+ if (sort_timer.getElapsedSinceUpdate() > 2000)
+ {
+ bool warmup = cman.getNumChunks() - cman.chunksLeft() <= 4;
+// dataChecked(bs);
+ chunks.sort(RareCmp(cman,pman.getChunkCounter(),warmup));
+ sort_timer.update();
+ }
+
+ std::list<Uint32>::iterator itr = chunks.begin();
+ while (itr != chunks.end())
+ {
+ Uint32 i = *itr;
+ Chunk* c = cman.getChunk(*itr);
+
+ // if we have the chunk remove it from the list
+ if (bs.get(i))
+ {
+ std::list<Uint32>::iterator tmp = itr;
+ itr++;
+ chunks.erase(tmp);
+ }
+ else
+ {
+ // pd has to have the selected chunk and it needs to be not excluded
+ if (pd->hasChunk(i) && !downer.areWeDownloading(i) &&
+ !c->isExcluded() && !c->isExcludedForDownloading())
+ {
+ // we have a chunk
+ chunk = i;
+ return true;
+ }
+ itr++;
+ }
+ }
+
+ return false;
+ }
+
+ void ChunkSelector::dataChecked(const BitSet & ok_chunks)
+ {
+ for (Uint32 i = 0;i < ok_chunks.getNumBits();i++)
+ {
+ bool in_chunks = std::find(chunks.begin(),chunks.end(),i) != chunks.end();
+ if (in_chunks && ok_chunks.get(i))
+ {
+ // if we have the chunk, remove it from the chunks list
+ chunks.remove(i);
+ }
+ else if (!in_chunks && !ok_chunks.get(i))
+ {
+ // if we don't have the chunk, add it to the list if it wasn't allrready in there
+ chunks.push_back(i);
+ }
+ }
+ }
+
+ void ChunkSelector::reincluded(Uint32 from, Uint32 to)
+ {
+ // lets do a safety check first
+ if (from >= cman.getNumChunks() || to >= cman.getNumChunks())
+ {
+ Out(SYS_DIO|LOG_NOTICE) << "Internal error in chunkselector" << endl;
+ return;
+ }
+
+ for (Uint32 i = from;i <= to;i++)
+ {
+ bool in_chunks = std::find(chunks.begin(),chunks.end(),i) != chunks.end();
+ if (!in_chunks && cman.getChunk(i)->getStatus() != Chunk::ON_DISK)
+ {
+ // Out(SYS_DIO|LOG_DEBUG) << "ChunkSelector::reIncluded " << i << endl;
+ chunks.push_back(i);
+ }
+ }
+ }
+
+ void ChunkSelector::reinsert(Uint32 chunk)
+ {
+ bool in_chunks = std::find(chunks.begin(),chunks.end(),chunk) != chunks.end();
+ if (!in_chunks)
+ chunks.push_back(chunk);
+ }
+
+
+}
+
diff --git a/libktorrent/torrent/chunkselector.h b/libktorrent/torrent/chunkselector.h
new file mode 100644
index 0000000..3ba2f8a
--- /dev/null
+++ b/libktorrent/torrent/chunkselector.h
@@ -0,0 +1,80 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef BTCHUNKSELECTOR_H
+#define BTCHUNKSELECTOR_H
+
+#include <list>
+#include <util/timer.h>
+
+namespace bt
+{
+ class BitSet;
+ class PeerDownloader;
+ class ChunkManager;
+ class Downloader;
+ class PeerManager;
+ /**
+ * @author Joris Guisson
+ *
+ * Selects which Chunks to download.
+ */
+ class ChunkSelector
+ {
+ ChunkManager & cman;
+ Downloader & downer;
+ PeerManager & pman;
+ std::list<Uint32> chunks;
+ Timer sort_timer;
+ public:
+ ChunkSelector(ChunkManager & cman,Downloader & downer,PeerManager &pman);
+ virtual ~ChunkSelector();
+
+ /**
+ * Select which chunk to download for a PeerDownloader.
+ * @param pd The PeerDownloader
+ * @param chunk Index of chunk gets stored here
+ * @return true upon succes, false otherwise
+ */
+ bool select(PeerDownloader* pd,Uint32 & chunk);
+
+ /**
+ * Data has been checked, and these chunks are OK.
+ * @param ok_chunks The ok_chunks
+ */
+ void dataChecked(const BitSet & ok_chunks);
+
+ /**
+ * A range of chunks has been reincluded.
+ * @param from The first chunk
+ * @param to The last chunk
+ */
+ void reincluded(Uint32 from, Uint32 to);
+
+ /**
+ * Reinsert a chunk.
+ * @param chunk The chunk
+ */
+ void reinsert(Uint32 chunk);
+ };
+
+}
+
+#endif
+
diff --git a/libktorrent/torrent/dndfile.cpp b/libktorrent/torrent/dndfile.cpp
new file mode 100644
index 0000000..deace69
--- /dev/null
+++ b/libktorrent/torrent/dndfile.cpp
@@ -0,0 +1,268 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <klocale.h>
+#include <util/file.h>
+#include <util/error.h>
+#include <util/fileops.h>
+#include <util/sha1hash.h>
+#include "dndfile.h"
+
+namespace bt
+{
+ const Uint32 DND_FILE_HDR_MAGIC = 0xD1234567;
+
+ struct DNDFileHeader
+ {
+ Uint32 magic;
+ Uint32 first_size;
+ Uint32 last_size;
+ Uint8 data_sha1[20];
+ };
+
+ DNDFile::DNDFile(const QString & path) : path(path)
+ {}
+
+
+ DNDFile::~DNDFile()
+ {}
+
+ void DNDFile::changePath(const QString & npath)
+ {
+ path = npath;
+ }
+
+ void DNDFile::checkIntegrity()
+ {
+ File fptr;
+ if (!fptr.open(path,"rb"))
+ {
+ create();
+ return;
+ }
+
+ DNDFileHeader hdr;
+ if (fptr.read(&hdr,sizeof(DNDFileHeader)) != sizeof(DNDFileHeader))
+ {
+ create();
+ return;
+ }
+
+ if (hdr.magic != DND_FILE_HDR_MAGIC && bt::FileSize(path) != sizeof(DNDFileHeader) + hdr.first_size + hdr.last_size)
+ {
+ create();
+ return;
+ }
+
+#if 0
+ if (hdr.first_size > 0 || hdr.last_size > 0)
+ {
+ // check hash
+ Uint32 data_size = hdr.first_size + hdr.last_size;
+ Uint8* buf = new Uint8[data_size];
+ if (fptr.read(buf,data_size) != data_size)
+ {
+ delete [] buf;
+ create();
+ return;
+ }
+
+ if (SHA1Hash::generate(buf,data_size) != SHA1Hash(hdr.data_sha1))
+ {
+ delete [] buf;
+ create();
+ return;
+ }
+
+ delete [] buf;
+ }
+#endif
+ }
+
+ void DNDFile::create()
+ {
+ DNDFileHeader hdr;
+ hdr.magic = DND_FILE_HDR_MAGIC;
+ hdr.first_size = 0;
+ hdr.last_size = 0;
+ memset(hdr.data_sha1,0,20);
+
+ File fptr;
+ if (!fptr.open(path,"wb"))
+ throw Error(i18n("Cannot create file %1 : %2").arg(path).arg(fptr.errorString()));
+
+ fptr.write(&hdr,sizeof(DNDFileHeader));
+ fptr.close();
+ }
+
+
+
+ Uint32 DNDFile::readFirstChunk(Uint8* buf,Uint32 off,Uint32 buf_size)
+ {
+ File fptr;
+ if (!fptr.open(path,"rb"))
+ {
+ create();
+ return 0;
+ }
+
+ DNDFileHeader hdr;
+ if (fptr.read(&hdr,sizeof(DNDFileHeader)) != sizeof(DNDFileHeader))
+ {
+ create();
+ return 0;
+ }
+
+ if (hdr.first_size == 0)
+ return 0;
+
+ if (hdr.first_size + off > buf_size)
+ return 0;
+
+ return fptr.read(buf + off,hdr.first_size);
+ }
+
+ Uint32 DNDFile::readLastChunk(Uint8* buf,Uint32 off,Uint32 buf_size)
+ {
+ File fptr;
+ if (!fptr.open(path,"rb"))
+ {
+ create();
+ return 0;
+ }
+
+ DNDFileHeader hdr;
+ if (fptr.read(&hdr,sizeof(DNDFileHeader)) != sizeof(DNDFileHeader))
+ {
+ create();
+ return 0;
+ }
+
+ if (hdr.last_size == 0)
+ return 0;
+
+ if (hdr.last_size + off > buf_size)
+ return 0;
+
+ fptr.seek(File::BEGIN,sizeof(DNDFileHeader) + hdr.first_size);
+ return fptr.read(buf + off,hdr.last_size);
+ }
+
+ void DNDFile::writeFirstChunk(const Uint8* buf,Uint32 fc_size)
+ {
+ File fptr;
+ if (!fptr.open(path,"r+b"))
+ {
+ create();
+ if (!fptr.open(path,"r+b"))
+ {
+ throw Error(i18n("Failed to write first chunk to DND file : %1").arg(fptr.errorString()));
+ }
+ }
+
+ DNDFileHeader hdr;
+ fptr.read(&hdr,sizeof(DNDFileHeader));
+ if (hdr.last_size == 0)
+ {
+ hdr.first_size = fc_size;
+ fptr.seek(File::BEGIN,0);
+ // update hash first
+ // SHA1Hash h = SHA1Hash::generate(buf,fc_size);
+ // memcpy(hdr.data_sha1,h.getData(),20);
+ // write header
+ fptr.write(&hdr,sizeof(DNDFileHeader));
+ // write data
+ fptr.write(buf,fc_size);
+ }
+ else
+ {
+ hdr.first_size = fc_size;
+ Uint8* tmp = new Uint8[hdr.first_size + hdr.last_size];
+ try
+ {
+
+ // put everything in tmp buf
+ memcpy(tmp,buf,hdr.first_size);
+ fptr.seek(File::BEGIN,sizeof(DNDFileHeader) + hdr.first_size);
+ fptr.read(tmp + hdr.first_size,hdr.last_size);
+
+ // update the hash of the header
+ // SHA1Hash h = SHA1Hash::generate(tmp,hdr.first_size + hdr.last_size);
+ // memcpy(hdr.data_sha1,h.getData(),20);
+
+ // write header + data
+ fptr.seek(File::BEGIN,0);
+ fptr.write(&hdr,sizeof(DNDFileHeader));
+ fptr.write(tmp,hdr.first_size + hdr.last_size);
+ delete [] tmp;
+
+ }
+ catch (...)
+ {
+ delete [] tmp;
+ throw;
+ }
+ }
+ }
+
+
+ void DNDFile::writeLastChunk(const Uint8* buf,Uint32 lc_size)
+ {
+ File fptr;
+ if (!fptr.open(path,"r+b"))
+ {
+ create();
+ if (!fptr.open(path,"r+b"))
+ {
+ throw Error(i18n("Failed to write last chunk to DND file : %1").arg(fptr.errorString()));
+ }
+ }
+
+ DNDFileHeader hdr;
+ fptr.read(&hdr,sizeof(DNDFileHeader));
+ hdr.last_size = lc_size;
+ Uint8* tmp = new Uint8[hdr.first_size + hdr.last_size];
+ try
+ {
+ // put everything in tmp buf
+ memcpy(tmp + hdr.first_size,buf,lc_size);
+ if (hdr.first_size > 0)
+ {
+ fptr.seek(File::BEGIN,sizeof(DNDFileHeader));
+ fptr.read(tmp,hdr.first_size);
+ }
+
+ // update the hash of the header
+ // SHA1Hash h = SHA1Hash::generate(tmp,hdr.first_size + hdr.last_size);
+ // memcpy(hdr.data_sha1,h.getData(),20);
+
+ // write header + data
+ fptr.seek(File::BEGIN,0);
+ fptr.write(&hdr,sizeof(DNDFileHeader));
+ fptr.write(tmp,hdr.first_size + hdr.last_size);
+ delete [] tmp;
+ }
+ catch (...)
+ {
+ delete [] tmp;
+ throw;
+ }
+ }
+
+}
diff --git a/libktorrent/torrent/dndfile.h b/libktorrent/torrent/dndfile.h
new file mode 100644
index 0000000..a7a7e7b
--- /dev/null
+++ b/libktorrent/torrent/dndfile.h
@@ -0,0 +1,89 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef BTDNDFILE_H
+#define BTDNDFILE_H
+
+#include <qstring.h>
+#include <util/constants.h>
+
+namespace bt
+{
+
+
+ /**
+ * @author Joris Guisson <joris.guisson@gmail.com>
+ *
+ * Special file where we keep the first and last chunk of a file which is marked as do not download.
+ * THe first and last chunk of a file will most certainly be partial chunks.
+ */
+ class DNDFile
+ {
+ public:
+ DNDFile(const QString & path);
+ virtual ~DNDFile();
+
+ /// Change the path of the file
+ void changePath(const QString & npath);
+
+ /**
+ * CHeck integrity of the file, create it if it doesn't exist.
+ */
+ void checkIntegrity();
+
+ /**
+ * Read the (partial)first chunk into a buffer.
+ * @param buf The buffer
+ * @param off OFfset into the buffer
+ * @param buf_size Size of the buffer
+ */
+ Uint32 readFirstChunk(Uint8* buf,Uint32 off,Uint32 buf_size);
+
+ /**
+ * Read the (partial)last chunk into a buffer.
+ * @param buf The buffer
+ * @param off OFfset into the buffer
+ * @param buf_size Size of the buffer
+ */
+ Uint32 readLastChunk(Uint8* buf,Uint32 off,Uint32 buf_size);
+
+ /**
+ * Write the partial first chunk.
+ * @param buf The buffer
+ * @param fc_size Size to write
+ */
+ void writeFirstChunk(const Uint8* buf,Uint32 fc_size);
+
+ /**
+ * Write the partial last chunk.
+ * @param buf The buffer
+ * @param lc_size Size to write
+ */
+ void writeLastChunk(const Uint8* buf,Uint32 lc_size);
+
+ private:
+ void create();
+
+ private:
+ QString path;
+ };
+
+}
+
+#endif
diff --git a/libktorrent/torrent/downloadcap.cpp b/libktorrent/torrent/downloadcap.cpp
new file mode 100644
index 0000000..73e0cbb
--- /dev/null
+++ b/libktorrent/torrent/downloadcap.cpp
@@ -0,0 +1,43 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#if 0
+#include <math.h>
+#include <util/log.h>
+#include "downloadcap.h"
+#include "globals.h"
+
+namespace bt
+{
+ DownloadCap DownloadCap::self;
+
+ const Uint32 SLOT_SIZE = 5*1024;
+
+ DownloadCap::DownloadCap() : Cap(true)
+ {
+ }
+
+ DownloadCap::~ DownloadCap()
+ {
+ }
+
+
+
+}
+#endif
diff --git a/libktorrent/torrent/downloadcap.h b/libktorrent/torrent/downloadcap.h
new file mode 100644
index 0000000..2bda73c
--- /dev/null
+++ b/libktorrent/torrent/downloadcap.h
@@ -0,0 +1,48 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef BTDOWNLOADCAP_H
+#define BTDOWNLOADCAP_H
+
+#if 0
+#include <qvaluelist.h>
+#include <util/timer.h>
+#include "globals.h"
+#include "cap.h"
+
+namespace bt
+{
+
+ /**
+ * @author Joris Guisson
+ */
+ class DownloadCap : public Cap
+ {
+ static DownloadCap self;
+
+ DownloadCap();
+ public:
+ ~DownloadCap();
+
+ static DownloadCap & instance() {return self;}
+ };
+
+}
+#endif
+#endif
diff --git a/libktorrent/torrent/downloader.cpp b/libktorrent/torrent/downloader.cpp
new file mode 100644
index 0000000..b8acdc7
--- /dev/null
+++ b/libktorrent/torrent/downloader.cpp
@@ -0,0 +1,688 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <util/file.h>
+#include <util/log.h>
+#include "downloader.h"
+#include "chunkmanager.h"
+#include "torrent.h"
+#include "peermanager.h"
+#include <util/error.h>
+#include "chunkdownload.h"
+#include <util/sha1hash.h>
+#include <util/array.h>
+#include "peer.h"
+#include "piece.h"
+#include "peerdownloader.h"
+#include <interfaces/functions.h>
+#include <interfaces/monitorinterface.h>
+#include "packetwriter.h"
+#include "chunkselector.h"
+#include "ipblocklist.h"
+#include "ktversion.h"
+
+namespace bt
+{
+
+
+
+ Downloader::Downloader(Torrent & tor,PeerManager & pman,ChunkManager & cman)
+ : tor(tor),pman(pman),cman(cman),downloaded(0),tmon(0)
+ {
+ chunk_selector = new ChunkSelector(cman,*this,pman);
+ Uint64 total = tor.getFileLength();
+ downloaded = (total - cman.bytesLeft());
+ curr_chunks_downloaded = 0;
+ unnecessary_data = 0;
+
+ current_chunks.setAutoDelete(true);
+ connect(&pman,SIGNAL(newPeer(Peer* )),this,SLOT(onNewPeer(Peer* )));
+ connect(&pman,SIGNAL(peerKilled(Peer* )),this,SLOT(onPeerKilled(Peer*)));
+ }
+
+
+ Downloader::~Downloader()
+ {
+ delete chunk_selector;
+ }
+
+ void Downloader::pieceRecieved(const Piece & p)
+ {
+ if (cman.completed())
+ return;
+
+ ChunkDownload* cd = 0;
+
+ for (CurChunkItr j = current_chunks.begin();j != current_chunks.end();++j)
+ {
+ if (p.getIndex() != j->first)
+ continue;
+
+ cd = j->second;
+ break;
+ }
+
+ if (!cd)
+ {
+ unnecessary_data += p.getLength();
+ Out(SYS_DIO|LOG_DEBUG) <<
+ "Unnecessary piece, total unnecessary data : " << kt::BytesToString(unnecessary_data) << endl;
+ return;
+ }
+
+ // if the chunk is not in memory, reload it
+ if (cd->getChunk()->getStatus() == Chunk::ON_DISK)
+ {
+ cman.prepareChunk(cd->getChunk(),true);
+ }
+
+ bool ok = false;
+
+ if (cd->piece(p,ok))
+ {
+ if (tmon)
+ tmon->downloadRemoved(cd);
+
+ if (ok)
+ downloaded += p.getLength();
+
+ if (!finished(cd))
+ {
+ // if the chunk fails don't count the bytes downloaded
+ if (cd->getChunk()->getSize() > downloaded)
+ downloaded = 0;
+ else
+ downloaded -= cd->getChunk()->getSize();
+ }
+ current_chunks.erase(p.getIndex());
+ update(); // run an update to assign new pieces
+ }
+ else
+ {
+ if (ok)
+ downloaded += p.getLength();
+
+ // save to disk again, if it is idle
+ if (cd->isIdle() && cd->getChunk()->getStatus() == Chunk::MMAPPED)
+ {
+ cman.saveChunk(cd->getChunk()->getIndex(),false);
+ }
+ }
+
+ if (!ok)
+ {
+ unnecessary_data += p.getLength();
+ Out(SYS_DIO|LOG_DEBUG) <<
+ "Unnecessary piece, total unnecessary data : " << kt::BytesToString(unnecessary_data) << endl;
+ }
+ }
+
+ void Downloader::update()
+ {
+ if (cman.completed())
+ return;
+
+ /*
+ Normal update should now handle all modes properly.
+ */
+ normalUpdate();
+
+ // now see if there aren't any timed out pieces
+ for (Uint32 i = 0;i < pman.getNumConnectedPeers();i++)
+ {
+ Peer* p = pman.getPeer(i);
+ p->getPeerDownloader()->checkTimeouts();
+ }
+ }
+
+
+ void Downloader::normalUpdate()
+ {
+ for (CurChunkItr j = current_chunks.begin();j != current_chunks.end();++j)
+ {
+ ChunkDownload* cd = j->second;
+ if (cd->isIdle()) // idle chunks do not need to be in memory
+ {
+ Chunk* c = cd->getChunk();
+ if (c->getStatus() == Chunk::MMAPPED)
+ {
+ cman.saveChunk(cd->getChunk()->getIndex(),false);
+ }
+ }
+ else if (cd->isChoked())
+ {
+ cd->releaseAllPDs();
+ Chunk* c = cd->getChunk();
+ if (c->getStatus() == Chunk::MMAPPED)
+ {
+ cman.saveChunk(cd->getChunk()->getIndex(),false);
+ }
+ }
+ else if (cd->needsToBeUpdated())
+ {
+ cd->update();
+ }
+ }
+
+ for (Uint32 i = 0; i < pman.getNumConnectedPeers();++i)
+ {
+ PeerDownloader* pd = pman.getPeer(i)->getPeerDownloader();
+
+ if (pd->isNull())
+ continue;
+
+ bool ok =
+ (pd->getNumGrabbed() < pd->getMaxChunkDownloads() ||
+ pd->isNearlyDone()) &&
+ pd->canAddRequest();
+
+
+ if (ok)
+ {
+ if (!pd->isChoked())
+ downloadFrom(pd);
+
+ pd->setNearlyDone(false);
+ }
+ }
+ }
+
+ Uint32 Downloader::maxMemoryUsage()
+ {
+ Uint32 max = 1024 * 1024;
+ switch (mem_usage)
+ {
+ case 1: // Medium
+ max *= 60; // 60 MB
+ break;
+ case 2: // High
+ max *= 80; // 90 MB
+ break;
+ case 0: // LOW
+ default:
+ max *= 40; // 30 MB
+ break;
+ }
+ return max;
+ }
+
+ Uint32 Downloader::numNonIdle()
+ {
+ Uint32 num_non_idle = 0;
+ for (CurChunkItr j = current_chunks.begin();j != current_chunks.end();++j)
+ {
+ ChunkDownload* cd = j->second;
+ if (!cd->isIdle())
+ num_non_idle++;
+ }
+ return num_non_idle;
+ }
+
+ ChunkDownload* Downloader::selectCD(PeerDownloader* pd,Uint32 num)
+ {
+ ChunkDownload* sel = 0;
+ Uint32 sel_left = 0xFFFFFFFF;
+
+ for (CurChunkItr j = current_chunks.begin();j != current_chunks.end();++j)
+ {
+ ChunkDownload* cd = j->second;
+ if (pd->isChoked() || !pd->hasChunk(cd->getChunk()->getIndex()))
+ continue;
+
+ if (cd->getNumDownloaders() == num)
+ {
+ // lets favor the ones which are nearly finished
+ if (!sel || cd->getTotalPieces() - cd->getPiecesDownloaded() < sel_left)
+ {
+ sel = cd;
+ sel_left = sel->getTotalPieces() - sel->getPiecesDownloaded();
+ }
+ }
+ }
+ return sel;
+ }
+
+ bool Downloader::findDownloadForPD(PeerDownloader* pd,bool warmup)
+ {
+ ChunkDownload* sel = 0;
+
+ // first see if there are ChunkDownload's which need a PeerDownloader
+ sel = selectCD(pd,0);
+
+ if (!sel && warmup)
+ {
+ // if we couldn't find one, try to select another
+ // which only has one downloader
+ // so that during warmup, there are at the most 2 downloaders
+ // assigned to one peer
+ sel = selectCD(pd,1);
+ }
+
+ if (sel)
+ {
+ // if it is on disk, reload it
+ if (sel->getChunk()->getStatus() == Chunk::ON_DISK)
+ cman.prepareChunk(sel->getChunk(),true);
+
+ sel->assignPeer(pd);
+ return true;
+ }
+
+ return false;
+ }
+
+ ChunkDownload* Downloader::selectWorst(PeerDownloader* pd)
+ {
+ ChunkDownload* cdmin = NULL;
+ for (CurChunkItr j = current_chunks.begin();j != current_chunks.end();++j)
+ {
+ ChunkDownload* cd = j->second;
+ if (!pd->hasChunk(cd->getChunk()->getIndex()) || cd->containsPeer(pd))
+ continue;
+
+ if (!cdmin)
+ cdmin = cd;
+ else if (cd->getDownloadSpeed() < cdmin->getDownloadSpeed())
+ cdmin = cd;
+ else if (cd->getNumDownloaders() < cdmin->getNumDownloaders())
+ cdmin = cd;
+ }
+
+ return cdmin;
+ }
+
+ void Downloader::downloadFrom(PeerDownloader* pd)
+ {
+ // calculate the max memory usage
+ Uint32 max = maxMemoryUsage();
+ // calculate number of non idle chunks
+ Uint32 num_non_idle = numNonIdle();
+
+ // first see if we can use an existing dowload
+ if (findDownloadForPD(pd,cman.getNumChunks() - cman.chunksLeft() <= 4))
+ return;
+
+ bool limit_exceeded = num_non_idle * tor.getChunkSize() >= max;
+
+ Uint32 chunk = 0;
+ if (!limit_exceeded && chunk_selector->select(pd,chunk))
+ {
+ Chunk* c = cman.getChunk(chunk);
+ if (cman.prepareChunk(c))
+ {
+ ChunkDownload* cd = new ChunkDownload(c);
+ current_chunks.insert(chunk,cd);
+ cd->assignPeer(pd);
+ if (tmon)
+ tmon->downloadStarted(cd);
+ }
+ }
+ else if (pd->getNumGrabbed() == 0)
+ {
+ // If the peer hasn't got a chunk we want,
+ ChunkDownload *cdmin = selectWorst(pd);
+
+ if (cdmin)
+ {
+ // if it is on disk, reload it
+ if (cdmin->getChunk()->getStatus() == Chunk::ON_DISK)
+ {
+ cman.prepareChunk(cdmin->getChunk(),true);
+ }
+
+ cdmin->assignPeer(pd);
+ }
+ }
+ }
+
+
+ bool Downloader::areWeDownloading(Uint32 chunk) const
+ {
+ return current_chunks.find(chunk) != 0;
+ }
+
+ void Downloader::onNewPeer(Peer* peer)
+ {
+ PeerDownloader* pd = peer->getPeerDownloader();
+ connect(pd,SIGNAL(downloaded(const Piece& )),
+ this,SLOT(pieceRecieved(const Piece& )));
+ }
+
+ void Downloader::onPeerKilled(Peer* peer)
+ {
+ PeerDownloader* pd = peer->getPeerDownloader();
+ if (pd)
+ {
+ for (CurChunkItr i = current_chunks.begin();i != current_chunks.end();++i)
+ {
+ ChunkDownload* cd = i->second;
+ cd->peerKilled(pd);
+ }
+ }
+ }
+
+ bool Downloader::finished(ChunkDownload* cd)
+ {
+ Chunk* c = cd->getChunk();
+ // verify the data
+ SHA1Hash h;
+ if (cd->usingContinuousHashing())
+ h = cd->getHash();
+ else
+ h = SHA1Hash::generate(c->getData(),c->getSize());
+
+ if (tor.verifyHash(h,c->getIndex()))
+ {
+ // hash ok so save it
+ try
+ {
+ cman.saveChunk(c->getIndex());
+ Out(SYS_GEN|LOG_NOTICE) << "Chunk " << c->getIndex() << " downloaded " << endl;
+ // tell everybody we have the Chunk
+ for (Uint32 i = 0;i < pman.getNumConnectedPeers();i++)
+ {
+ pman.getPeer(i)->getPacketWriter().sendHave(c->getIndex());
+ }
+ }
+ catch (Error & e)
+ {
+ Out(SYS_DIO|LOG_IMPORTANT) << "Error " << e.toString() << endl;
+ emit ioError(e.toString());
+ return false;
+ }
+ }
+ else
+ {
+ Out(SYS_GEN|LOG_IMPORTANT) << "Hash verification error on chunk " << c->getIndex() << endl;
+ Out(SYS_GEN|LOG_IMPORTANT) << "Is : " << h << endl;
+ Out(SYS_GEN|LOG_IMPORTANT) << "Should be : " << tor.getHash(c->getIndex()) << endl;
+
+ cman.resetChunk(c->getIndex());
+ chunk_selector->reinsert(c->getIndex());
+ Uint32 pid;
+ if (cd->getOnlyDownloader(pid))
+ {
+ Peer* p = pman.findPeer(pid);
+ if (!p)
+ return false;
+ QString IP(p->getIPAddresss());
+ Out(SYS_GEN|LOG_NOTICE) << "Peer " << IP << " sent bad data" << endl;
+ IPBlocklist & ipfilter = IPBlocklist::instance();
+ ipfilter.insert( IP );
+ p->kill();
+ }
+ return false;
+ }
+ return true;
+ }
+
+ void Downloader::clearDownloads()
+ {
+ for (CurChunkItr i = current_chunks.begin();i != current_chunks.end();++i)
+ {
+ Uint32 ch = i->first;
+ Chunk* c = i->second->getChunk();
+ if (c->getStatus() == Chunk::MMAPPED)
+ cman.saveChunk(ch,false);
+
+ c->setStatus(Chunk::NOT_DOWNLOADED);
+ }
+ current_chunks.clear();
+ }
+
+ Uint32 Downloader::downloadRate() const
+ {
+ // sum of the download rate of each peer
+ Uint32 rate = 0;
+ for (Uint32 i = 0;i < pman.getNumConnectedPeers();i++)
+ {
+ Peer* p = pman.getPeer(i);
+ rate += p->getDownloadRate();
+ }
+ return rate;
+ }
+
+ void Downloader::setMonitor(kt::MonitorInterface* tmo)
+ {
+ tmon = tmo;
+ if (!tmon)
+ return;
+
+ for (CurChunkItr i = current_chunks.begin();i != current_chunks.end();++i)
+ {
+ ChunkDownload* cd = i->second;
+ tmon->downloadStarted(cd);
+ }
+ }
+
+
+
+ void Downloader::saveDownloads(const QString & file)
+ {
+ File fptr;
+ if (!fptr.open(file,"wb"))
+ return;
+
+ // Save all the current downloads to a file
+ CurrentChunksHeader hdr;
+ hdr.magic = CURRENT_CHUNK_MAGIC;
+ hdr.major = kt::MAJOR;
+ hdr.minor = kt::MINOR;
+ hdr.num_chunks = current_chunks.count();
+ fptr.write(&hdr,sizeof(CurrentChunksHeader));
+
+// Out() << "sizeof(CurrentChunksHeader)" << sizeof(CurrentChunksHeader) << endl;
+ Out() << "Saving " << current_chunks.count() << " chunk downloads" << endl;
+ for (CurChunkItr i = current_chunks.begin();i != current_chunks.end();++i)
+ {
+ ChunkDownload* cd = i->second;
+ cd->save(fptr);
+ }
+ }
+
+ void Downloader::loadDownloads(const QString & file)
+ {
+ // don't load stuff if download is finished
+ if (cman.completed())
+ return;
+
+ // Load all partial downloads
+ File fptr;
+ if (!fptr.open(file,"rb"))
+ return;
+
+ // recalculate downloaded bytes
+ downloaded = (tor.getFileLength() - cman.bytesLeft());
+
+ CurrentChunksHeader chdr;
+ fptr.read(&chdr,sizeof(CurrentChunksHeader));
+ if (chdr.magic != CURRENT_CHUNK_MAGIC)
+ {
+ Out() << "Warning : current_chunks file corrupted" << endl;
+ return;
+ }
+
+ Out() << "Loading " << chdr.num_chunks << " active chunk downloads" << endl;
+ for (Uint32 i = 0;i < chdr.num_chunks;i++)
+ {
+ ChunkDownloadHeader hdr;
+ // first read header
+ fptr.read(&hdr,sizeof(ChunkDownloadHeader));
+ Out() << "Loading chunk " << hdr.index << endl;
+ if (hdr.index >= tor.getNumChunks())
+ {
+ Out() << "Warning : current_chunks file corrupted, invalid index " << hdr.index << endl;
+ return;
+ }
+
+ if (!cman.getChunk(hdr.index) || current_chunks.contains(hdr.index))
+ {
+ Out() << "Illegal chunk " << hdr.index << endl;
+ return;
+ }
+ Chunk* c = cman.getChunk(hdr.index);
+ if (!c->isExcluded() && cman.prepareChunk(c))
+ {
+ ChunkDownload* cd = new ChunkDownload(c);
+ bool ret = false;
+ try
+ {
+ ret = cd->load(fptr,hdr);
+ }
+ catch (...)
+ {
+ ret = false;
+ }
+
+ if (!ret)
+ {
+ delete cd;
+ }
+ else
+ {
+ current_chunks.insert(hdr.index,cd);
+ downloaded += cd->bytesDownloaded();
+
+ if (tmon)
+ tmon->downloadStarted(cd);
+ }
+ }
+ }
+
+ // reset curr_chunks_downloaded to 0
+ curr_chunks_downloaded = 0;
+ }
+
+ Uint32 Downloader::getDownloadedBytesOfCurrentChunksFile(const QString & file)
+ {
+ // Load all partial downloads
+ File fptr;
+ if (!fptr.open(file,"rb"))
+ return 0;
+
+ // read the number of chunks
+ CurrentChunksHeader chdr;
+ fptr.read(&chdr,sizeof(CurrentChunksHeader));
+ if (chdr.magic != CURRENT_CHUNK_MAGIC)
+ {
+ Out() << "Warning : current_chunks file corrupted" << endl;
+ return 0;
+ }
+ Uint32 num_bytes = 0;
+
+ // load all chunks and calculate how much is downloaded
+ for (Uint32 i = 0;i < chdr.num_chunks;i++)
+ {
+ // read the chunkdownload header
+ ChunkDownloadHeader hdr;
+ fptr.read(&hdr,sizeof(ChunkDownloadHeader));
+
+ Chunk* c = cman.getChunk(hdr.index);
+ if (!c)
+ return num_bytes;
+
+ Uint32 last_size = c->getSize() % MAX_PIECE_LEN;
+ if (last_size == 0)
+ last_size = MAX_PIECE_LEN;
+
+ // create the bitset and read it
+ BitSet bs(hdr.num_bits);
+ fptr.read(bs.getData(),bs.getNumBytes());
+
+ for (Uint32 j = 0;j < hdr.num_bits;j++)
+ {
+ if (bs.get(j))
+ num_bytes += j == hdr.num_bits - 1 ?
+ last_size : MAX_PIECE_LEN;
+ }
+
+ if (hdr.buffered)
+ fptr.seek(File::CURRENT,c->getSize());
+ }
+ curr_chunks_downloaded = num_bytes;
+ return num_bytes;
+ }
+
+ bool Downloader::isFinished() const
+ {
+ return cman.completed();
+ }
+
+ void Downloader::onExcluded(Uint32 from,Uint32 to)
+ {
+ for (Uint32 i = from;i <= to;i++)
+ {
+ ChunkDownload* cd = current_chunks.find(i);
+ // let only seed chunks finish
+ if (!cd || cman.getChunk(i)->getPriority() == ONLY_SEED_PRIORITY)
+ continue;
+
+ cd->cancelAll();
+ cd->releaseAllPDs();
+ if (tmon)
+ tmon->downloadRemoved(cd);
+ current_chunks.erase(i);
+ cman.resetChunk(i); // reset chunk it is not fully downloaded yet
+ }
+ }
+
+ void Downloader::onIncluded(Uint32 from,Uint32 to)
+ {
+ chunk_selector->reincluded(from,to);
+ }
+
+ void Downloader::corrupted(Uint32 chunk)
+ {
+ chunk_selector->reinsert(chunk);
+ }
+
+ Uint32 Downloader::mem_usage = 0;
+
+ void Downloader::setMemoryUsage(Uint32 m)
+ {
+ mem_usage = m;
+// PeerDownloader::setMemoryUsage(m);
+ }
+
+ void Downloader::dataChecked(const BitSet & ok_chunks)
+ {
+ for (Uint32 i = 0;i < ok_chunks.getNumBits();i++)
+ {
+ ChunkDownload* cd = current_chunks.find(i);
+ if (ok_chunks.get(i) && cd)
+ {
+ // we have a chunk and we are downloading it so kill it
+ cd->releaseAllPDs();
+ if (tmon)
+ tmon->downloadRemoved(cd);
+
+ current_chunks.erase(i);
+ }
+ }
+ chunk_selector->dataChecked(ok_chunks);
+ }
+
+ void Downloader::recalcDownloaded()
+ {
+ Uint64 total = tor.getFileLength();
+ downloaded = (total - cman.bytesLeft());
+ }
+}
+
+#include "downloader.moc"
diff --git a/libktorrent/torrent/downloader.h b/libktorrent/torrent/downloader.h
new file mode 100644
index 0000000..5b39eeb
--- /dev/null
+++ b/libktorrent/torrent/downloader.h
@@ -0,0 +1,221 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef BTDOWNLOADER_H
+#define BTDOWNLOADER_H
+
+#include <qobject.h>
+#include <util/ptrmap.h>
+#include "globals.h"
+
+namespace kt
+{
+ class MonitorInterface;
+}
+
+
+namespace bt
+{
+ class BitSet;
+ class Torrent;
+ class ChunkManager;
+ class PeerManager;
+ class Peer;
+ class Chunk;
+ class ChunkDownload;
+ class PeerDownloader;
+ class Piece;
+ class Request;
+ class ChunkSelector;
+
+ typedef PtrMap<Uint32,ChunkDownload>::iterator CurChunkItr;
+ typedef PtrMap<Uint32,ChunkDownload>::const_iterator CurChunkCItr;
+
+ #define CURRENT_CHUNK_MAGIC 0xABCDEF00
+
+ struct CurrentChunksHeader
+ {
+ Uint32 magic; // CURRENT_CHUNK_MAGIC
+ Uint32 major;
+ Uint32 minor;
+ Uint32 num_chunks;
+ };
+
+ /**
+ * @author Joris Guisson
+ * @brief Manages the downloading
+ *
+ * This class manages the downloading of the file. It should
+ * regurarly be updated.
+ */
+ class Downloader : public QObject
+ {
+ Q_OBJECT
+
+ public:
+ /**
+ * Constructor.
+ * @param tor The Torrent
+ * @param pman The PeerManager
+ * @param cman The ChunkManager
+ */
+ Downloader(Torrent & tor,PeerManager & pman,ChunkManager & cman);
+ virtual ~Downloader();
+
+ /// Get the number of bytes we have downloaded
+ Uint64 bytesDownloaded() const {return downloaded + curr_chunks_downloaded;}
+
+ /// Get the current dowload rate
+ Uint32 downloadRate() const;
+
+ /// Get the number of chunks we are dowloading
+ Uint32 numActiveDownloads() const {return current_chunks.count();}
+
+ /// See if the download is finished.
+ bool isFinished() const;
+
+ /**
+ * Clear all downloads. Deletes all active downloads.
+ */
+ void clearDownloads();
+
+ CurChunkCItr beginDownloads() const {return current_chunks.begin();}
+ CurChunkCItr endDownloads() const {return current_chunks.end();}
+
+ /**
+ * See if we are downloading a Chunk
+ * @param chunk ID of Chunk
+ * @return true if we are, false if not
+ */
+ bool areWeDownloading(Uint32 chunk) const;
+
+ /**
+ * Save the current downloads.
+ * @param file The file to save to
+ */
+ void saveDownloads(const QString & file);
+
+ /**
+ * Load the current downloads.
+ * @param file The file to load from
+ */
+ void loadDownloads(const QString & file);
+
+ /**
+ * Get the number of bytes already downloaded in the current_chunks file.
+ * @param file The path of the current_chunks file
+ * @return The bytes already downloading
+ */
+ Uint32 getDownloadedBytesOfCurrentChunksFile(const QString & file);
+
+ /**
+ * A corrupted chunk has been detected, make sure we redownload it.
+ * @param chunk The chunk
+ */
+ void corrupted(Uint32 chunk);
+ public slots:
+ /**
+ * Update the downloader.
+ */
+ void update();
+
+ /**
+ * We got a new connection.
+ * @param peer The Peer
+ */
+ void onNewPeer(Peer* peer);
+
+ /**
+ * A Peer has disconnected.
+ * @param peer The Peer
+ */
+ void onPeerKilled(Peer* peer);
+
+ /**
+ * Set the TorrentMonitor.
+ * @param tmo
+ */
+ void setMonitor(kt::MonitorInterface* tmo);
+
+ static void setMemoryUsage(Uint32 m);
+
+ /**
+ * Data has been checked, and these chunks are OK.
+ * @param ok_chunks The ok_chunks
+ */
+ void dataChecked(const BitSet & ok_chunks);
+
+ /**
+ * Recalculate the number of bytes downloaded.
+ */
+ void recalcDownloaded();
+
+ private slots:
+ void pieceRecieved(const Piece & p);
+ bool finished(ChunkDownload* c);
+
+ /**
+ * Kill all ChunkDownload's which have been excluded.
+ * @param from First chunk of range
+ * @param to Last chunk of range
+ */
+ void onExcluded(Uint32 from,Uint32 to);
+
+ /**
+ * Make sure chunk selector is back OK, when chunks are included back again.
+ * @param from First chunk
+ * @param to Last chunk
+ */
+ void onIncluded(Uint32 from,Uint32 to);
+
+ signals:
+ /**
+ * An error occurred while we we're writing or reading from disk.
+ * @param msg Message
+ */
+ void ioError(const QString & msg);
+
+ private:
+ void downloadFrom(PeerDownloader* pd);
+ void normalUpdate();
+ Uint32 maxMemoryUsage();
+ Uint32 numNonIdle();
+ bool findDownloadForPD(PeerDownloader* pd,bool warmup);
+ ChunkDownload* selectCD(PeerDownloader* pd,Uint32 num);
+ ChunkDownload* selectWorst(PeerDownloader* pd);
+
+ private:
+ Torrent & tor;
+ PeerManager & pman;
+ ChunkManager & cman;
+ Uint64 downloaded;
+ Uint64 curr_chunks_downloaded;
+ Uint64 unnecessary_data;
+ PtrMap<Uint32,ChunkDownload> current_chunks;
+ ChunkSelector* chunk_selector;
+
+ kt::MonitorInterface* tmon;
+ static Uint32 mem_usage;
+ };
+
+
+
+}
+
+#endif
diff --git a/libktorrent/torrent/globals.cpp b/libktorrent/torrent/globals.cpp
new file mode 100644
index 0000000..0221c17
--- /dev/null
+++ b/libktorrent/torrent/globals.cpp
@@ -0,0 +1,97 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+
+#include <util/log.h>
+#include <util/error.h>
+#include <net/portlist.h>
+#include <kademlia/dht.h>
+
+#include "globals.h"
+#include "server.h"
+
+namespace bt
+{
+
+ Globals* Globals::inst = 0;
+
+ Globals::Globals()
+ {
+ plist = new net::PortList();
+ debug_mode = false;
+ log = new Log();
+ server = 0;
+ dh_table = new dht::DHT();
+ }
+
+ Globals::~ Globals()
+ {
+ delete server;
+ delete log;
+ delete dh_table;
+ delete plist;
+ }
+
+ Globals & Globals::instance()
+ {
+ if (!inst)
+ inst = new Globals();
+ return *inst;
+ }
+
+ void Globals::cleanup()
+ {
+ delete inst;
+ inst = 0;
+ }
+
+ void Globals::initLog(const QString & file)
+ {
+ log->setOutputFile(file);
+ log->setOutputToConsole(debug_mode);
+ }
+
+ void Globals::initServer(Uint16 port)
+ {
+ if (server)
+ {
+ delete server;
+ server = 0;
+ }
+
+ server = new Server(port);
+ }
+
+ void Globals::shutdownServer()
+ {
+ if (server)
+ {
+ server->close();
+ }
+ }
+
+ Log& Globals::getLog(unsigned int arg)
+ {
+ log->setFilter(arg);
+ return *log;
+ }
+
+
+}
+
diff --git a/libktorrent/torrent/globals.h b/libktorrent/torrent/globals.h
new file mode 100644
index 0000000..7cfe3f5
--- /dev/null
+++ b/libktorrent/torrent/globals.h
@@ -0,0 +1,78 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef BTGLOBALS_H
+#define BTGLOBALS_H
+
+#include <util/constants.h>
+
+class QString;
+
+namespace net
+{
+ class PortList;
+}
+
+namespace dht
+{
+ class DHTBase;
+}
+
+namespace bt
+{
+ class Log;
+ class Server;
+
+
+
+ class Globals
+ {
+ public:
+ virtual ~Globals();
+
+ void initLog(const QString & file);
+ void initServer(Uint16 port);
+ void setDebugMode(bool on) {debug_mode = on;}
+ bool isDebugModeSet() const {return debug_mode;}
+ void shutdownServer();
+
+ Log & getLog(unsigned int arg);
+ Server & getServer() {return *server;}
+ dht::DHTBase & getDHT() {return *dh_table;}
+ net::PortList & getPortList() {return *plist;}
+
+ static Globals & instance();
+ static void cleanup();
+ private:
+ Globals();
+
+ bool debug_mode;
+ Log* log;
+ Server* server;
+ dht::DHTBase* dh_table;
+ net::PortList* plist;
+
+ friend Log& Out(unsigned int arg);
+
+ static Globals* inst;
+
+ };
+}
+
+#endif
diff --git a/libktorrent/torrent/httptracker.cpp b/libktorrent/torrent/httptracker.cpp
new file mode 100644
index 0000000..b220bc0
--- /dev/null
+++ b/libktorrent/torrent/httptracker.cpp
@@ -0,0 +1,462 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <config.h>
+
+#include <kurl.h>
+#include <klocale.h>
+#include <qhostaddress.h>
+#include <util/log.h>
+#include <util/functions.h>
+#include <util/error.h>
+#include <util/waitjob.h>
+#include <interfaces/exitoperation.h>
+#include <kio/job.h>
+#include <kio/netaccess.h>
+#include <kio/scheduler.h>
+#include "bnode.h"
+#include "httptracker.h"
+#include "torrentcontrol.h"
+#include "bdecoder.h"
+#include "peermanager.h"
+#include "server.h"
+#include "globals.h"
+#include "settings.h"
+
+
+using namespace kt;
+
+namespace bt
+{
+
+ HTTPTracker::HTTPTracker(const KURL & url,kt::TorrentInterface* tor,const PeerID & id,int tier)
+ : Tracker(url,tor,id,tier)
+ {
+ active_job = 0;
+
+ interval = 5 * 60; // default interval 5 minutes
+ failures = 0;
+ seeders = leechers = 0;
+ }
+
+
+ HTTPTracker::~HTTPTracker()
+ {
+ }
+
+ void HTTPTracker::start()
+ {
+ event = "started";
+ doRequest();
+ }
+
+ void HTTPTracker::stop(WaitJob* wjob)
+ {
+ if (!started)
+ return;
+
+ event = "stopped";
+ doRequest(wjob);
+ started = false;
+ }
+
+ void HTTPTracker::completed()
+ {
+ event = "completed";
+ doRequest();
+ event = QString::null;
+ }
+
+ void HTTPTracker::manualUpdate()
+ {
+ if (!started)
+ event = "started";
+ doRequest();
+ }
+
+ void HTTPTracker::scrape()
+ {
+ if (!url.isValid())
+ {
+ Out(SYS_TRK|LOG_NOTICE) << "Invalid tracker url, canceling scrape" << endl;
+ return;
+ }
+
+ if (!url.fileName(false).startsWith("announce"))
+ {
+ Out(SYS_TRK|LOG_NOTICE) << "Tracker " << url << " does not support scraping" << endl;
+ return;
+ }
+
+ KURL scrape_url = url;
+ scrape_url.setFileName(url.fileName(false).replace("announce","scrape"));
+
+ QString epq = scrape_url.encodedPathAndQuery();
+ const SHA1Hash & info_hash = tor->getInfoHash();
+ if (scrape_url.queryItems().count() > 0)
+ epq += "&info_hash=" + info_hash.toURLString();
+ else
+ epq += "?info_hash=" + info_hash.toURLString();
+ scrape_url.setEncodedPathAndQuery(epq);
+
+ Out(SYS_TRK|LOG_NOTICE) << "Doing scrape request to url : " << scrape_url.prettyURL() << endl;
+ KIO::MetaData md;
+ setupMetaData(md);
+
+ KIO::StoredTransferJob* j = KIO::storedGet(scrape_url,false,false);
+ // set the meta data
+ j->setMetaData(md);
+ KIO::Scheduler::scheduleJob(j);
+
+ connect(j,SIGNAL(result(KIO::Job* )),this,SLOT(onScrapeResult( KIO::Job* )));
+ }
+
+ void HTTPTracker::onScrapeResult(KIO::Job* j)
+ {
+ if (j->error())
+ {
+ Out(SYS_TRK|LOG_IMPORTANT) << "Scrape failed : " << j->errorString() << endl;
+ return;
+ }
+
+ KIO::StoredTransferJob* st = (KIO::StoredTransferJob*)j;
+ BDecoder dec(st->data(),false,0);
+ BNode* n = 0;
+
+ try
+ {
+ n = dec.decode();
+ }
+ catch (bt::Error & err)
+ {
+ Out(SYS_TRK|LOG_IMPORTANT) << "Invalid scrape data " << err.toString() << endl;
+ return;
+ }
+
+ if (n && n->getType() == BNode::DICT)
+ {
+ BDictNode* d = (BDictNode*)n;
+ d = d->getDict("files");
+ if (d)
+ {
+ d = d->getDict(tor->getInfoHash().toByteArray());
+ if (d)
+ {
+ BValueNode* vn = d->getValue("complete");
+ if (vn && vn->data().getType() == Value::INT)
+ {
+ seeders = vn->data().toInt();
+ }
+
+
+ vn = d->getValue("incomplete");
+ if (vn && vn->data().getType() == Value::INT)
+ {
+ leechers = vn->data().toInt();
+ }
+
+ Out(SYS_TRK|LOG_DEBUG) << "Scrape : leechers = " << leechers
+ << ", seeders = " << seeders << endl;
+ }
+ }
+ }
+
+ delete n;
+ }
+
+ void HTTPTracker::doRequest(WaitJob* wjob)
+ {
+ const TorrentStats & s = tor->getStats();
+
+ KURL u = url;
+ if (!url.isValid())
+ {
+ requestPending();
+ QTimer::singleShot(500,this,SLOT(emitInvalidURLFailure()));
+ return;
+ }
+
+ Uint16 port = Globals::instance().getServer().getPortInUse();;
+
+ u.addQueryItem("peer_id",peer_id.toString());
+ u.addQueryItem("port",QString::number(port));
+ u.addQueryItem("uploaded",QString::number(s.trk_bytes_uploaded));
+ u.addQueryItem("downloaded",QString::number(s.trk_bytes_downloaded));
+
+ if (event == "completed")
+ u.addQueryItem("left","0"); // need to send 0 when we are completed
+ else
+ u.addQueryItem("left",QString::number(s.bytes_left));
+
+ u.addQueryItem("compact","1");
+ if (event != "stopped")
+ u.addQueryItem("numwant","100");
+ else
+ u.addQueryItem("numwant","0");
+
+ u.addQueryItem("key",QString::number(key));
+ QString cip = Tracker::getCustomIP();
+ if (!cip.isNull())
+ u.addQueryItem("ip",cip);
+
+ if (event != QString::null)
+ u.addQueryItem("event",event);
+ QString epq = u.encodedPathAndQuery();
+ const SHA1Hash & info_hash = tor->getInfoHash();
+ epq += "&info_hash=" + info_hash.toURLString();
+
+
+ u.setEncodedPathAndQuery(epq);
+
+ if (active_job)
+ {
+ announce_queue.append(u);
+ Out(SYS_TRK|LOG_NOTICE) << "Announce ongoing, queueing announce" << endl;
+ }
+ else
+ {
+ doAnnounce(u);
+ // if there is a wait job, add this job to the waitjob
+ if (wjob)
+ wjob->addExitOperation(new kt::ExitJobOperation(active_job));
+ }
+ }
+
+ bool HTTPTracker::updateData(const QByteArray & data)
+ {
+//#define DEBUG_PRINT_RESPONSE
+#ifdef DEBUG_PRINT_RESPONSE
+ Out() << "Data : " << endl;
+ Out() << QString(data) << endl;
+#endif
+ // search for dictionary, there might be random garbage infront of the data
+ Uint32 i = 0;
+ while (i < data.size())
+ {
+ if (data[i] == 'd')
+ break;
+ i++;
+ }
+
+ if (i == data.size())
+ {
+ failures++;
+ requestFailed(i18n("Invalid response from tracker"));
+ return false;
+ }
+
+ BDecoder dec(data,false,i);
+ BNode* n = 0;
+ try
+ {
+ n = dec.decode();
+ }
+ catch (...)
+ {
+ failures++;
+ requestFailed(i18n("Invalid data from tracker"));
+ return false;
+ }
+
+ if (!n || n->getType() != BNode::DICT)
+ {
+ failures++;
+ requestFailed(i18n("Invalid response from tracker"));
+ return false;
+ }
+
+ BDictNode* dict = (BDictNode*)n;
+ if (dict->getData("failure reason"))
+ {
+ BValueNode* vn = dict->getValue("failure reason");
+ QString msg = vn->data().toString();
+ delete n;
+ failures++;
+ requestFailed(msg);
+ return false;
+ }
+
+ BValueNode* vn = dict->getValue("interval");
+
+ // if no interval is specified, use 5 minutes
+ if (vn)
+ interval = vn->data().toInt();
+ else
+ interval = 5 * 60;
+
+ vn = dict->getValue("incomplete");
+ if (vn)
+ leechers = vn->data().toInt();
+
+ vn = dict->getValue("complete");
+ if (vn)
+ seeders = vn->data().toInt();
+
+ BListNode* ln = dict->getList("peers");
+ if (!ln)
+ {
+ // no list, it might however be a compact response
+ vn = dict->getValue("peers");
+ if (!vn)
+ {
+ delete n;
+ failures++;
+ requestFailed(i18n("Invalid response from tracker"));
+ return false;
+ }
+
+ QByteArray arr = vn->data().toByteArray();
+ for (Uint32 i = 0;i < arr.size();i+=6)
+ {
+ Uint8 buf[6];
+ for (int j = 0;j < 6;j++)
+ buf[j] = arr[i + j];
+
+ addPeer(QHostAddress(ReadUint32(buf,0)).toString(),ReadUint16(buf,4));
+ }
+ }
+ else
+ {
+ for (Uint32 i = 0;i < ln->getNumChildren();i++)
+ {
+ BDictNode* dict = dynamic_cast<BDictNode*>(ln->getChild(i));
+
+ if (!dict)
+ continue;
+
+ BValueNode* ip_node = dict->getValue("ip");
+ BValueNode* port_node = dict->getValue("port");
+
+ if (!ip_node || !port_node)
+ continue;
+
+ addPeer(ip_node->data().toString(),port_node->data().toInt());
+ }
+ }
+
+ delete n;
+ return true;
+ }
+
+
+ void HTTPTracker::onAnnounceResult(KIO::Job* j)
+ {
+ if (j->error())
+ {
+ KIO::StoredTransferJob* st = (KIO::StoredTransferJob*)j;
+ KURL u = st->url();
+ active_job = 0;
+
+ Out(SYS_TRK|LOG_IMPORTANT) << "Error : " << st->errorString() << endl;
+ if (u.queryItem("event") != "stopped")
+ {
+ failures++;
+ requestFailed(j->errorString());
+ }
+ else
+ {
+ stopDone();
+ }
+ }
+ else
+ {
+ KIO::StoredTransferJob* st = (KIO::StoredTransferJob*)j;
+ KURL u = st->url();
+ active_job = 0;
+
+ if (u.queryItem("event") != "stopped")
+ {
+ try
+ {
+ if (updateData(st->data()))
+ {
+ failures = 0;
+ peersReady(this);
+ requestOK();
+ if (u.queryItem("event") == "started")
+ started = true;
+ }
+ }
+ catch (bt::Error & err)
+ {
+ failures++;
+ requestFailed(i18n("Invalid response from tracker"));
+ }
+ event = QString::null;
+ }
+ else
+ {
+ failures = 0;
+ stopDone();
+ }
+ }
+ doAnnounceQueue();
+ }
+
+ void HTTPTracker::emitInvalidURLFailure()
+ {
+ failures++;
+ requestFailed(i18n("Invalid tracker URL"));
+ }
+
+ void HTTPTracker::setupMetaData(KIO::MetaData & md)
+ {
+ md["UserAgent"] = "ktorrent/" VERSION;
+ md["SendLanguageSettings"] = "false";
+ md["Cookies"] = "none";
+ // md["accept"] = "text/plain";
+ md["accept"] = "text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2";
+ if (Settings::doNotUseKDEProxy())
+ {
+ // set the proxy if the doNotUseKDEProxy ix enabled (URL must be valid to)
+ KURL url = KURL::fromPathOrURL(Settings::httpTrackerProxy());
+ if (url.isValid())
+ md["UseProxy"] = url.pathOrURL();
+ else
+ md["UseProxy"] = QString::null;
+ }
+ }
+
+ void HTTPTracker::doAnnounceQueue()
+ {
+ if (announce_queue.empty())
+ return;
+
+ KURL u = announce_queue.front();
+ announce_queue.pop_front();
+ doAnnounce(u);
+ }
+
+ void HTTPTracker::doAnnounce(const KURL & u)
+ {
+ Out(SYS_TRK|LOG_NOTICE) << "Doing tracker request to url : " << u.prettyURL() << endl;
+ KIO::MetaData md;
+ setupMetaData(md);
+ KIO::StoredTransferJob* j = KIO::storedGet(u,false,false);
+ // set the meta data
+ j->setMetaData(md);
+ KIO::Scheduler::scheduleJob(j);
+
+ connect(j,SIGNAL(result(KIO::Job* )),this,SLOT(onAnnounceResult( KIO::Job* )));
+
+ active_job = j;
+ requestPending();
+ }
+}
+#include "httptracker.moc"
diff --git a/libktorrent/torrent/httptracker.h b/libktorrent/torrent/httptracker.h
new file mode 100644
index 0000000..8ac7e69
--- /dev/null
+++ b/libktorrent/torrent/httptracker.h
@@ -0,0 +1,77 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef BTHTTPTRACKER_H
+#define BTHTTPTRACKER_H
+
+#include <qtimer.h>
+#include "tracker.h"
+
+namespace KIO
+{
+ class Job;
+ class MetaData;
+}
+
+namespace bt
+{
+
+
+ /**
+ * @author Joris Guisson
+ * @brief Communicates with the tracker
+ *
+ * This class uses the HTTP protocol to communicate with the tracker.
+ */
+ class HTTPTracker : public Tracker
+ {
+ Q_OBJECT
+ public:
+ HTTPTracker(const KURL & url,kt::TorrentInterface* tor,const PeerID & id,int tier);
+ virtual ~HTTPTracker();
+
+ virtual void start();
+ virtual void stop(WaitJob* wjob = 0);
+ virtual void completed();
+ virtual void manualUpdate();
+ virtual Uint32 failureCount() const {return failures;}
+ virtual void scrape();
+
+ private slots:
+ void onAnnounceResult(KIO::Job* j);
+ void onScrapeResult(KIO::Job* j);
+ void emitInvalidURLFailure();
+
+ private:
+ void doRequest(WaitJob* wjob = 0);
+ bool updateData(const QByteArray & data);
+ void setupMetaData(KIO::MetaData & md);
+ void doAnnounceQueue();
+ void doAnnounce(const KURL & u);
+
+ private:
+ KIO::Job* active_job;
+ KURL::List announce_queue;
+ QString event;
+ Uint32 failures;
+ };
+
+}
+
+#endif
diff --git a/libktorrent/torrent/ipblocklist.cpp b/libktorrent/torrent/ipblocklist.cpp
new file mode 100644
index 0000000..de30968
--- /dev/null
+++ b/libktorrent/torrent/ipblocklist.cpp
@@ -0,0 +1,400 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * ivasic@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+
+#include "ipblocklist.h"
+#include <qmap.h>
+#include <qstring.h>
+#include <qstringlist.h>
+#include <util/constants.h>
+#include <util/log.h>
+#include "globals.h"
+#include <interfaces/ipblockinginterface.h>
+
+
+namespace bt
+{
+ Uint32 toUint32(const QString& ip, bool* ok)
+ {
+ bool test;
+ *ok = true;
+
+ Uint32 ret = ip.section('.',0,0).toULongLong(&test);
+ if(!test) *ok=false;
+ ret <<= 8;
+ ret |= ip.section('.',1,1).toULong(&test);
+ if(!test) *ok=false;
+ ret <<= 8;
+ ret |= ip.section('.',2,2).toULong(&test);
+ if(!test) *ok=false;
+ ret <<= 8;
+ ret |= ip.section('.',3,3).toULong(&test);
+ if(!test) *ok=false;
+
+ if(*ok)
+ {
+ // Out() << "IP: " << ip << " parsed: " << ret << endl;
+ return ret;
+ }
+ else
+ {
+ // Out() << "Could not parse IP " << ip << ". IP blocklist might not be working." << endl;
+ return 0;
+ }
+ }
+
+ IPBlocklist::IPBlocklist()
+ {
+ this->pluginInterface = 0;
+ insert("0.0.0.0",3);
+ addRange("3.*.*.*");
+ }
+
+ IPBlocklist::IPBlocklist(const IPBlocklist & ) {}
+
+ void IPBlocklist::insert( QString ip, int state )
+ {
+ bool ok;
+ Uint32 ipi = toUint32(ip, &ok);
+ if(!ok)
+ return;
+ IPKey key(ipi,0xFFFFFFFF); //-- you can test ranges here. Just specify your mask.
+ insertRangeIP(key, state);
+ Out(SYS_IPF|LOG_NOTICE) << "IP " << ip << " banned." << endl;
+ }
+
+ void IPBlocklist::addRange(QString ip)
+ {
+ bool ok;
+ int tmp = 0;
+ Uint32 addr = 0;
+ Uint32 mask = 0xFFFFFFFF;
+
+ tmp = ip.section('.',0,0).toInt(&ok);
+ if(!ok)
+ {
+ if(ip.section('.',0,0) == "*")
+ mask &= 0x00FFFFFF;
+ else return; //illegal character
+ }
+ else
+ addr = tmp;
+
+ tmp = ip.section('.',1,1).toInt(&ok);
+ if(!ok)
+ {
+ addr <<= 8;
+ if(ip.section('.',1,1) == "*")
+ mask &= 0xFF00FFFF;
+ else return; //illegal character
+ }
+ else
+ {
+ addr <<= 8;
+ addr |= tmp;
+ }
+
+ tmp = ip.section('.',2,2).toInt(&ok);
+ if(!ok)
+ {
+ addr <<= 8;
+ if(ip.section('.',2,2) == "*")
+ mask &= 0xFFFF00FF;
+ else return; //illegal character
+ }
+ else
+ {
+ addr <<= 8;
+ addr |= tmp;
+ }
+
+ tmp = ip.section('.',3,3).toInt(&ok);
+ if(!ok)
+ {
+ addr <<= 8;
+ if(ip.section('.',3,3) == "*")
+ mask &=0xFFFFFF00;
+ else return; //illegal character
+ }
+ else
+ {
+ addr <<= 8;
+ addr |= tmp;
+ }
+
+ IPKey key(addr, mask);
+ this->insertRangeIP(key);
+ }
+
+ void IPBlocklist::insertRangeIP(IPKey& key, int state)
+ {
+// Out() << "Blocked range: " << key.m_ip << " - " << key.m_mask << endl;
+ QMap<IPKey, int>::iterator it;
+ if ((it = m_peers.find(key)) != m_peers.end())
+ {
+
+ if(it.key().m_mask != key.m_mask)
+ {
+ int st = it.data();
+ IPKey key1(key.m_ip, it.key().m_mask | key.m_mask);
+ m_peers.insert(key1, state+st);
+ return;
+ }
+ m_peers[key]+= state;
+ }
+ else
+ m_peers.insert(key,state);
+ }
+
+ void IPBlocklist::removeRange(QString ip)
+ {
+ bool ok;
+ int tmp = 0;
+ Uint32 addr = 0;
+ Uint32 mask = 0xFFFFFFFF;
+
+ tmp = ip.section('.',0,0).toInt(&ok);
+ if(!ok)
+ {
+ if(ip.section('.',0,0) == "*")
+ mask &= 0x00FFFFFF;
+ else return; //illegal character
+ }
+ else
+ addr = tmp;
+
+ tmp = ip.section('.',1,1).toInt(&ok);
+ if(!ok)
+ {
+ addr <<= 8;
+ if(ip.section('.',1,1) == "*")
+ mask &= 0xFF00FFFF;
+ else return; //illegal character
+ }
+ else
+ {
+ addr <<= 8;
+ addr |= tmp;
+ }
+
+ tmp = ip.section('.',2,2).toInt(&ok);
+ if(!ok)
+ {
+ addr <<= 8;
+ if(ip.section('.',2,2) == "*")
+ mask &= 0xFFFF00FF;
+ else return; //illegal character
+ }
+ else
+ {
+ addr <<= 8;
+ addr |= tmp;
+ }
+
+ tmp = ip.section('.',3,3).toInt(&ok);
+ if(!ok)
+ {
+ addr <<= 8;
+ if(ip.section('.',3,3) == "*")
+ mask &=0xFFFFFF00;
+ else return; //illegal character
+ }
+ else
+ {
+ addr <<= 8;
+ addr |= tmp;
+ }
+
+ IPKey key(addr, mask);
+
+ QMap<IPKey, int>::iterator it = m_peers.find(key);
+ if (it == m_peers.end())
+ return;
+
+ m_peers.remove(key);
+ }
+
+ void IPBlocklist::setPluginInterfacePtr( kt::IPBlockingInterface* ptr )
+ {
+ this->pluginInterface = ptr;
+ }
+
+ bool IPBlocklist::isBlocked(const QString& ip )
+ {
+ //First check local filter list
+ if(isBlockedLocal(ip))
+ {
+ Out(SYS_IPF|LOG_NOTICE) << "IP " << ip << " is blacklisted. Connection denied." << endl;
+ return true;
+ }
+
+ //Then we ask plugin
+ if(isBlockedPlugin(ip))
+ {
+ Out(SYS_IPF|LOG_NOTICE) << "IP " << ip << " is blacklisted. Connection denied." << endl;
+ return true;
+ }
+
+ return false;
+ }
+
+ bool IPBlocklist::isBlockedLocal(const QString& ip )
+ {
+ bool ok;
+ Uint32 ipi = toUint32(ip,&ok);
+ if (!ok)
+ return false;
+ IPKey key(ipi);
+
+ QMap<IPKey, int>::iterator it;
+ it = m_peers.find(key);
+ if (it==m_peers.end())
+ return false;
+
+ return m_peers[key] >= 3;
+ }
+
+ bool IPBlocklist::isBlockedPlugin(const QString& ip )
+ {
+ if (pluginInterface == 0) //the plugin is not loaded
+ return false;
+ else
+ return pluginInterface->isBlockedIP(ip);
+ }
+
+ QStringList* IPBlocklist::getBlocklist()
+ {
+ QStringList* ret = new QStringList();
+ QMap<IPKey,int>::iterator it = m_peers.begin();
+ for( ;it!=m_peers.end();++it)
+ {
+ IPKey key = it.key();
+ *ret << key.toString();
+ }
+
+ return ret;
+ }
+
+ void IPBlocklist::setBlocklist(QStringList* list)
+ {
+ m_peers.clear();
+ for (QStringList::Iterator it = list->begin(); it != list->end(); ++it )
+ addRange(*it);
+ }
+
+ /*** IPKey *****************************************************************************************************************/
+
+ IPKey::IPKey()
+ {
+ m_ip = 0;
+ m_mask = 0xFFFFFFFF;
+ }
+
+ IPKey::IPKey(QString& ip, Uint32 mask)
+ : m_mask(mask)
+ {
+ bool ok;
+ this->m_ip = toUint32(ip, &ok);
+ }
+
+ IPKey::IPKey(const IPKey& ip)
+ {
+ m_ip = ip.m_ip;
+ m_mask = ip.m_mask;
+ }
+
+ IPKey::IPKey(Uint32 ip, Uint32 mask)
+ : m_ip(ip), m_mask(mask)
+ {}
+
+ QString IPKey::toString()
+ {
+ Uint32 tmp, tmpmask;
+ Uint32 ip = m_ip;
+ Uint32 mask = m_mask;
+ QString out;
+
+ tmp = ip;
+ tmpmask = mask;
+ tmp &= 0x000000FF;
+ tmpmask &= 0x000000FF;
+ if(tmpmask == 0)
+ out.prepend("*");
+ else
+ out.prepend(QString("%1").arg(tmp));
+ ip >>= 8;
+ mask >>= 8;
+ tmp = ip;
+ tmpmask = mask;
+ tmp &= 0x000000FF;
+ tmpmask &= 0x000000FF;
+ if(tmpmask == 0)
+ out.prepend("*.");
+ else
+ out.prepend(QString("%1.").arg(tmp));
+ ip >>= 8;
+ mask >>= 8;
+ tmp = ip;
+ tmpmask = mask;
+ tmp &= 0x000000FF;
+ tmpmask &= 0x000000FF;
+ if(tmpmask == 0)
+ out.prepend("*.");
+ else
+ out.prepend(QString("%1.").arg(tmp));
+ ip >>= 8;
+ mask >>= 8;
+ tmp = ip;
+ tmpmask = mask;
+ tmp &= 0x000000FF;
+ tmpmask &= 0x000000FF;
+ if(tmpmask == 0)
+ out.prepend("*.");
+ else
+ out.prepend(QString("%1.").arg(tmp));
+
+ return out;
+ }
+
+ bool IPKey::operator ==(const IPKey& ip) const
+ {
+ return (m_ip & m_mask) == m_mask & ip.m_ip;
+ }
+
+ bool IPKey::operator !=(const IPKey& ip) const
+ {
+ return (m_ip & m_mask) != m_mask & ip.m_ip;
+ }
+
+ bool IPKey::operator < (const IPKey& ip) const
+ {
+ return (m_ip & m_mask) < (m_mask & ip.m_ip);
+ }
+
+ IPKey& IPKey::operator =(const IPKey& ip)
+ {
+ m_ip = ip.m_ip;
+ m_mask = ip.m_mask;
+ return *this;
+ }
+
+ IPKey::~ IPKey()
+ {}
+}
diff --git a/libktorrent/torrent/ipblocklist.h b/libktorrent/torrent/ipblocklist.h
new file mode 100644
index 0000000..b30a856
--- /dev/null
+++ b/libktorrent/torrent/ipblocklist.h
@@ -0,0 +1,175 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * ivasic@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef IPBLOCKLIST_H
+#define IPBLOCKLIST_H
+
+#include <interfaces/ipblockinginterface.h>
+
+#include <qmap.h>
+#include <qstringlist.h>
+#include <util/constants.h>
+
+class QString;
+
+namespace bt
+{
+ class IPKey
+ {
+ public:
+ IPKey();
+ IPKey(QString& ip, Uint32 mask = 0xFFFFFFFF);
+ IPKey(Uint32 ip, Uint32 mask = 0xFFFFFFFF);
+ IPKey(const IPKey& ip);
+ ~IPKey();
+
+ bool operator== (const IPKey& ip) const;
+ bool operator!= (const IPKey& ip) const;
+ bool operator < (const IPKey & ip) const;
+ IPKey& operator= (const IPKey& ip);
+
+ QString toString();
+
+ Uint32 m_ip;
+ Uint32 m_mask;
+ };
+
+ /**
+ * @author Ivan Vasic <ivasic@gmail.com>
+ * @brief Keeps track of blocked peers
+ *
+ * This class is used for keeping the IP addresses list of peers that
+ * have sent bad chunks.
+ *
+ * Peers that have sent >= 3 bad chunks are blocked.
+ */
+ class IPBlocklist
+ {
+ IPBlocklist();
+ IPBlocklist(const IPBlocklist & );
+ const IPBlocklist& operator=(const IPBlocklist&);
+
+ public:
+
+ inline static IPBlocklist & instance()
+ {
+ static IPBlocklist singleton;
+ return singleton;
+ }
+
+ /**
+ * @brief Adds ip address to the list.
+ * It also increases the number of times this IP appeared in the list.
+ * @param ip QString containing the peer IP address
+ * @param state int number of bad chunks client from ip sent. Basically this parameter
+ * is used only to permanently block some IP (by setting this param to 3)
+ */
+ void insert(QString ip, int state=1);
+
+ /**
+ * @brief Adds IP range to the list
+ * It is used for blocking plugin. For single IP use insert() instead.
+ * @param ip QString peer IP address. Uses ''*" for ranges.
+ **/
+ void addRange(QString ip);
+
+
+ /**
+ * @brief Removes IP range from list
+ * It is used for blocking plugin.
+ * @param ip QString peer IP address. Uses ''*" for ranges.
+ **/
+ void removeRange(QString ip);
+
+ /**
+ * Checks if IP is in the blocking list
+ * @param ip - IP address to check
+ * @returns true if IP is blocked
+ */
+ bool isBlocked(const QString& ip);
+
+ /**
+ * @brief Sets the pointer to the IPBlockingInterface (IPBlocking plugin)
+ * Call this function from IPBlocking plugin when it gets loaded.
+ * @arg ptr - pointer to be set
+ */
+ void setPluginInterfacePtr(kt::IPBlockingInterface* ptr);
+
+ /**
+ * @brief Unsets the interface pointer
+ * Call this when IPBlockingPlugin gets unloaded or deleted
+ */
+ void unsetPluginInterfacePtr() { pluginInterface = 0; }
+
+
+ /**
+ * @brief This function will fill QStringList with all banned peer IP addresses.
+ * @return QStringList filled with blacklisted peers.
+ * It will create a new QStringList object so don't forget to delete it after using.
+ */
+ QStringList* getBlocklist();
+
+
+ /**
+ * @brief This function will load blacklisted peers to IPFilter.
+ * @param list QStringList containing all banned peers.
+ * @note This function will remove current peers from blocklist before setting new list!!!
+ */
+ void setBlocklist(QStringList* list);
+
+ private:
+
+ /**
+ * Pointer to the IPBlocking plugin which implements IPBlockingInterface
+ * Used to provide a way to use this plugin functions from within this class
+ */
+ kt::IPBlockingInterface* pluginInterface;
+
+ /**
+ * @param IPKey - Key: Peer IP address and bit mask if it is a range
+ * @param int - Number of bad chunks sent.
+ **/
+ QMap<IPKey, int> m_peers;
+
+ /**
+ * @brief Adds IP range to the list.
+ * @param key IPKey that represents this IP range
+ * @param state int Number of 'warnings' for the range.
+ * Default is 3 - that means range is blocked permanently.
+ */
+ void insertRangeIP(IPKey& key, int state=3);
+
+
+ /**
+ * Checks if IP is listed in local database (IPBlocklist::m_peers)
+ * @return TRUE if IP is to be blocked
+ */
+ bool isBlockedLocal(const QString& ip);
+
+ /**
+ * Checks if IP is listed in plugins antip2p file
+ * @return TRUE if IP is to be blocked
+ */
+ bool isBlockedPlugin(const QString& ip);
+ };
+}
+
+#endif
+
diff --git a/libktorrent/torrent/movedatafilesjob.cpp b/libktorrent/torrent/movedatafilesjob.cpp
new file mode 100644
index 0000000..c0c24e7
--- /dev/null
+++ b/libktorrent/torrent/movedatafilesjob.cpp
@@ -0,0 +1,103 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <util/log.h>
+#include "movedatafilesjob.h"
+
+namespace bt
+{
+
+ MoveDataFilesJob::MoveDataFilesJob() : KIO::Job(false),err(false),active_job(0)
+ {}
+
+
+ MoveDataFilesJob::~MoveDataFilesJob()
+ {}
+
+ void MoveDataFilesJob::addMove(const QString & src,const QString & dst)
+ {
+ todo.insert(src,dst);
+ }
+
+ void MoveDataFilesJob::onJobDone(KIO::Job* j)
+ {
+ if (j->error() || err)
+ {
+ if (!err)
+ m_error = KIO::ERR_INTERNAL;
+
+ active_job = 0;
+ if (j->error())
+ j->showErrorDialog();
+
+ // shit happened cancel all previous moves
+ err = true;
+ recover();
+ }
+ else
+ {
+ success.insert(active_src,active_dst);
+ active_src = active_dst = QString::null;
+ active_job = 0;
+ startMoving();
+ }
+ }
+
+ void MoveDataFilesJob::onCanceled(KIO::Job* j)
+ {
+ m_error = KIO::ERR_USER_CANCELED;
+ active_job = 0;
+ err = true;
+ recover();
+ }
+
+ void MoveDataFilesJob::startMoving()
+ {
+ if (todo.isEmpty())
+ {
+ m_error = 0;
+ emitResult();
+ return;
+ }
+
+ QMap<QString,QString>::iterator i = todo.begin();
+ active_job = KIO::move(KURL::fromPathOrURL(i.key()),KURL::fromPathOrURL(i.data()),false);
+ active_src = i.key();
+ active_dst = i.data();
+ Out(SYS_GEN|LOG_DEBUG) << "Moving " << active_src << " -> " << active_dst << endl;
+ connect(active_job,SIGNAL(result(KIO::Job*)),this,SLOT(onJobDone(KIO::Job*)));
+ connect(active_job,SIGNAL(canceled(KIO::Job*)),this,SLOT(onCanceled(KIO::Job*)));
+ todo.erase(i);
+ }
+
+ void MoveDataFilesJob::recover()
+ {
+ if (success.isEmpty())
+ {
+ emitResult();
+ return;
+ }
+ QMap<QString,QString>::iterator i = success.begin();
+ active_job = KIO::move(KURL::fromPathOrURL(i.data()),KURL::fromPathOrURL(i.key()),false);
+ connect(active_job,SIGNAL(result(KIO::Job*)),this,SLOT(onJobDone(KIO::Job*)));
+ connect(active_job,SIGNAL(canceled(KIO::Job*)),this,SLOT(onCanceled(KIO::Job*)));
+ success.erase(i);
+ }
+}
+#include "movedatafilesjob.moc"
diff --git a/libktorrent/torrent/movedatafilesjob.h b/libktorrent/torrent/movedatafilesjob.h
new file mode 100644
index 0000000..b0002d9
--- /dev/null
+++ b/libktorrent/torrent/movedatafilesjob.h
@@ -0,0 +1,68 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef BTMOVEDATAFILESJOB_H
+#define BTMOVEDATAFILESJOB_H
+
+#include <kio/job.h>
+
+namespace bt
+{
+
+ /**
+ * @author Joris Guisson <joris.guisson@gmail.com>
+ * KIO::Job to move all the files of a torrent.
+ */
+ class MoveDataFilesJob : public KIO::Job
+ {
+ Q_OBJECT
+ public:
+ MoveDataFilesJob();
+ virtual ~MoveDataFilesJob();
+
+ /**
+ * Add a move to the todo list.
+ * @param src File to move
+ * @param dst Where to move it to
+ */
+ void addMove(const QString & src,const QString & dst);
+
+ /**
+ * Start moving the files.
+ */
+ void startMoving();
+
+ private slots:
+ void onJobDone(KIO::Job* j);
+ void onCanceled(KIO::Job* j);
+
+ private:
+ void recover();
+
+ private:
+ bool err;
+ KIO::Job* active_job;
+ QString active_src,active_dst;
+ QMap<QString,QString> todo;
+ QMap<QString,QString> success;
+ };
+
+}
+
+#endif
diff --git a/libktorrent/torrent/multifilecache.cpp b/libktorrent/torrent/multifilecache.cpp
new file mode 100644
index 0000000..c6af92c
--- /dev/null
+++ b/libktorrent/torrent/multifilecache.cpp
@@ -0,0 +1,867 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <errno.h>
+#include <qdir.h>
+#include <qstringlist.h>
+#include <qfileinfo.h>
+#include <klocale.h>
+#include <kio/netaccess.h>
+#include <util/file.h>
+#include <util/fileops.h>
+#include <util/functions.h>
+#include <util/error.h>
+#include <util/log.h>
+#include "torrent.h"
+#include "cache.h"
+#include "multifilecache.h"
+#include "globals.h"
+#include "chunk.h"
+#include "cachefile.h"
+#include "dndfile.h"
+#include "preallocationthread.h"
+#include "movedatafilesjob.h"
+
+
+
+namespace bt
+{
+ static Uint64 FileOffset(Chunk* c,const TorrentFile & f,Uint64 chunk_size);
+ static Uint64 FileOffset(Uint32 cindex,const TorrentFile & f,Uint64 chunk_size);
+ static void DeleteEmptyDirs(const QString & output_dir,const QString & fpath);
+
+
+ MultiFileCache::MultiFileCache(Torrent& tor,const QString & tmpdir,const QString & datadir,bool custom_output_name) : Cache(tor, tmpdir,datadir)
+ {
+ cache_dir = tmpdir + "cache" + bt::DirSeparator();
+ if (datadir.length() == 0)
+ this->datadir = guessDataDir();
+ if (!custom_output_name)
+ output_dir = this->datadir + tor.getNameSuggestion() + bt::DirSeparator();
+ else
+ output_dir = this->datadir;
+ files.setAutoDelete(true);
+ }
+
+
+ MultiFileCache::~MultiFileCache()
+ {}
+
+ QString MultiFileCache::guessDataDir()
+ {
+ for (Uint32 i = 0;i < tor.getNumFiles();i++)
+ {
+ TorrentFile & tf = tor.getFile(i);
+ if (tf.doNotDownload())
+ continue;
+
+ QString p = cache_dir + tf.getPath();
+ QFileInfo fi(p);
+ if (!fi.isSymLink())
+ continue;
+
+ QString dst = fi.readLink();
+ QString tmp = tor.getNameSuggestion() + bt::DirSeparator() + tf.getPath();
+ dst = dst.left(dst.length() - tmp.length());
+ if (dst.length() == 0)
+ continue;
+
+ if (!dst.endsWith(bt::DirSeparator()))
+ dst += bt::DirSeparator();
+ Out() << "Guessed outputdir to be " << dst << endl;
+ return dst;
+ }
+
+ return QString::null;
+ }
+
+ QString MultiFileCache::getOutputPath() const
+ {
+ return output_dir;
+ }
+
+ void MultiFileCache::close()
+ {
+ files.clear();
+ }
+
+ void MultiFileCache::open()
+ {
+ QString dnd_dir = tmpdir + "dnd" + bt::DirSeparator();
+ // open all files
+ for (Uint32 i = 0;i < tor.getNumFiles();i++)
+ {
+ TorrentFile & tf = tor.getFile(i);
+ CacheFile* fd = 0;
+ DNDFile* dfd = 0;
+ try
+ {
+ if (!tf.doNotDownload())
+ {
+ if (files.contains(i))
+ files.erase(i);
+
+ fd = new CacheFile();
+ fd->open(cache_dir + tf.getPath(),tf.getSize());
+ files.insert(i,fd);
+ }
+ else
+ {
+ if (dnd_files.contains(i))
+ dnd_files.erase(i);
+
+ dfd = new DNDFile(dnd_dir + tf.getPath() + ".dnd");
+ dfd->checkIntegrity();
+ dnd_files.insert(i,dfd);
+ }
+ }
+ catch (...)
+ {
+ delete fd;
+ fd = 0;
+ delete dfd;
+ dfd = 0;
+ throw;
+ }
+ }
+ }
+
+ void MultiFileCache::changeTmpDir(const QString& ndir)
+ {
+ Cache::changeTmpDir(ndir);
+ cache_dir = tmpdir + "cache/";
+ QString dnd_dir = tmpdir + "dnd" + bt::DirSeparator();
+
+ // change paths for individual files, it should not
+ // be a problem to move these files when they are open
+ for (Uint32 i = 0;i < tor.getNumFiles();i++)
+ {
+ TorrentFile & tf = tor.getFile(i);
+ if (tf.doNotDownload())
+ {
+ DNDFile* dfd = dnd_files.find(i);
+ if (dfd)
+ dfd->changePath(dnd_dir + tf.getPath() + ".dnd");
+ }
+ else
+ {
+ CacheFile* fd = files.find(i);
+ if (fd)
+ fd->changePath(cache_dir + tf.getPath());
+ }
+ }
+ }
+
+ void MultiFileCache::changeOutputPath(const QString & outputpath)
+ {
+ output_dir = outputpath;
+ if (!output_dir.endsWith(bt::DirSeparator()))
+ output_dir += bt::DirSeparator();
+
+ datadir = output_dir;
+
+ if (!bt::Exists(cache_dir))
+ MakeDir(cache_dir);
+
+ for (Uint32 i = 0;i < tor.getNumFiles();i++)
+ {
+ TorrentFile & tf = tor.getFile(i);
+ if (!tf.doNotDownload())
+ {
+ QString fpath = tf.getPath();
+ if (bt::Exists(output_dir + fpath))
+ {
+ bt::Delete(cache_dir + fpath,true); // delete any existing symlinks
+ // create new one
+ bt::SymLink(output_dir + fpath,cache_dir + fpath,true);
+ }
+ }
+ }
+ }
+
+ KIO::Job* MultiFileCache::moveDataFiles(const QString & ndir)
+ {
+ if (!bt::Exists(ndir))
+ bt::MakeDir(ndir);
+
+ QString nd = ndir;
+ if (!nd.endsWith(bt::DirSeparator()))
+ nd += bt::DirSeparator();
+
+ try
+ {
+ MoveDataFilesJob* mvd = new MoveDataFilesJob();
+ for (Uint32 i = 0;i < tor.getNumFiles();i++)
+ {
+ TorrentFile & tf = tor.getFile(i);
+ if (tf.doNotDownload())
+ continue;
+
+ // check if every directory along the path exists, and if it doesn't
+ // create it
+ QStringList sl = QStringList::split(bt::DirSeparator(),nd + tf.getPath());
+ QString odir = bt::DirSeparator();
+ for (Uint32 i = 0;i < sl.count() - 1;i++)
+ {
+ odir += sl[i] + bt::DirSeparator();
+ if (!bt::Exists(odir))
+ {
+ bt::MakeDir(odir);
+ }
+ }
+
+ mvd->addMove(output_dir + tf.getPath(),nd + tf.getPath());
+ }
+
+ mvd->startMoving();
+ return mvd;
+ }
+ catch (bt::Error & err)
+ {
+ throw; // rethrow error
+ }
+ return 0;
+ }
+
+ void MultiFileCache::moveDataFilesCompleted(KIO::Job* job)
+ {
+ if (!job->error())
+ {
+ for (Uint32 i = 0;i < tor.getNumFiles();i++)
+ {
+ TorrentFile & tf = tor.getFile(i);
+ // check for empty directories and delete them
+ DeleteEmptyDirs(output_dir,tf.getPath());
+ }
+ }
+ }
+
+ void MultiFileCache::create()
+ {
+ if (!bt::Exists(cache_dir))
+ MakeDir(cache_dir);
+ if (!bt::Exists(output_dir))
+ MakeDir(output_dir);
+ if (!bt::Exists(tmpdir + "dnd"))
+ bt::MakeDir(tmpdir + "dnd");
+
+ // update symlinks
+ for (Uint32 i = 0;i < tor.getNumFiles();i++)
+ {
+ TorrentFile & tf = tor.getFile(i);
+ touch(tf);
+ }
+ }
+
+ void MultiFileCache::touch(TorrentFile & tf)
+ {
+ QString fpath = tf.getPath();
+ bool dnd = tf.doNotDownload();
+ // first split fpath by / separator
+ QStringList sl = QStringList::split(bt::DirSeparator(),fpath);
+ // create all necessary subdirs
+ QString ctmp = cache_dir;
+ QString otmp = output_dir;
+ QString dtmp = tmpdir + "dnd" + bt::DirSeparator();
+ for (Uint32 i = 0;i < sl.count() - 1;i++)
+ {
+ otmp += sl[i];
+ ctmp += sl[i];
+ dtmp += sl[i];
+ // we need to make the same directory structure in the cache,
+ // the output_dir and the dnd directory
+ if (!bt::Exists(ctmp))
+ MakeDir(ctmp);
+ if (!bt::Exists(otmp))
+ MakeDir(otmp);
+ if (!bt::Exists(dtmp))
+ MakeDir(dtmp);
+ otmp += bt::DirSeparator();
+ ctmp += bt::DirSeparator();
+ dtmp += bt::DirSeparator();
+ }
+
+
+ bt::Delete(cache_dir + fpath,true); // delete any existing symlinks
+
+ // then make the file
+ QString tmp = dnd ? tmpdir + "dnd" + bt::DirSeparator() : output_dir;
+ if (dnd)
+ {
+ // only symlink, when we open the files a default dnd file will be made if the file is corrupt or doesn't exist
+ bt::SymLink(tmp + fpath + ".dnd",cache_dir + fpath);
+ }
+ else
+ {
+ if (!bt::Exists(tmp + fpath))
+ {
+ bt::Touch(tmp + fpath);
+ }
+ else
+ {
+ preexisting_files = true;
+ tf.setPreExisting(true); // mark the file as preexisting
+ }
+
+ bt::SymLink(tmp + fpath,cache_dir + fpath);
+ }
+ }
+
+ void MultiFileCache::load(Chunk* c)
+ {
+ QValueList<Uint32> tflist;
+ tor.calcChunkPos(c->getIndex(),tflist);
+
+ // one file is simple, just mmap it
+ if (tflist.count() == 1)
+ {
+ const TorrentFile & f = tor.getFile(tflist.first());
+ CacheFile* fd = files.find(tflist.first());
+ if (!fd)
+ return;
+
+ if (Cache::mappedModeAllowed() && mmap_failures < 3)
+ {
+ Uint64 off = FileOffset(c,f,tor.getChunkSize());
+ Uint8* buf = (Uint8*)fd->map(c,off,c->getSize(),CacheFile::READ);
+ if (buf)
+ {
+ c->setData(buf,Chunk::MMAPPED);
+ // only return when the mapping is OK
+ // if mmap fails we will just load it buffered
+ return;
+ }
+ else
+ mmap_failures++;
+ }
+ }
+
+ Uint8* data = new Uint8[c->getSize()];
+ Uint64 read = 0; // number of bytes read
+ for (Uint32 i = 0;i < tflist.count();i++)
+ {
+ const TorrentFile & f = tor.getFile(tflist[i]);
+ CacheFile* fd = files.find(tflist[i]);
+ DNDFile* dfd = dnd_files.find(tflist[i]);
+
+ // first calculate offset into file
+ // only the first file can have an offset
+ // the following files will start at the beginning
+ Uint64 off = 0;
+ if (i == 0)
+ off = FileOffset(c,f,tor.getChunkSize());
+
+ Uint32 to_read = 0;
+ // then the amount of data we can read from this file
+ if (tflist.count() == 1)
+ to_read = c->getSize();
+ else if (i == 0)
+ to_read = f.getLastChunkSize();
+ else if (i == tflist.count() - 1)
+ to_read = c->getSize() - read;
+ else
+ to_read = f.getSize();
+
+
+ // read part of data
+ if (fd)
+ fd->read(data + read,to_read,off);
+ else if (dfd)
+ {
+ Uint32 ret = 0;
+ if (i == 0)
+ ret = dfd->readLastChunk(data,read,c->getSize());
+ else if (i == tflist.count() - 1)
+ ret = dfd->readFirstChunk(data,read,c->getSize());
+ else
+ ret = dfd->readFirstChunk(data,read,c->getSize());
+
+ if (ret > 0 && ret != to_read)
+ Out() << "Warning : MultiFileCache::load ret != to_read" << endl;
+ }
+ read += to_read;
+ }
+ c->setData(data,Chunk::BUFFERED);
+ }
+
+
+ bool MultiFileCache::prep(Chunk* c)
+ {
+ // find out in which files a chunk lies
+ QValueList<Uint32> tflist;
+ tor.calcChunkPos(c->getIndex(),tflist);
+
+// Out() << "Prep " << c->getIndex() << endl;
+ if (tflist.count() == 1)
+ {
+ // in one so just mmap it
+ Uint64 off = FileOffset(c,tor.getFile(tflist.first()),tor.getChunkSize());
+ CacheFile* fd = files.find(tflist.first());
+ Uint8* buf = 0;
+ if (fd && Cache::mappedModeAllowed() && mmap_failures < 3)
+ {
+ buf = (Uint8*)fd->map(c,off,c->getSize(),CacheFile::RW);
+ if (!buf)
+ mmap_failures++;
+ }
+
+ if (!buf)
+ {
+ // if mmap fails or is not possible use buffered mode
+ c->allocate();
+ c->setStatus(Chunk::BUFFERED);
+ }
+ else
+ {
+ c->setData(buf,Chunk::MMAPPED);
+ }
+ }
+ else
+ {
+ // just allocate it
+ c->allocate();
+ c->setStatus(Chunk::BUFFERED);
+ }
+ return true;
+ }
+
+ void MultiFileCache::save(Chunk* c)
+ {
+ QValueList<Uint32> tflist;
+ tor.calcChunkPos(c->getIndex(),tflist);
+
+ if (c->getStatus() == Chunk::MMAPPED)
+ {
+ // mapped chunks are easy
+ CacheFile* fd = files.find(tflist[0]);
+ if (!fd)
+ return;
+
+ fd->unmap(c->getData(),c->getSize());
+ c->clear();
+ c->setStatus(Chunk::ON_DISK);
+ return;
+ }
+
+ // Out() << "Writing to " << tflist.count() << " files " << endl;
+ Uint64 written = 0; // number of bytes written
+ for (Uint32 i = 0;i < tflist.count();i++)
+ {
+ const TorrentFile & f = tor.getFile(tflist[i]);
+ CacheFile* fd = files.find(tflist[i]);
+ DNDFile* dfd = dnd_files.find(tflist[i]);
+
+ // first calculate offset into file
+ // only the first file can have an offset
+ // the following files will start at the beginning
+ Uint64 off = 0;
+ Uint32 to_write = 0;
+ if (i == 0)
+ {
+ off = FileOffset(c,f,tor.getChunkSize());
+ }
+
+ // the amount of data we can write to this file
+ if (tflist.count() == 1)
+ to_write = c->getSize();
+ else if (i == 0)
+ to_write = f.getLastChunkSize();
+ else if (i == tflist.count() - 1)
+ to_write = c->getSize() - written;
+ else
+ to_write = f.getSize();
+
+ // Out() << "to_write " << to_write << endl;
+ // write the data
+ if (fd)
+ fd->write(c->getData() + written,to_write,off);
+ else if (dfd)
+ {
+ if (i == 0)
+ dfd->writeLastChunk(c->getData() + written,to_write);
+ else if (i == tflist.count() - 1)
+ dfd->writeFirstChunk(c->getData() + written,to_write);
+ else
+ dfd->writeFirstChunk(c->getData() + written,to_write);
+ }
+
+ written += to_write;
+ }
+
+ // set the chunk to on disk and clear it
+ c->clear();
+ c->setStatus(Chunk::ON_DISK);
+ }
+
+ void MultiFileCache::downloadStatusChanged(TorrentFile* tf, bool download)
+ {
+ bool dnd = !download;
+ QString dnd_dir = tmpdir + "dnd" + bt::DirSeparator();
+ // if it is dnd and it is already in the dnd tree do nothing
+ if (dnd && bt::Exists(dnd_dir + tf->getPath() + ".dnd"))
+ return;
+
+ // if it is !dnd and it is already in the output_dir tree do nothing
+ if (!dnd && bt::Exists(output_dir + tf->getPath()))
+ return;
+
+
+ DNDFile* dfd = 0;
+ CacheFile* fd = 0;
+ try
+ {
+
+ if (dnd && bt::Exists(dnd_dir + tf->getPath()))
+ {
+ // old download, we need to convert it
+ // save first and last chunk of the file
+ saveFirstAndLastChunk(tf,dnd_dir + tf->getPath(),dnd_dir + tf->getPath() + ".dnd");
+ // delete symlink
+ bt::Delete(cache_dir + tf->getPath());
+ bt::Delete(dnd_dir + tf->getPath()); // delete old dnd file
+ // recreate it
+ bt::SymLink(dnd_dir + tf->getPath() + ".dnd",cache_dir + tf->getPath());
+
+ files.erase(tf->getIndex());
+ dfd = new DNDFile(dnd_dir + tf->getPath() + ".dnd");
+ dfd->checkIntegrity();
+ dnd_files.insert(tf->getIndex(),dfd);
+ }
+ else if (dnd)
+ {
+ // save first and last chunk of the file
+ if (bt::Exists(output_dir + tf->getPath()))
+ saveFirstAndLastChunk(tf,output_dir + tf->getPath(),dnd_dir + tf->getPath() + ".dnd");
+
+ // delete symlink
+ bt::Delete(cache_dir + tf->getPath());
+ // delete data file
+ bt::Delete(output_dir + tf->getPath(),true);
+ // recreate it
+ bt::SymLink(dnd_dir + tf->getPath() + ".dnd",cache_dir + tf->getPath());
+
+ files.erase(tf->getIndex());
+ dfd = new DNDFile(dnd_dir + tf->getPath() + ".dnd");
+ dfd->checkIntegrity();
+ dnd_files.insert(tf->getIndex(),dfd);
+ }
+ else
+ {
+ // recreate the file
+ recreateFile(tf,dnd_dir + tf->getPath() + ".dnd",output_dir + tf->getPath());
+ // delete symlink and dnd file
+ bt::Delete(cache_dir + tf->getPath());
+ bt::Delete(dnd_dir + tf->getPath() + ".dnd");
+ // recreate it
+ bt::SymLink(output_dir + tf->getPath(),cache_dir + tf->getPath());
+ dnd_files.erase(tf->getIndex());
+
+ fd = new CacheFile();
+ fd->open(output_dir + tf->getPath(),tf->getSize());
+ files.insert(tf->getIndex(),fd);
+ }
+ }
+ catch (bt::Error & err)
+ {
+ delete fd;
+ delete dfd;
+ Out() << err.toString() << endl;
+ }
+ }
+
+
+
+ void MultiFileCache::saveFirstAndLastChunk(TorrentFile* tf,const QString & src_file,const QString & dst_file)
+ {
+ DNDFile out(dst_file);
+ File fptr;
+ if (!fptr.open(src_file,"rb"))
+ throw Error(i18n("Cannot open file %1 : %2").arg(src_file).arg(fptr.errorString()));
+
+ Uint32 cs = 0;
+ if (tf->getFirstChunk() == tor.getNumChunks() - 1)
+ {
+ cs = tor.getFileLength() % tor.getChunkSize();
+ if (cs == 0)
+ cs = tor.getChunkSize();
+ }
+ else
+ cs = tor.getChunkSize();
+
+ Uint8* tmp = new Uint8[tor.getChunkSize()];
+ try
+ {
+ fptr.read(tmp,cs - tf->getFirstChunkOffset());
+ out.writeFirstChunk(tmp,cs - tf->getFirstChunkOffset());
+
+ if (tf->getFirstChunk() != tf->getLastChunk())
+ {
+ Uint64 off = FileOffset(tf->getLastChunk(),*tf,tor.getChunkSize());
+ fptr.seek(File::BEGIN,off);
+ fptr.read(tmp,tf->getLastChunkSize());
+ out.writeLastChunk(tmp,tf->getLastChunkSize());
+ }
+ delete [] tmp;
+ }
+ catch (...)
+ {
+ delete [] tmp;
+ throw;
+ }
+ }
+
+ void MultiFileCache::recreateFile(TorrentFile* tf,const QString & dnd_file,const QString & output_file)
+ {
+ DNDFile dnd(dnd_file);
+
+ // create the output file
+ bt::Touch(output_file);
+ // truncate it
+ try
+ {
+ bool res = false;
+
+ #ifdef HAVE_XFS_XFS_H
+ if( (! res) && (Settings::fullDiskPreallocMethod() == 1) )
+ {
+ res = XfsPreallocate(output_file, tf->getSize());
+ }
+ #endif
+
+ if(! res)
+ {
+ bt::TruncateFile(output_file,tf->getSize());
+ }
+ }
+ catch (bt::Error & e)
+ {
+ // first attempt failed, must be fat so try that
+ if (!FatPreallocate(output_file,tf->getSize()))
+ {
+ throw Error(i18n("Cannot preallocate diskspace : %1").arg(strerror(errno)));
+ }
+ }
+
+ Uint32 cs = 0;
+ if (tf->getFirstChunk() == tor.getNumChunks() - 1)
+ {
+ cs = tor.getFileLength() % tor.getChunkSize();
+ if (cs == 0)
+ cs = tor.getChunkSize();
+ }
+ else
+ cs = tor.getChunkSize();
+
+ File fptr;
+ if (!fptr.open(output_file,"r+b"))
+ throw Error(i18n("Cannot open file %1 : %2").arg(output_file).arg(fptr.errorString()));
+
+
+ Uint32 ts = cs - tf->getFirstChunkOffset() > tf->getLastChunkSize() ?
+ cs - tf->getFirstChunkOffset() : tf->getLastChunkSize();
+ Uint8* tmp = new Uint8[ts];
+
+ try
+ {
+ dnd.readFirstChunk(tmp,0,cs - tf->getFirstChunkOffset());
+ fptr.write(tmp,cs - tf->getFirstChunkOffset());
+
+ if (tf->getFirstChunk() != tf->getLastChunk())
+ {
+ Uint64 off = FileOffset(tf->getLastChunk(),*tf,tor.getChunkSize());
+ fptr.seek(File::BEGIN,off);
+ dnd.readLastChunk(tmp,0,tf->getLastChunkSize());
+ fptr.write(tmp,tf->getLastChunkSize());
+ }
+ delete [] tmp;
+ }
+ catch (...)
+ {
+ delete [] tmp;
+ throw;
+ }
+ }
+
+ void MultiFileCache::preallocateDiskSpace(PreallocationThread* prealloc)
+ {
+ Out() << "MultiFileCache::preallocateDiskSpace" << endl;
+ PtrMap<Uint32,CacheFile>::iterator i = files.begin();
+ while (i != files.end())
+ {
+ CacheFile* cf = i->second;
+ if (!prealloc->isStopped())
+ {
+ cf->preallocate(prealloc);
+ }
+ else
+ {
+ // we got interrupted tell the thread we are not finished and return
+ prealloc->setNotFinished();
+ return;
+ }
+ i++;
+ }
+ }
+
+ bool MultiFileCache::hasMissingFiles(QStringList & sl)
+ {
+ bool ret = false;
+ for (Uint32 i = 0;i < tor.getNumFiles();i++)
+ {
+ TorrentFile & tf = tor.getFile(i);
+ if (tf.doNotDownload())
+ continue;
+
+ QString p = cache_dir + tf.getPath();
+ QFileInfo fi(p);
+ // always use symlink first, file might have been moved
+ if (!fi.exists())
+ {
+ ret = true;
+ p = fi.readLink();
+ if (p.isNull())
+ p = output_dir + tf.getPath();
+ sl.append(p);
+ tf.setMissing(true);
+ }
+ else
+ {
+ p = output_dir + tf.getPath();
+ // no symlink so try the actual file
+ if (!bt::Exists(p))
+ {
+ ret = true;
+ sl.append(p);
+ tf.setMissing(true);
+ }
+ }
+ }
+ return ret;
+ }
+
+ static void DeleteEmptyDirs(const QString & output_dir,const QString & fpath)
+ {
+ QStringList sl = QStringList::split(bt::DirSeparator(),fpath);
+ // remove the last, which is just the filename
+ sl.pop_back();
+
+ while (sl.count() > 0)
+ {
+ QString path = output_dir;
+ // reassemble the full directory path
+ for (QStringList::iterator itr = sl.begin(); itr != sl.end();itr++)
+ path += *itr + bt::DirSeparator();
+
+ QDir dir(path);
+ QStringList el = dir.entryList(QDir::All|QDir::System|QDir::Hidden);
+ el.remove(".");
+ el.remove("..");
+ if (el.count() == 0)
+ {
+ // no childern so delete the directory
+ Out(SYS_GEN|LOG_IMPORTANT) << "Deleting empty directory : " << path << endl;
+ bt::Delete(path,true);
+ sl.pop_back(); // remove the last so we can go one higher
+ }
+ else
+ {
+
+ // children, so we cannot delete any more directories higher up
+ return;
+ }
+ }
+
+ // now the output_dir itself
+ QDir dir(output_dir);
+ QStringList el = dir.entryList(QDir::All|QDir::System|QDir::Hidden);
+ el.remove(".");
+ el.remove("..");
+ if (el.count() == 0)
+ {
+ Out(SYS_GEN|LOG_IMPORTANT) << "Deleting empty directory : " << output_dir << endl;
+ bt::Delete(output_dir,true);
+ }
+ }
+
+ void MultiFileCache::deleteDataFiles()
+ {
+ for (Uint32 i = 0;i < tor.getNumFiles();i++)
+ {
+ TorrentFile & tf = tor.getFile(i);
+ QString fpath = tf.getPath();
+ if (!tf.doNotDownload())
+ {
+ // first delete the file
+ bt::Delete(output_dir + fpath);
+ }
+
+ // check for subdirectories
+ DeleteEmptyDirs(output_dir,fpath);
+ }
+ }
+
+ Uint64 MultiFileCache::diskUsage()
+ {
+ Uint64 sum = 0;
+
+ for (Uint32 i = 0;i < tor.getNumFiles();i++)
+ {
+ TorrentFile & tf = tor.getFile(i);
+ if (tf.doNotDownload())
+ continue;
+
+ try
+ {
+ CacheFile* cf = files.find(i);
+ if (cf)
+ {
+ sum += cf->diskUsage();
+ }
+ else
+ {
+ // doesn't exist yet, must be before open is called
+ // so create one and delete it right after
+ cf = new CacheFile();
+ cf->open(cache_dir + tf.getPath(),tf.getSize());
+ sum += cf->diskUsage();
+ delete cf;
+ }
+ }
+ catch (bt::Error & err) // make sure we catch any exceptions
+ {
+ Out(SYS_DIO|LOG_DEBUG) << "Error: " << err.toString() << endl;
+ }
+ }
+
+ return sum;
+ }
+
+ ///////////////////////////////
+
+ Uint64 FileOffset(Chunk* c,const TorrentFile & f,Uint64 chunk_size)
+ {
+ return FileOffset(c->getIndex(),f,chunk_size);
+ }
+
+ Uint64 FileOffset(Uint32 cindex,const TorrentFile & f,Uint64 chunk_size)
+ {
+ return f.fileOffset(cindex,chunk_size);
+ }
+
+}
diff --git a/libktorrent/torrent/multifilecache.h b/libktorrent/torrent/multifilecache.h
new file mode 100644
index 0000000..9c1280e
--- /dev/null
+++ b/libktorrent/torrent/multifilecache.h
@@ -0,0 +1,74 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef BTMULTIFILECACHE_H
+#define BTMULTIFILECACHE_H
+
+
+#include <util/ptrmap.h>
+#include "cache.h"
+#include "settings.h"
+
+namespace bt
+{
+ class DNDFile;
+ class CacheFile;
+
+ /**
+ * @author Joris Guisson
+ * @brief Cache for multi file torrents
+ *
+ * This class manages a multi file torrent cache. Everything gets stored in the
+ * correct files immediately.
+ */
+ class MultiFileCache : public Cache
+ {
+ QString cache_dir,output_dir;
+ PtrMap<Uint32,CacheFile> files;
+ PtrMap<Uint32,DNDFile> dnd_files;
+ public:
+ MultiFileCache(Torrent& tor,const QString & tmpdir,const QString & datadir,bool custom_output_name);
+ virtual ~MultiFileCache();
+
+ virtual void changeTmpDir(const QString& ndir);
+ virtual void create();
+ virtual void load(Chunk* c);
+ virtual void save(Chunk* c);
+ virtual bool prep(Chunk* c);
+ virtual void close();
+ virtual void open();
+ virtual QString getOutputPath() const;
+ virtual void changeOutputPath(const QString & outputpath);
+ virtual KIO::Job* moveDataFiles(const QString & ndir);
+ virtual void moveDataFilesCompleted(KIO::Job* job);
+ virtual void preallocateDiskSpace(PreallocationThread* prealloc);
+ virtual bool hasMissingFiles(QStringList & sl);
+ virtual void deleteDataFiles();
+ virtual Uint64 diskUsage();
+ private:
+ void touch(TorrentFile & tf);
+ virtual void downloadStatusChanged(TorrentFile*, bool);
+ QString guessDataDir();
+ void saveFirstAndLastChunk(TorrentFile* tf,const QString & src_file,const QString & dst_file);
+ void recreateFile(TorrentFile* tf,const QString & dnd_file,const QString & output_file);
+ };
+
+}
+
+#endif
diff --git a/libktorrent/torrent/newchokealgorithm.cpp b/libktorrent/torrent/newchokealgorithm.cpp
new file mode 100644
index 0000000..875f356
--- /dev/null
+++ b/libktorrent/torrent/newchokealgorithm.cpp
@@ -0,0 +1,345 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#if 0
+#include <util/log.h>
+#include <util/timer.h>
+#include <util/functions.h>
+#include <torrent/globals.h>
+#include <interfaces/functions.h>
+#include "newchokealgorithm.h"
+#include "peermanager.h"
+#include "peer.h"
+#include "packetwriter.h"
+#include "peeruploader.h"
+
+
+using namespace kt;
+
+namespace bt
+{
+
+
+
+ NewChokeAlgorithm::NewChokeAlgorithm(): ChokeAlgorithm()
+ {
+ round_state = 1;
+ }
+
+
+ NewChokeAlgorithm::~NewChokeAlgorithm()
+ {}
+
+ int RevDownloadRateCmp(Peer* a,Peer* b)
+ {
+ if (b->getDownloadRate() > a->getDownloadRate())
+ return 1;
+ else if (a->getDownloadRate() > b->getDownloadRate())
+ return -1;
+ else
+ return 0;
+ }
+
+ void NewChokeAlgorithm::doChokingLeechingState(PeerManager & pman,ChunkManager & cman,const kt::TorrentStats & stats)
+ {
+ Uint32 num_peers = pman.getNumConnectedPeers();
+ if (num_peers == 0)
+ return;
+
+ Uint32 now = GetCurrentTime();
+ Peer* poup = pman.findPeer(opt_unchoked_peer_id);
+ Peer* unchokers[] = {0,0,0,0};
+
+ // first find the planned optimistic unchoked peer if we are in the correct round
+ if (round_state == 1 || poup == 0)
+ {
+ opt_unchoked_peer_id = findPlannedOptimisticUnchokedPeer(pman);
+ poup = pman.findPeer(opt_unchoked_peer_id);
+ }
+
+ PeerPtrList peers,other;
+ // now get all the peers who are interested and have sent us a piece in the
+ // last 30 seconds
+ for (Uint32 i = 0;i < num_peers;i++)
+ {
+ Peer* p = pman.getPeer(i);
+ if (!p)
+ continue;
+
+ if (!p->isSeeder())
+ {
+ if (p->isInterested() && now - p->getTimeSinceLastPiece() <= 30000)
+ peers.append(p);
+ else
+ other.append(p);
+ }
+ else
+ {
+ p->choke();
+ }
+ }
+
+ // sort them using a reverse download rate compare
+ // so that the fastest downloaders are in front
+ peers.setCompareFunc(RevDownloadRateCmp);
+ peers.sort();
+ other.setCompareFunc(RevDownloadRateCmp);
+ other.sort();
+
+ // get the first tree and punt them in the unchokers
+ for (Uint32 i = 0;i < 3;i++)
+ {
+ if (i < peers.count())
+ {
+ unchokers[i] = peers.at(i);
+ }
+ }
+
+ // see if poup if part of the first 3
+ // and if necessary replace it
+ bool poup_in_unchokers = false;
+ Uint32 attempts = 0;
+ do
+ {
+ poup_in_unchokers = false;
+ for (Uint32 i = 0;i < 3;i++)
+ {
+ if (unchokers[i] != poup)
+ continue;
+
+ opt_unchoked_peer_id = findPlannedOptimisticUnchokedPeer(pman);
+ poup = pman.findPeer(opt_unchoked_peer_id);
+ poup_in_unchokers = true;
+ break;
+ }
+ // we don't want to keep trying this forever, so limit it to 5 atttempts
+ attempts++;
+ }while (poup_in_unchokers && attempts < 5);
+
+ unchokers[3] = poup;
+
+ Uint32 other_idx = 0;
+ Uint32 peers_idx = 3;
+ // unchoke the 4 unchokers
+ for (Uint32 i = 0;i < 4;i++)
+ {
+ if (!unchokers[i])
+ {
+ // pick some other peer to unchoke
+ unchokers[i] = peers.at(peers_idx++);
+ if (unchokers[i] == poup) // it must not be equal to the poup
+ unchokers[i] = peers.at(peers_idx++);
+
+ // nobody in the peers list, try the others list
+ if (!unchokers[i])
+ unchokers[i] = other.at(other_idx++);
+ }
+
+ if (unchokers[i])
+ unchokers[i]->getPacketWriter().sendUnchoke();
+ }
+
+ // choke the rest
+ for (Uint32 i = 0;i < num_peers;i++)
+ {
+ Peer* p = pman.getPeer(i);
+ if (p == unchokers[0] || p == unchokers[1] || p == unchokers[2] || p == unchokers[3])
+ continue;
+ if (p)
+ p->choke();
+ }
+
+ round_state++;
+ if (round_state > 3)
+ round_state = 1;
+ }
+
+ Uint32 NewChokeAlgorithm::findPlannedOptimisticUnchokedPeer(PeerManager& pman)
+ {
+ Uint32 num_peers = pman.getNumConnectedPeers();
+ if (num_peers == 0)
+ return UNDEFINED_ID;
+
+ // find a random peer that is choked and interested
+ Uint32 start = rand() % num_peers;
+ Uint32 i = (start + 1) % num_peers;
+ while (i != start)
+ {
+ Peer* p = pman.getPeer(i);
+ if (p && p->isChoked() && p->isInterested() && !p->isSeeder())
+ return p->getID();
+ i = (i + 1) % num_peers;
+ }
+
+ // we do not expect to have 4 billion peers
+ return 0xFFFFFFFF;
+ }
+
+ //////////////////////////////////////////////
+
+ int NChokeCmp(Peer* a,Peer* b)
+ {
+ Uint32 now = GetCurrentTime();
+ // if they have pending upload requests or they were unchoked in the last 20 seconds,
+ // they are category 1
+ bool a_first_class = a->getPeerUploader()->getNumRequests() > 0 ||
+ (now - a->getUnchokeTime() <= 20000);
+ bool b_first_class = b->getPeerUploader()->getNumRequests() > 0 ||
+ (now - b->getUnchokeTime() <= 20000);
+
+ if (a_first_class && !b_first_class)
+ {
+ // category 1 come first
+ return -1;
+ }
+ else if (!a_first_class && b_first_class)
+ {
+ // category 1 come first
+ return 1;
+ }
+ else
+ {
+ // use upload rate to differentiate peers of the same class
+ if (a->getUploadRate() > b->getUploadRate())
+ return -1;
+ else if (b->getUploadRate() > a->getUploadRate())
+ return 1;
+ else
+ return 0;
+ }
+ }
+
+
+ void NewChokeAlgorithm::doChokingSeedingState(PeerManager & pman,ChunkManager & cman,const kt::TorrentStats & stats)
+ {
+ Uint32 num_peers = pman.getNumConnectedPeers();
+ if (num_peers == 0)
+ return;
+
+ // first get all unchoked and interested peers
+ PeerPtrList peers,others;
+ for (Uint32 i = 0;i < num_peers;i++)
+ {
+ Peer* p = pman.getPeer(i);
+ if (!p)
+ continue;
+
+ if (!p->isSeeder())
+ {
+ if (!p->isChoked() && p->isInterested())
+ peers.append(p);
+ else
+ others.append(p);
+ }
+ else
+ {
+ p->choke();
+ }
+ }
+
+ // sort them
+ peers.setCompareFunc(NChokeCmp);
+ peers.sort();
+ others.setCompareFunc(NChokeCmp);
+ others.sort();
+
+ // first round so take the 4 first peers
+ if (round_state == 1)
+ {
+ Uint32 num_unchoked = 0;
+ for (Uint32 i = 0;i < peers.count();i++)
+ {
+ Peer* p = peers.at(i);
+ if (!p)
+ continue;
+
+ if (num_unchoked < 4)
+ {
+ p->getPacketWriter().sendUnchoke();
+ num_unchoked++;
+ }
+ else
+ p->choke();
+ }
+ // go over the other peers and unchoke, if we do not have enough
+ for (Uint32 i = 0;i < others.count();i++)
+ {
+ Peer* p = others.at(i);
+ if (!p)
+ continue;
+
+ if (num_unchoked < 4)
+ {
+ p->getPacketWriter().sendUnchoke();
+ num_unchoked++;
+ }
+ else
+ p->choke();
+ }
+ }
+ else
+ {
+ Uint32 rnd = 0;
+ if (peers.count() > 3)
+ rnd = 3 + rand() % (peers.count() - 3);
+
+ Uint32 num_unchoked = 0;
+ // take the first 3 and a random one
+ for (Uint32 i = 0;i < peers.count();i++)
+ {
+ Peer* p = peers.at(i);
+ if (!p)
+ continue;
+
+ if (num_unchoked < 4 || i == rnd)
+ {
+ p->getPacketWriter().sendUnchoke();
+ num_unchoked++;
+ }
+ else
+ p->choke();
+ }
+
+ // go over the other peers and unchoke, if we do not have enough
+ for (Uint32 i = 0;i < others.count();i++)
+ {
+ Peer* p = others.at(i);
+ if (!p)
+ continue;
+
+ if (num_unchoked < 4 || i == rnd)
+ {
+ p->getPacketWriter().sendUnchoke();
+ num_unchoked++;
+ }
+ else
+ p->choke();
+ }
+ }
+
+ round_state++;
+ if (round_state > 3)
+ round_state = 1;
+ }
+
+
+
+}
+#endif
+
diff --git a/libktorrent/torrent/newchokealgorithm.h b/libktorrent/torrent/newchokealgorithm.h
new file mode 100644
index 0000000..9a0738a
--- /dev/null
+++ b/libktorrent/torrent/newchokealgorithm.h
@@ -0,0 +1,54 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#if 0
+#ifndef BTNEWCHOKEALGORITHM_H
+#define BTNEWCHOKEALGORITHM_H
+
+#include <choker.h>
+
+namespace bt
+{
+
+ /**
+ * @author Joris Guisson
+ *
+ * The new choking algorithm.
+ */
+ class NewChokeAlgorithm : public ChokeAlgorithm
+ {
+ Uint32 round_state;
+ public:
+ NewChokeAlgorithm();
+ virtual ~NewChokeAlgorithm();
+
+ virtual void doChokingLeechingState(PeerManager & pman,ChunkManager & cman,const kt::TorrentStats & stats);
+ virtual void doChokingSeedingState(PeerManager & pman,ChunkManager & cman,const kt::TorrentStats & stats);
+ private:
+ void doChokingLeecherState(PeerManager& pman);
+ void doChokingSeederState(PeerManager& pman);
+
+ Uint32 findPlannedOptimisticUnchokedPeer(PeerManager& pman);
+ };
+
+}
+
+#endif
+#endif
+
diff --git a/libktorrent/torrent/oldchokealgorithm.cpp b/libktorrent/torrent/oldchokealgorithm.cpp
new file mode 100644
index 0000000..e24d63a
--- /dev/null
+++ b/libktorrent/torrent/oldchokealgorithm.cpp
@@ -0,0 +1,223 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <interfaces/functions.h>
+#include "oldchokealgorithm.h"
+#include "peer.h"
+#include "packetwriter.h"
+#include "peermanager.h"
+
+
+using namespace kt;
+
+namespace bt
+{
+ int UploadRateCmp(Peer* pa,Peer* pb)
+ {
+ return CompareVal(pa->getUploadRate(),pb->getUploadRate());
+ }
+
+ int DownloadRateCmp(Peer* pa,Peer* pb)
+ {
+ return CompareVal(pa->getDownloadRate(),pb->getDownloadRate());
+ }
+
+
+ OldChokeAlgorithm::OldChokeAlgorithm(): ChokeAlgorithm()
+ {
+ opt_unchoke_index = 0;
+ opt_unchoke = 1;
+ }
+
+
+ OldChokeAlgorithm::~OldChokeAlgorithm()
+ {}
+
+
+ void OldChokeAlgorithm::doChoking(PeerManager& pman, bool have_all)
+ {
+ if (pman.getNumConnectedPeers() == 0)
+ return;
+
+ downloaders.clear();
+ interested.clear();
+ not_interested.clear();
+
+ // first alert everybody that we're interested or not
+ sendInterested(pman,have_all);
+ // get who is interested and not
+ updateInterested(pman);
+ // them sort them;
+ if (have_all)
+ {
+ interested.setCompareFunc(DownloadRateCmp);
+ interested.sort();
+ not_interested.setCompareFunc(DownloadRateCmp);
+ not_interested.sort();
+ }
+ else
+ {
+ interested.setCompareFunc(UploadRateCmp);
+ interested.sort();
+ not_interested.setCompareFunc(UploadRateCmp);
+ not_interested.sort();
+ }
+ // determine the downloaders
+ updateDownloaders();
+ // unchoke the not_interested peers
+ // which have a faster upload rate then the downloaders
+ sendUnchokes(have_all);
+ // optimisticly unchoke somebody
+ optimisticUnchoke(pman);
+ }
+
+ void OldChokeAlgorithm::updateInterested(PeerManager& pman)
+ {
+ for (Uint32 i = 0;i < pman.getNumConnectedPeers();i++)
+ {
+ Peer* p = pman.getPeer(i);
+
+ if (p->getID() == opt_unchoked_peer_id)
+ continue;
+
+ if (p->isInterested())
+ {
+ interested.append(p);
+ }
+ else
+ {
+ not_interested.append(p);
+ }
+ }
+ }
+
+ void OldChokeAlgorithm::updateDownloaders()
+ {
+ QPtrList<Peer>::iterator itr = interested.begin();
+ int num = 0;
+ // send all downloaders an unchoke
+ for (;itr != interested.end();itr++)
+ {
+ Peer* p = *itr;
+
+ if (p->getID() == opt_unchoked_peer_id)
+ continue;
+
+ if (num < 4)
+ {
+ p->choke();
+ downloaders.append(p);
+ num++;
+ }
+ else
+ {
+ p->choke();
+ }
+ }
+ }
+
+ void OldChokeAlgorithm::sendInterested(PeerManager& pman,bool have_all)
+ {
+ for (Uint32 i = 0;i < pman.getNumConnectedPeers();i++)
+ {
+ Peer* p = pman.getPeer(i);
+ PacketWriter & pout = p->getPacketWriter();
+ // if we don't have the entire file, send an intereseted message,
+ // else we're not intereseted
+ if (have_all && p->areWeInterested())
+ pout.sendNotInterested();
+ else if (!have_all && !p->areWeInterested())
+ pout.sendInterested();
+ }
+ }
+
+ void OldChokeAlgorithm::sendUnchokes(bool have_all)
+ {
+ if (downloaders.count() == 0)
+ return;
+
+ QPtrList<Peer>::iterator itr = not_interested.begin();
+ // fd = fastest_downloader
+ Peer* fd = downloaders.first();
+ // send all downloaders an unchoke
+ for (;itr != not_interested.end();itr++)
+ {
+ Peer* p = *itr;
+ if (p->getID() == opt_unchoked_peer_id)
+ continue;
+
+ if ((have_all && p->getDownloadRate() > fd->getDownloadRate()) ||
+ (!have_all && p->getUploadRate() > fd->getUploadRate()))
+ {
+ p->getPacketWriter().sendUnchoke();
+ }
+ else
+ {
+ p->getPacketWriter().sendChoke();
+ }
+ }
+ }
+
+ void OldChokeAlgorithm::optimisticUnchoke(PeerManager& pman)
+ {
+ if (pman.getNumConnectedPeers() == 0)
+ return;
+
+ // only switch optimistic unchoked peer every 30 seconds
+ // (update interval of choker is 10 seconds)
+ if (opt_unchoke != 3)
+ {
+ opt_unchoke++;
+ return;
+ }
+
+ // Get current time
+ QTime now = QTime::currentTime();
+ QPtrList<Peer> peers; // list to store peers to select from
+
+ // recently connected peers == peers connected in the last 5 minutes
+ const int RECENTLY_CONNECT_THRESH = 5*60;
+
+ for (Uint32 i = 0;i < pman.getNumConnectedPeers();i++)
+ {
+ Peer* p = pman.getPeer(i);
+ if (p->getConnectTime().secsTo(now) < RECENTLY_CONNECT_THRESH)
+ {
+ // we favor recently connected peers 3 times over other peers
+ // so we add them 3 times to the list
+ peers.append(p);
+ peers.append(p);
+ peers.append(p);
+ }
+ else
+ {
+ // not recent, so just add one time
+ peers.append(p);
+ }
+ }
+
+ // draw a random one from the list and send it an unchoke
+ opt_unchoke_index = rand() % peers.count();
+ Peer* lucky_one = peers.at(opt_unchoke_index);
+ lucky_one->getPacketWriter().sendUnchoke();
+ opt_unchoked_peer_id = lucky_one->getID();
+ opt_unchoke = 1;
+ }
+
+}
diff --git a/libktorrent/torrent/oldchokealgorithm.h b/libktorrent/torrent/oldchokealgorithm.h
new file mode 100644
index 0000000..bc813a8
--- /dev/null
+++ b/libktorrent/torrent/oldchokealgorithm.h
@@ -0,0 +1,54 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef BTOLDCHOKEALGORITHM_H
+#define BTOLDCHOKEALGORITHM_H
+
+#include <choker.h>
+
+namespace bt
+{
+
+ /**
+ * @author Joris Guisson
+ *
+ * The old choking algorithm as it is described on wiki.theory.org.
+ */
+ class OldChokeAlgorithm : public ChokeAlgorithm
+ {
+ int opt_unchoke_index;
+ int opt_unchoke;
+
+ PeerPtrList downloaders,interested,not_interested;
+ public:
+ OldChokeAlgorithm();
+ virtual ~OldChokeAlgorithm();
+
+ virtual void doChoking(PeerManager& pman, bool have_all);
+ private:
+ void updateInterested(PeerManager& pman);
+ void updateDownloaders();
+ void sendInterested(PeerManager& pman,bool have_all);
+ void sendUnchokes(bool have_all);
+ void optimisticUnchoke(PeerManager& pman);
+ };
+
+}
+
+#endif
diff --git a/libktorrent/torrent/packet.cpp b/libktorrent/torrent/packet.cpp
new file mode 100644
index 0000000..febede2
--- /dev/null
+++ b/libktorrent/torrent/packet.cpp
@@ -0,0 +1,175 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <qstring.h>
+#include <string.h>
+#include <util/log.h>
+#include <util/bitset.h>
+#include <util/functions.h>
+#include <torrent/globals.h>
+#include "packet.h"
+#include "request.h"
+#include "chunk.h"
+#include "peer.h"
+
+namespace bt
+{
+
+ static Uint8* AllocPacket(Uint32 size,Uint8 type)
+ {
+ Uint8* data = new Uint8[size];
+ WriteUint32(data,0,size - 4);
+ data[4] = type;
+ return data;
+ }
+
+
+ Packet::Packet(Uint8 type) : data(0),size(0),written(0)
+ {
+ size = 5;
+ data = AllocPacket(size,type);
+ }
+
+ Packet::Packet(Uint16 port) : data(0),size(0),written(0)
+ {
+ size = 7;
+ data = AllocPacket(size,PORT);
+ WriteUint16(data,5,port);
+
+ }
+
+ Packet::Packet(Uint32 chunk,Uint8 type) : data(0),size(0),written(0)
+ {
+ size = 9;
+ data = AllocPacket(size,type);
+ WriteUint32(data,5,chunk);
+ }
+
+ Packet::Packet(const BitSet & bs) : data(0),size(0),written(0)
+ {
+ size = 5 + bs.getNumBytes();
+ data = AllocPacket(size,BITFIELD);
+ memcpy(data+5,bs.getData(),bs.getNumBytes());
+ }
+
+ Packet::Packet(const Request & r,Uint8 type) : data(0),size(0),written(0)
+ {
+ size = 17;
+ data = AllocPacket(size,type);
+ WriteUint32(data,5,r.getIndex());
+ WriteUint32(data,9,r.getOffset());
+ WriteUint32(data,13,r.getLength());
+ }
+
+ Packet::Packet(Uint32 index,Uint32 begin,Uint32 len,Chunk* ch) : data(0),size(0),written(0)
+ {
+ size = 13 + len;
+ data = AllocPacket(size,PIECE);
+ WriteUint32(data,5,index);
+ WriteUint32(data,9,begin);
+ memcpy(data+13,ch->getData() + begin,len);
+ }
+
+ Packet::Packet(Uint8 ext_id,const QByteArray & ext_data) : data(0),size(0),written(0)
+ {
+ size = 6 + ext_data.size();
+ data = AllocPacket(size,EXTENDED);
+ data[5] = ext_id;
+ memcpy(data + 6,ext_data.data(),ext_data.size());
+ }
+
+ Packet::~Packet()
+ {
+ delete [] data;
+ }
+
+ bool Packet::isPiece(const Request & req) const
+ {
+ if (data[4] == PIECE)
+ {
+ if (ReadUint32(data,5) != req.getIndex())
+ return false;
+
+ if (ReadUint32(data,9) != req.getOffset())
+ return false;
+
+ if (ReadUint32(data,13) != req.getLength())
+ return false;
+
+ return true;
+ }
+ return false;
+ }
+
+ Packet* Packet::makeRejectOfPiece()
+ {
+ if (getType() != PIECE)
+ return 0;
+
+ Uint32 idx = bt::ReadUint32(data,5);
+ Uint32 off = bt::ReadUint32(data,9);
+ Uint32 len = size - 13;
+
+ // Out(SYS_CON|LOG_DEBUG) << "Packet::makeRejectOfPiece " << idx << " " << off << " " << len << endl;
+ return new Packet(Request(idx,off,len,0),bt::REJECT_REQUEST);
+ }
+
+ /*
+ QString Packet::debugString() const
+ {
+ if (!data)
+ return QString::null;
+
+ switch (data[4])
+ {
+ case CHOKE : return QString("CHOKE %1 %2").arg(hdr_length).arg(data_length);
+ case UNCHOKE : return QString("UNCHOKE %1 %2").arg(hdr_length).arg(data_length);
+ case INTERESTED : return QString("INTERESTED %1 %2").arg(hdr_length).arg(data_length);
+ case NOT_INTERESTED : return QString("NOT_INTERESTED %1 %2").arg(hdr_length).arg(data_length);
+ case HAVE : return QString("HAVE %1 %2").arg(hdr_length).arg(data_length);
+ case BITFIELD : return QString("BITFIELD %1 %2").arg(hdr_length).arg(data_length);
+ case PIECE : return QString("PIECE %1 %2").arg(hdr_length).arg(data_length);
+ case REQUEST : return QString("REQUEST %1 %2").arg(hdr_length).arg(data_length);
+ case CANCEL : return QString("CANCEL %1 %2").arg(hdr_length).arg(data_length);
+ default: return QString("UNKNOWN %1 %2").arg(hdr_length).arg(data_length);
+ }
+ }
+ */
+ bool Packet::isOK() const
+ {
+ if (!data)
+ return false;
+
+ return true;
+ }
+
+ Uint32 Packet::putInOutputBuffer(Uint8* buf,Uint32 max_to_put,bool & piece)
+ {
+ piece = data[4] == PIECE;
+ Uint32 bw = size - written;
+ if (!bw) // nothing to write
+ return 0;
+
+ if (bw > max_to_put)
+ bw = max_to_put;
+ memcpy(buf,data + written,bw);
+ written += bw;
+ return bw;
+ }
+}
diff --git a/libktorrent/torrent/packet.h b/libktorrent/torrent/packet.h
new file mode 100644
index 0000000..9259d31
--- /dev/null
+++ b/libktorrent/torrent/packet.h
@@ -0,0 +1,91 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef BTPACKET_H
+#define BTPACKET_H
+
+#include "globals.h"
+
+class QString;
+
+namespace bt
+{
+ class BitSet;
+ class Request;
+ class Chunk;
+ class Peer;
+
+ /**
+ * @author Joris Guisson
+ *
+ * Packet off data, which gets sent to a Peer
+ */
+ class Packet
+ {
+ Uint8* data;
+ Uint32 size;
+ Uint32 written;
+ public:
+ Packet(Uint8 type);
+ Packet(Uint16 port);
+ Packet(Uint32 chunk,Uint8 type);
+ Packet(const BitSet & bs);
+ Packet(const Request & req,Uint8 type);
+ Packet(Uint32 index,Uint32 begin,Uint32 len,Chunk* ch);
+ Packet(Uint8 ext_id,const QByteArray & ext_data); // extension protocol packet
+ virtual ~Packet();
+
+ Uint8 getType() const {return data ? data[4] : 0;}
+
+ bool isOK() const;
+
+ const Uint8* getData() const {return data;}
+ Uint32 getDataLength() const {return size;}
+
+ Uint32 isSent() const {return written == size;}
+
+ /**
+ * If this packet is a piece, make a reject for it.
+ * @return The newly created Packet, 0 if this is not a piece
+ */
+ Packet* makeRejectOfPiece();
+
+ /// Are we sending this packet ?
+ bool sending() const {return written > 0;}
+
+ /**
+ * Is this a piece packet which matches a request
+ * @param req The request
+ * @return If this is a piece in response of this request
+ */
+ bool isPiece(const Request & req) const;
+
+ /**
+ * Put the packet in an output buffer.
+ * @param buf The buffer
+ * @param max_to_put Maximum bytes to put
+ * @param piece Set to true if this is a piece
+ * @return The number of bytes put in the buffer
+ */
+ Uint32 putInOutputBuffer(Uint8* buf,Uint32 max_to_put,bool & piece);
+ };
+
+}
+
+#endif
diff --git a/libktorrent/torrent/packetreader.cpp b/libktorrent/torrent/packetreader.cpp
new file mode 100644
index 0000000..8df348b
--- /dev/null
+++ b/libktorrent/torrent/packetreader.cpp
@@ -0,0 +1,247 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+
+//#define LOG_PACKET
+#ifdef LOG_PACKET
+#include <sys/types.h>
+#include <unistd.h>
+#endif
+
+#include <util/log.h>
+#include <util/file.h>
+#include <util/functions.h>
+#include "packetreader.h"
+#include "peer.h"
+
+
+namespace bt
+{
+#ifdef LOG_PACKET
+ static void LogPacket(const Uint8* data,Uint32 size,Uint32 len)
+ {
+ QString file = QString("/tmp/kt-packetreader-%1.log").arg(getpid());
+ File fptr;
+ if (!fptr.open(file,"a"))
+ return;
+
+
+ QString tmp = QString("PACKET len = %1, type = %2\nDATA: \n").arg(len).arg(data[0]);
+
+ fptr.write(tmp.ascii(),tmp.length());
+
+ Uint32 j = 0;
+ if (size <= 40)
+ {
+ for (Uint32 i = 0;i < size;i++)
+ {
+ tmp = QString("0x%1 ").arg(data[i],0,16);
+ fptr.write(tmp.ascii(),tmp.length());
+ j++;
+ if (j > 10)
+ {
+ fptr.write("\n",1);
+ j = 0;
+ }
+ }
+ }
+ else
+ {
+ for (Uint32 i = 0;i < 20;i++)
+ {
+ tmp = QString("0x%1 ").arg(data[i],0,16);
+ fptr.write(tmp.ascii(),tmp.length());
+ j++;
+ if (j > 10)
+ {
+ fptr.write("\n",1);
+ j = 0;
+ }
+ }
+ tmp = QString("\n ... \n");
+ fptr.write(tmp.ascii(),tmp.length());
+ for (Uint32 i = size - 20;i < size;i++)
+ {
+ tmp = QString("0x%1 ").arg(data[i],0,16);
+ fptr.write(tmp.ascii(),tmp.length());
+ j++;
+ if (j > 10)
+ {
+ fptr.write("\n",1);
+ j = 0;
+ }
+ }
+ }
+ fptr.write("\n",1);
+ }
+#endif
+
+ IncomingPacket::IncomingPacket(Uint32 size) : data(0),size(size),read(0)
+ {
+ data = new Uint8[size];
+ }
+
+ IncomingPacket::~IncomingPacket()
+ {
+ delete [] data;
+ }
+
+ PacketReader::PacketReader(Peer* peer)
+ : peer(peer),error(false)
+ {
+ packet_queue.setAutoDelete(true);
+ len_received = -1;
+ }
+
+
+ PacketReader::~PacketReader()
+ {
+ }
+
+
+ void PacketReader::update()
+ {
+ if (error)
+ return;
+
+ mutex.lock();
+ // pass packets to peer
+ while (packet_queue.count() > 0)
+ {
+ IncomingPacket* pck = packet_queue.first();
+ if (pck->read == pck->size)
+ {
+ // full packet is read pass it to peer
+ peer->packetReady(pck->data,pck->size);
+ packet_queue.removeFirst();
+ }
+ else
+ {
+ // packet is not yet full, break out of loop
+ break;
+ }
+ }
+ mutex.unlock();
+ }
+
+ Uint32 PacketReader::newPacket(Uint8* buf,Uint32 size)
+ {
+ Uint32 packet_length = 0;
+ Uint32 am_of_len_read = 0;
+ if (len_received > 0)
+ {
+ if (size < 4 - len_received)
+ {
+ memcpy(len + len_received,buf,size);
+ len_received += size;
+ return size;
+ }
+ else
+ {
+ memcpy(len + len_received,buf,4 - len_received);
+ am_of_len_read = 4 - len_received;
+ len_received = 0;
+ packet_length = ReadUint32(len,0);
+
+ }
+ }
+ else if (size < 4)
+ {
+ memcpy(len,buf,size);
+ len_received = size;
+ return size;
+ }
+ else
+ {
+ packet_length = ReadUint32(buf,0);
+ am_of_len_read = 4;
+ }
+
+ if (packet_length == 0)
+ return am_of_len_read;
+
+ if (packet_length > MAX_PIECE_LEN + 13)
+ {
+ Out(SYS_CON|LOG_DEBUG) << " packet_length too large " << packet_length << endl;
+
+ error = true;
+ return size;
+ }
+
+ IncomingPacket* pck = new IncomingPacket(packet_length);
+ packet_queue.append(pck);
+ return am_of_len_read + readPacket(buf + am_of_len_read,size - am_of_len_read);
+ }
+
+ Uint32 PacketReader::readPacket(Uint8* buf,Uint32 size)
+ {
+ if (!size)
+ return 0;
+
+ IncomingPacket* pck = packet_queue.last();
+ if (pck->read + size >= pck->size)
+ {
+ // we can read the full packet
+ Uint32 tr = pck->size - pck->read;
+ memcpy(pck->data + pck->read,buf,tr);
+ pck->read += tr;
+ return tr;
+ }
+ else
+ {
+ // we can do a partial read
+ Uint32 tr = size;
+ memcpy(pck->data + pck->read,buf,tr);
+ pck->read += tr;
+ return tr;
+ }
+ }
+
+
+ void PacketReader::onDataReady(Uint8* buf,Uint32 size)
+ {
+ if (error)
+ return;
+
+ mutex.lock();
+ if (packet_queue.count() == 0)
+ {
+ Uint32 ret = 0;
+ while (ret < size && !error)
+ {
+ ret += newPacket(buf + ret,size - ret);
+ }
+ }
+ else
+ {
+ Uint32 ret = 0;
+ IncomingPacket* pck = packet_queue.last();
+ if (pck->read == pck->size) // last packet in queue is fully read
+ ret = newPacket(buf,size);
+ else
+ ret = readPacket(buf,size);
+
+ while (ret < size && !error)
+ {
+ ret += newPacket(buf + ret,size - ret);
+ }
+ }
+ mutex.unlock();
+ }
+}
diff --git a/libktorrent/torrent/packetreader.h b/libktorrent/torrent/packetreader.h
new file mode 100644
index 0000000..da1e03e
--- /dev/null
+++ b/libktorrent/torrent/packetreader.h
@@ -0,0 +1,68 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef BTPACKETREADER_H
+#define BTPACKETREADER_H
+
+#include <qmutex.h>
+#include <qptrlist.h>
+#include <net/bufferedsocket.h>
+#include "globals.h"
+
+namespace bt
+{
+ class Peer;
+
+ struct IncomingPacket
+ {
+ Uint8* data;
+ Uint32 size;
+ Uint32 read;
+
+ IncomingPacket(Uint32 size);
+ virtual ~IncomingPacket();
+ };
+
+ /**
+ @author Joris Guisson
+ */
+ class PacketReader : public net::SocketReader
+ {
+ Peer* peer;
+ bool error;
+ QPtrList<IncomingPacket> packet_queue;
+ QMutex mutex;
+ Uint8 len[4];
+ int len_received;
+ public:
+ PacketReader(Peer* peer);
+ virtual ~PacketReader();
+
+ void update();
+ bool ok() const {return !error;}
+ private:
+ Uint32 newPacket(Uint8* buf,Uint32 size);
+ Uint32 readPacket(Uint8* buf,Uint32 size);
+ virtual void onDataReady(Uint8* buf,Uint32 size);
+
+ };
+
+}
+
+#endif
diff --git a/libktorrent/torrent/packetwriter.cpp b/libktorrent/torrent/packetwriter.cpp
new file mode 100644
index 0000000..888d23d
--- /dev/null
+++ b/libktorrent/torrent/packetwriter.cpp
@@ -0,0 +1,399 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+//#define LOG_PACKET
+
+#include <util/log.h>
+#include <util/file.h>
+#include <util/functions.h>
+#include <net/socketmonitor.h>
+#include <ktversion.h>
+#include "packetwriter.h"
+#include "peer.h"
+#include "request.h"
+#include "chunk.h"
+#include <util/bitset.h>
+#include "packet.h"
+#include "uploadcap.h"
+#include <util/log.h>
+#include "globals.h"
+#include "bencoder.h"
+
+
+
+namespace bt
+{
+
+
+ PacketWriter::PacketWriter(Peer* peer) : peer(peer),mutex(true) // this is a recursive mutex
+ {
+ uploaded = 0;
+ uploaded_non_data = 0;
+ curr_packet = 0;
+ ctrl_packets_sent = 0;
+ }
+
+
+ PacketWriter::~PacketWriter()
+ {
+ std::list<Packet*>::iterator i = data_packets.begin();
+ while (i != data_packets.end())
+ {
+ Packet* p = *i;
+ delete p;
+ i++;
+ }
+
+ i = control_packets.begin();
+ while (i != control_packets.end())
+ {
+ Packet* p = *i;
+ delete p;
+ i++;
+ }
+ }
+
+ void PacketWriter::queuePacket(Packet* p)
+ {
+ QMutexLocker locker(&mutex);
+ if (p->getType() == PIECE)
+ data_packets.push_back(p);
+ else
+ control_packets.push_back(p);
+ // tell upload thread we have data ready should it be sleeping
+ net::SocketMonitor::instance().signalPacketReady();
+ }
+
+
+
+ void PacketWriter::sendChoke()
+ {
+ if (peer->am_choked == true)
+ return;
+
+ queuePacket(new Packet(CHOKE));
+ peer->am_choked = true;
+ peer->stats.has_upload_slot = false;
+ }
+
+ void PacketWriter::sendUnchoke()
+ {
+ if (peer->am_choked == false)
+ return;
+
+ queuePacket(new Packet(UNCHOKE));
+ peer->am_choked = false;
+ peer->stats.has_upload_slot = true;
+ }
+
+ void PacketWriter::sendEvilUnchoke()
+ {
+ queuePacket(new Packet(UNCHOKE));
+ peer->am_choked = true;
+ peer->stats.has_upload_slot = false;
+ }
+
+ void PacketWriter::sendInterested()
+ {
+ if (peer->am_interested == true)
+ return;
+
+ queuePacket(new Packet(INTERESTED));
+ peer->am_interested = true;
+ }
+
+ void PacketWriter::sendNotInterested()
+ {
+ if (peer->am_interested == false)
+ return;
+
+ queuePacket(new Packet(NOT_INTERESTED));
+ peer->am_interested = false;
+ }
+
+ void PacketWriter::sendRequest(const Request & r)
+ {
+ queuePacket(new Packet(r,bt::REQUEST));
+ }
+
+ void PacketWriter::sendCancel(const Request & r)
+ {
+ queuePacket(new Packet(r,bt::CANCEL));
+ }
+
+ void PacketWriter::sendReject(const Request & r)
+ {
+ queuePacket(new Packet(r,bt::REJECT_REQUEST));
+ }
+
+ void PacketWriter::sendHave(Uint32 index)
+ {
+ queuePacket(new Packet(index,bt::HAVE));
+ }
+
+ void PacketWriter::sendPort(Uint16 port)
+ {
+ queuePacket(new Packet(port));
+ }
+
+ void PacketWriter::sendBitSet(const BitSet & bs)
+ {
+ queuePacket(new Packet(bs));
+ }
+
+ void PacketWriter::sendHaveAll()
+ {
+ queuePacket(new Packet(bt::HAVE_ALL));
+ }
+
+ void PacketWriter::sendHaveNone()
+ {
+ queuePacket(new Packet(bt::HAVE_NONE));
+ }
+
+ void PacketWriter::sendSuggestPiece(Uint32 index)
+ {
+ queuePacket(new Packet(index,bt::SUGGEST_PIECE));
+ }
+
+ void PacketWriter::sendAllowedFast(Uint32 index)
+ {
+ queuePacket(new Packet(index,bt::ALLOWED_FAST));
+ }
+
+ bool PacketWriter::sendChunk(Uint32 index,Uint32 begin,Uint32 len,Chunk * ch)
+ {
+// Out() << "sendChunk " << index << " " << begin << " " << len << endl;
+ if (begin >= ch->getSize() || begin + len > ch->getSize())
+ {
+ Out(SYS_CON|LOG_NOTICE) << "Warning : Illegal piece request" << endl;
+ Out(SYS_CON|LOG_NOTICE) << "\tChunk : index " << index << " size = " << ch->getSize() << endl;
+ Out(SYS_CON|LOG_NOTICE) << "\tPiece : begin = " << begin << " len = " << len << endl;
+ return false;
+ }
+ else if (!ch || ch->getData() == 0)
+ {
+ Out(SYS_CON|LOG_NOTICE) << "Warning : attempted to upload an invalid chunk" << endl;
+ return false;
+ }
+ else
+ {
+ /* Out(SYS_CON|LOG_DEBUG) << QString("Uploading %1 %2 %3 %4 %5")
+ .arg(index).arg(begin).arg(len).arg((Q_ULLONG)ch,0,16).arg((Q_ULLONG)ch->getData(),0,16)
+ << endl;;
+ */
+ queuePacket(new Packet(index,begin,len,ch));
+ return true;
+ }
+ }
+
+ void PacketWriter::sendExtProtHandshake(Uint16 port,bool pex_on)
+ {
+ QByteArray arr;
+ BEncoder enc(new BEncoderBufferOutput(arr));
+ enc.beginDict();
+ enc.write("m");
+ // supported messages
+ enc.beginDict();
+ enc.write("ut_pex");enc.write((Uint32)(pex_on ? 1 : 0));
+ enc.end();
+ if (port > 0)
+ {
+ enc.write("p");
+ enc.write((Uint32)port);
+ }
+ enc.write("v"); enc.write(QString("KTorrent %1").arg(kt::VERSION_STRING));
+ enc.end();
+ sendExtProtMsg(0,arr);
+ }
+
+ void PacketWriter::sendExtProtMsg(Uint8 id,const QByteArray & data)
+ {
+ queuePacket(new Packet(id,data));
+ }
+
+ Packet* PacketWriter::selectPacket()
+ {
+ Packet* ret = 0;
+ // this function should ensure that between
+ // each data packet at least 3 control packets are sent
+ // so requests can get through
+
+ if (ctrl_packets_sent < 3)
+ {
+ // try to send another control packet
+ if (control_packets.size() > 0)
+ ret = control_packets.front();
+ else if (data_packets.size() > 0)
+ ret = data_packets.front();
+ }
+ else
+ {
+ if (data_packets.size() > 0)
+ {
+ ctrl_packets_sent = 0;
+ ret = data_packets.front();
+ }
+ else if (control_packets.size() > 0)
+ ret = control_packets.front();
+ }
+
+ return ret;
+ }
+
+ Uint32 PacketWriter::onReadyToWrite(Uint8* data,Uint32 max_to_write)
+ {
+ QMutexLocker locker(&mutex);
+
+ if (!curr_packet)
+ curr_packet = selectPacket();
+
+ Uint32 written = 0;
+ while (curr_packet && written < max_to_write)
+ {
+ Packet* p = curr_packet;
+ bool count_as_data = false;
+ Uint32 ret = p->putInOutputBuffer(data + written,max_to_write - written,count_as_data);
+ written += ret;
+ if (count_as_data)
+ uploaded += ret;
+ else
+ uploaded_non_data += ret;
+
+ if (p->isSent())
+ {
+ // packet sent, so remove it
+ if (p->getType() == PIECE)
+ {
+ // remove data packet
+ data_packets.pop_front();
+ delete p;
+ // reset ctrl_packets_sent so the next packet should be a ctrl packet
+ ctrl_packets_sent = 0;
+ curr_packet = selectPacket();
+ }
+ else
+ {
+ // remove control packet and select another one to send
+ control_packets.pop_front();
+ delete p;
+ ctrl_packets_sent++;
+ curr_packet = selectPacket();
+ }
+ }
+ else
+ {
+ // we can't send it fully, so break out of loop
+ break;
+ }
+ }
+
+ return written;
+ }
+
+ bool PacketWriter::hasBytesToWrite() const
+ {
+ return getNumPacketsToWrite() > 0;
+ }
+
+ Uint32 PacketWriter::getUploadedDataBytes() const
+ {
+ QMutexLocker locker(&mutex);
+ Uint32 ret = uploaded;
+ uploaded = 0;
+ return ret;
+ }
+
+ Uint32 PacketWriter::getUploadedNonDataBytes() const
+ {
+ QMutexLocker locker(&mutex);
+ Uint32 ret = uploaded_non_data;
+ uploaded_non_data = 0;
+ return ret;
+ }
+
+ Uint32 PacketWriter::getNumPacketsToWrite() const
+ {
+ QMutexLocker locker(&mutex);
+ return data_packets.size() + control_packets.size();
+ }
+
+ Uint32 PacketWriter::getNumDataPacketsToWrite() const
+ {
+ QMutexLocker locker(&mutex);
+ return data_packets.size();
+ }
+
+ void PacketWriter::doNotSendPiece(const Request & req,bool reject)
+ {
+ QMutexLocker locker(&mutex);
+ std::list<Packet*>::iterator i = data_packets.begin();
+ while (i != data_packets.end())
+ {
+ Packet* p = *i;
+ if (p->isPiece(req) && !p->sending())
+ {
+ // remove current item
+ if (curr_packet == p)
+ curr_packet = 0;
+
+ i = data_packets.erase(i);
+ if (reject)
+ {
+ // queue a reject packet
+ sendReject(req);
+ }
+ delete p;
+ }
+ else
+ {
+ i++;
+ }
+ }
+ }
+
+ void PacketWriter::clearPieces(bool reject)
+ {
+ QMutexLocker locker(&mutex);
+
+ std::list<Packet*>::iterator i = data_packets.begin();
+ while (i != data_packets.end())
+ {
+ Packet* p = *i;
+ if (p->getType() == bt::PIECE && !p->sending())
+ {
+ // remove current item
+ if (curr_packet == p)
+ curr_packet = 0;
+
+ if (reject)
+ {
+ queuePacket(p->makeRejectOfPiece());
+ }
+
+ i = data_packets.erase(i);
+ delete p;
+ }
+ else
+ {
+ i++;
+ }
+ }
+ }
+}
diff --git a/libktorrent/torrent/packetwriter.h b/libktorrent/torrent/packetwriter.h
new file mode 100644
index 0000000..9b77731
--- /dev/null
+++ b/libktorrent/torrent/packetwriter.h
@@ -0,0 +1,185 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef BTPACKETWRITER_H
+#define BTPACKETWRITER_H
+
+#include <list>
+#include <qmutex.h>
+#include <net/bufferedsocket.h>
+#include "globals.h"
+
+namespace bt
+{
+ class Peer;
+ class Request;
+ class Chunk;
+ class BitSet;
+ class Packet;
+
+ /**
+ @author Joris Guisson
+ */
+ class PacketWriter : public net::SocketWriter
+ {
+ Peer* peer;
+ std::list<Packet*> control_packets;
+ std::list<Packet*> data_packets;
+ Packet* curr_packet;
+ Uint32 ctrl_packets_sent;
+ mutable Uint32 uploaded;
+ mutable Uint32 uploaded_non_data;
+ mutable QMutex mutex;
+ public:
+ PacketWriter(Peer* peer);
+ virtual ~PacketWriter();
+
+ /**
+ * Send a choke packet.
+ */
+ void sendChoke();
+
+ /**
+ * Send an unchoke packet.
+ */
+ void sendUnchoke();
+
+ /**
+ * Sends an unchoke message but doesn't update the am_choked field so KT still thinks
+ * it is choked (and will not upload to it), this is to punish snubbers.
+ */
+ void sendEvilUnchoke();
+
+ /**
+ * Send an interested packet.
+ */
+ void sendInterested();
+
+ /**
+ * Send a not interested packet.
+ */
+ void sendNotInterested();
+
+ /**
+ * Send a request for data.
+ * @param req The Request
+ */
+ void sendRequest(const Request & r);
+
+ /**
+ * Cancel a request.
+ * @param req The Request
+ */
+ void sendCancel(const Request & r);
+
+
+ /**
+ * Send a reject for a request
+ * @param req The Request
+ */
+ void sendReject(const Request & r);
+
+ /**
+ * Send a have packet.
+ * @param index
+ */
+ void sendHave(Uint32 index);
+
+ /**
+ * Send an allowed fast packet
+ * @param index
+ */
+ void sendAllowedFast(Uint32 index);
+
+ /**
+ * Send a chunk of data.
+ * @param index Index of chunk
+ * @param begin Offset into chunk
+ * @param len Length of data
+ * @param ch The Chunk
+ * @return true If we satisfy the request, false otherwise
+ */
+ bool sendChunk(Uint32 index,Uint32 begin,Uint32 len,Chunk * ch);
+
+ /**
+ * Send a BitSet. The BitSet indicates which chunks we have.
+ * @param bs The BitSet
+ */
+ void sendBitSet(const BitSet & bs);
+
+ /**
+ * Send a port message
+ * @param port The port
+ */
+ void sendPort(Uint16 port);
+
+ /// Send a have all message
+ void sendHaveAll();
+
+ /// Send a have none message
+ void sendHaveNone();
+
+ /**
+ * Send a suggest piece packet
+ * @param index Index of the chunk
+ */
+ void sendSuggestPiece(Uint32 index);
+
+ /// Send the extension protocol handshake
+ void sendExtProtHandshake(Uint16 port,bool pex_on = true);
+
+ /// Send an extended protocol message
+ void sendExtProtMsg(Uint8 id,const QByteArray & data);
+
+ /// Get the number of packets which need to be written
+ Uint32 getNumPacketsToWrite() const;
+
+ /// Get the number of data packets to write
+ Uint32 getNumDataPacketsToWrite() const;
+
+ /// Get the number of data bytes uploaded
+ Uint32 getUploadedDataBytes() const;
+
+ /// Get the number of bytes uploaded
+ Uint32 getUploadedNonDataBytes() const;
+
+ /**
+ * Do not send a piece which matches this request.
+ * But only if we are not allready sending the piece.
+ * @param req The request
+ * @param reject Wether we can send a reject instead
+ */
+ void doNotSendPiece(const Request & req,bool reject);
+
+ /**
+ * Clear all pieces we are not in the progress of sending.
+ * @param reject Send a reject packet
+ */
+ void clearPieces(bool reject);
+
+ private:
+ void queuePacket(Packet* p);
+ Packet* selectPacket();
+ virtual Uint32 onReadyToWrite(Uint8* data,Uint32 max_to_write);
+ virtual bool hasBytesToWrite() const;
+ };
+
+}
+
+#endif
diff --git a/libktorrent/torrent/peer.cpp b/libktorrent/torrent/peer.cpp
new file mode 100644
index 0000000..7a5727b
--- /dev/null
+++ b/libktorrent/torrent/peer.cpp
@@ -0,0 +1,593 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <math.h>
+#include <util/log.h>
+#include <util/functions.h>
+#include <net/address.h>
+#include <mse/streamsocket.h>
+
+#include "peer.h"
+#include "chunk.h"
+#include "piece.h"
+#include "request.h"
+#include "packetreader.h"
+#include "packetwriter.h"
+#include "peerdownloader.h"
+#include "peeruploader.h"
+#include "bdecoder.h"
+#include "bnode.h"
+#include "utpex.h"
+#include "server.h"
+
+using namespace net;
+
+namespace bt
+{
+
+
+
+ static Uint32 peer_id_counter = 1;
+
+
+ Peer::Peer(mse::StreamSocket* sock,const PeerID & peer_id,
+ Uint32 num_chunks,Uint32 chunk_size,Uint32 support,bool local)
+ : sock(sock),pieces(num_chunks),peer_id(peer_id)
+ {
+ id = peer_id_counter;
+ peer_id_counter++;
+
+ ut_pex = 0;
+ preader = new PacketReader(this);
+ choked = am_choked = true;
+ interested = am_interested = false;
+ killed = false;
+ downloader = new PeerDownloader(this,chunk_size);
+ uploader = new PeerUploader(this);
+
+
+ pwriter = new PacketWriter(this);
+ time_choked = GetCurrentTime();
+ time_unchoked = 0;
+
+ connect_time = QTime::currentTime();
+ //sock->attachPeer(this);
+ stats.client = peer_id.identifyClient();
+ stats.ip_address = getIPAddresss();
+ stats.choked = true;
+ stats.download_rate = 0;
+ stats.upload_rate = 0;
+ stats.perc_of_file = 0;
+ stats.snubbed = false;
+ stats.dht_support = support & DHT_SUPPORT;
+ stats.fast_extensions = support & FAST_EXT_SUPPORT;
+ stats.extension_protocol = support & EXT_PROT_SUPPORT;
+ stats.bytes_downloaded = stats.bytes_uploaded = 0;
+ stats.aca_score = 0.0;
+ stats.evil = false;
+ stats.has_upload_slot = false;
+ stats.num_up_requests = stats.num_down_requests = 0;
+ stats.encrypted = sock->encrypted();
+ stats.local = local;
+ if (stats.ip_address == "0.0.0.0")
+ {
+ Out(SYS_CON|LOG_DEBUG) << "No more 0.0.0.0" << endl;
+ kill();
+ }
+ else
+ {
+ sock->startMonitoring(preader,pwriter);
+ }
+ pex_allowed = stats.extension_protocol;
+ utorrent_pex_id = 0;
+ }
+
+
+ Peer::~Peer()
+ {
+ delete ut_pex;
+ delete uploader;
+ delete downloader;
+ delete sock;
+ delete pwriter;
+ delete preader;
+ }
+
+ void Peer::closeConnection()
+ {
+ sock->close();
+ }
+
+
+ void Peer::kill()
+ {
+ sock->close();
+ killed = true;
+ }
+
+
+
+
+ void Peer::packetReady(const Uint8* packet,Uint32 len)
+ {
+ if (killed) return;
+
+ if (len == 0)
+ return;
+ const Uint8* tmp_buf = packet;
+ //Out() << "Got packet : " << len << " type = " << type << endl;
+ Uint8 type = tmp_buf[0];
+ switch (type)
+ {
+ case CHOKE:
+ if (len != 1)
+ {
+ Out() << "len err CHOKE" << endl;
+ kill();
+ return;
+ }
+
+ if (!choked)
+ {
+ time_choked = GetCurrentTime();
+ }
+ choked = true;
+ downloader->choked();
+ break;
+ case UNCHOKE:
+ if (len != 1)
+ {
+ Out() << "len err UNCHOKE" << endl;
+ kill();
+ return;
+ }
+
+ if (choked)
+ time_unchoked = GetCurrentTime();
+ choked = false;
+ break;
+ case INTERESTED:
+ if (len != 1)
+ {
+ Out() << "len err INTERESTED" << endl;
+ kill();
+ return;
+ }
+ if (!interested)
+ {
+ interested = true;
+ rerunChoker();
+ }
+ break;
+ case NOT_INTERESTED:
+ if (len != 1)
+ {
+ Out() << "len err NOT_INTERESTED" << endl;
+ kill();
+ return;
+ }
+ if (interested)
+ {
+ interested = false;
+ rerunChoker();
+ }
+ break;
+ case HAVE:
+ if (len != 5)
+ {
+ Out() << "len err HAVE" << endl;
+ kill();
+ }
+ else
+ {
+ Uint32 ch = ReadUint32(tmp_buf,1);
+ if (ch < pieces.getNumBits())
+ {
+ haveChunk(this,ch);
+ pieces.set(ch,true);
+ }
+ else
+ {
+ Out(SYS_CON|LOG_NOTICE) << "Received invalid have value, kicking peer" << endl;
+ kill();
+ }
+ }
+ break;
+ case BITFIELD:
+ if (len != 1 + pieces.getNumBytes())
+ {
+ Out() << "len err BITFIELD" << endl;
+ kill();
+ return;
+ }
+
+ pieces = BitSet(tmp_buf+1,pieces.getNumBits());
+ bitSetRecieved(pieces);
+ break;
+ case REQUEST:
+ if (len != 13)
+ {
+ Out() << "len err REQUEST" << endl;
+ kill();
+ return;
+ }
+
+ {
+ Request r(
+ ReadUint32(tmp_buf,1),
+ ReadUint32(tmp_buf,5),
+ ReadUint32(tmp_buf,9),
+ id);
+
+ if (!am_choked)
+ uploader->addRequest(r);
+ else if (stats.fast_extensions)
+ pwriter->sendReject(r);
+ // Out() << "REQUEST " << r.getIndex() << " " << r.getOffset() << endl;
+ }
+ break;
+ case PIECE:
+ if (len < 9)
+ {
+ Out() << "len err PIECE" << endl;
+ kill();
+ return;
+ }
+
+ snub_timer.update();
+
+ {
+ stats.bytes_downloaded += (len - 9);
+ // turn on evil bit
+ if (stats.evil)
+ stats.evil = false;
+ Piece p(ReadUint32(tmp_buf,1),
+ ReadUint32(tmp_buf,5),
+ len - 9,id,tmp_buf+9);
+ piece(p);
+ }
+ break;
+ case CANCEL:
+ if (len != 13)
+ {
+ Out() << "len err CANCEL" << endl;
+ kill();
+ return;
+ }
+
+ {
+ Request r(ReadUint32(tmp_buf,1),
+ ReadUint32(tmp_buf,5),
+ ReadUint32(tmp_buf,9),
+ id);
+ uploader->removeRequest(r);
+ }
+ break;
+ case REJECT_REQUEST:
+ if (len != 13)
+ {
+ Out() << "len err REJECT_REQUEST" << endl;
+ kill();
+ return;
+ }
+
+ {
+ Request r(ReadUint32(tmp_buf,1),
+ ReadUint32(tmp_buf,5),
+ ReadUint32(tmp_buf,9),
+ id);
+ downloader->onRejected(r);
+ }
+ break;
+ case PORT:
+ if (len != 3)
+ {
+ Out() << "len err PORT" << endl;
+ kill();
+ return;
+ }
+
+ {
+ Uint16 port = ReadUint16(tmp_buf,1);
+ // Out() << "Got PORT packet : " << port << endl;
+ gotPortPacket(getIPAddresss(),port);
+ }
+ break;
+ case HAVE_ALL:
+ if (len != 1)
+ {
+ Out() << "len err HAVE_ALL" << endl;
+ kill();
+ return;
+ }
+ pieces.setAll(true);
+ bitSetRecieved(pieces);
+ break;
+ case HAVE_NONE:
+ if (len != 1)
+ {
+ Out() << "len err HAVE_NONE" << endl;
+ kill();
+ return;
+ }
+ pieces.setAll(false);
+ bitSetRecieved(pieces);
+ break;
+ case SUGGEST_PIECE:
+ // ignore suggestions for the moment
+ break;
+ case ALLOWED_FAST:
+ // we no longer support this, so do nothing
+ break;
+ case EXTENDED:
+ handleExtendedPacket(packet,len);
+ break;
+ }
+ }
+
+ void Peer::handleExtendedPacket(const Uint8* packet,Uint32 size)
+ {
+ if (size <= 2 || packet[1] > 1)
+ return;
+
+ if (packet[1] == 1)
+ {
+ if (ut_pex)
+ ut_pex->handlePexPacket(packet,size);
+ return;
+ }
+
+ QByteArray tmp;
+ tmp.setRawData((const char*)packet,size);
+ BNode* node = 0;
+ try
+ {
+ BDecoder dec(tmp,false,2);
+ node = dec.decode();
+ if (node && node->getType() == BNode::DICT)
+ {
+ BDictNode* dict = (BDictNode*)node;
+
+ // handshake packet, so just check if the peer supports ut_pex
+ dict = dict->getDict("m");
+ BValueNode* val = 0;
+ if (dict && (val = dict->getValue("ut_pex")))
+ {
+ utorrent_pex_id = val->data().toInt();
+ if (ut_pex)
+ {
+ if (utorrent_pex_id > 0)
+ ut_pex->changeID(utorrent_pex_id);
+ else
+ {
+ // id 0 means disabled
+ delete ut_pex;
+ ut_pex = 0;
+ }
+ }
+ else if (!ut_pex && utorrent_pex_id != 0 && pex_allowed)
+ {
+ // Don't create it when the id is 0
+ ut_pex = new UTPex(this,utorrent_pex_id);
+ }
+ }
+ }
+ }
+ catch (...)
+ {
+ // just ignore invalid packets
+ Out(SYS_CON|LOG_DEBUG) << "Invalid extended packet" << endl;
+ }
+ delete node;
+ tmp.resetRawData((const char*)packet,size);
+ }
+
+ Uint32 Peer::sendData(const Uint8* data,Uint32 len)
+ {
+ if (killed) return 0;
+
+ Uint32 ret = sock->sendData(data,len);
+ if (!sock->ok())
+ kill();
+
+ return ret;
+ }
+
+ Uint32 Peer::readData(Uint8* buf,Uint32 len)
+ {
+ if (killed) return 0;
+
+ Uint32 ret = sock->readData(buf,len);
+
+ if (!sock->ok())
+ kill();
+
+ return ret;
+ }
+
+ Uint32 Peer::bytesAvailable() const
+ {
+ return sock->bytesAvailable();
+ }
+
+ void Peer::dataWritten(int )
+ {
+ // Out() << "dataWritten " << bytes << endl;
+
+ }
+
+ Uint32 Peer::getUploadRate() const
+ {
+ if (sock)
+ return (Uint32)ceil(sock->getUploadRate());
+ else
+ return 0;
+ }
+
+ Uint32 Peer::getDownloadRate() const
+ {
+ if (sock)
+ return (Uint32)ceil(sock->getDownloadRate());
+ else
+ return 0;
+ }
+
+ bool Peer::readyToSend() const
+ {
+ return true;
+ }
+
+ void Peer::update(PeerManager* pman)
+ {
+ if (killed)
+ return;
+
+ if (!sock->ok() || !preader->ok())
+ {
+ Out(SYS_CON|LOG_DEBUG) << "Connection closed" << endl;
+ kill();
+ return;
+ }
+
+ preader->update();
+
+ Uint32 data_bytes = pwriter->getUploadedDataBytes();
+
+ if (data_bytes > 0)
+ {
+ stats.bytes_uploaded += data_bytes;
+ uploader->addUploadedBytes(data_bytes);
+ }
+
+ if (ut_pex && ut_pex->needsUpdate())
+ ut_pex->update(pman);
+ }
+
+ bool Peer::isSnubbed() const
+ {
+ // 4 minutes
+ return snub_timer.getElapsedSinceUpdate() >= 2*60*1000 && stats.num_down_requests > 0;
+ }
+
+ bool Peer::isSeeder() const
+ {
+ return pieces.allOn();
+ }
+
+ QString Peer::getIPAddresss() const
+ {
+ if (sock)
+ return sock->getRemoteIPAddress();
+ else
+ return QString::null;
+ }
+
+ Uint16 Peer::getPort() const
+ {
+ if (!sock)
+ return 0;
+ else
+ return sock->getRemotePort();
+ }
+
+ net::Address Peer::getAddress() const
+ {
+ if (!sock)
+ return net::Address();
+ else
+ return sock->getRemoteAddress();
+ }
+
+ Uint32 Peer::getTimeSinceLastPiece() const
+ {
+ return snub_timer.getElapsedSinceUpdate();
+ }
+
+ float Peer::percentAvailable() const
+ {
+ return (float)pieces.numOnBits() / (float)pieces.getNumBits() * 100.0;
+ }
+
+ const kt::PeerInterface::Stats & Peer::getStats() const
+ {
+ stats.choked = this->isChoked();
+ stats.download_rate = this->getDownloadRate();
+ stats.upload_rate = this->getUploadRate();
+ stats.perc_of_file = this->percentAvailable();
+ stats.snubbed = this->isSnubbed();
+ stats.num_up_requests = uploader->getNumRequests();
+ stats.num_down_requests = downloader->getNumRequests();
+ return stats;
+ }
+
+ void Peer::setACAScore(double s)
+ {
+ stats.aca_score = s;
+ }
+
+ void Peer::choke()
+ {
+ if (am_choked)
+ return;
+
+ pwriter->sendChoke();
+ uploader->clearAllRequests();
+ }
+
+ void Peer::emitPortPacket()
+ {
+ gotPortPacket(sock->getRemoteIPAddress(),sock->getRemotePort());
+ }
+
+ void Peer::emitPex(const QByteArray & data)
+ {
+ pex(data);
+ }
+
+ void Peer::setPexEnabled(bool on)
+ {
+ if (!stats.extension_protocol)
+ return;
+
+ // send extension protocol handshake
+ bt::Uint16 port = Globals::instance().getServer().getPortInUse();
+
+ if (ut_pex && !on)
+ {
+ delete ut_pex;
+ ut_pex = 0;
+ }
+ else if (!ut_pex && on && utorrent_pex_id > 0)
+ {
+ // if the other side has enabled it to, create a new UTPex object
+ ut_pex = new UTPex(this,utorrent_pex_id);
+ }
+
+ pwriter->sendExtProtHandshake(port,on);
+
+ pex_allowed = on;
+ }
+
+ void Peer::setGroupIDs(Uint32 up_gid,Uint32 down_gid)
+ {
+ sock->setGroupIDs(up_gid,down_gid);
+ }
+}
+
+#include "peer.moc"
diff --git a/libktorrent/torrent/peer.h b/libktorrent/torrent/peer.h
new file mode 100644
index 0000000..68dfecc
--- /dev/null
+++ b/libktorrent/torrent/peer.h
@@ -0,0 +1,324 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef BTPEER_H
+#define BTPEER_H
+
+#include <qobject.h>
+#include <qdatetime.h>
+#include <util/timer.h>
+#include <interfaces/peerinterface.h>
+#include <util/bitset.h>
+#include "globals.h"
+#include "peerid.h"
+
+namespace net
+{
+ class Address;
+}
+
+
+namespace mse
+{
+ class RC4Encryptor;
+ class StreamSocket;
+}
+
+namespace bt
+{
+ class Chunk;
+ class Peer;
+ class Request;
+ class Piece;
+ class PacketReader;
+ class PacketWriter;
+ class PeerDownloader;
+ class PeerUploader;
+ class PeerManager;
+ class UTPex;
+
+
+
+
+ /**
+ * @author Joris Guisson
+ * @brief Manages the connection with a peer
+ *
+ * This class manages a connection with a peer in the P2P network.
+ * It provides functions for sending packets. Packets it receives
+ * get relayed to the outside world using a bunch of signals.
+ */
+ class Peer : public QObject, public kt::PeerInterface
+ //,public Object
+ {
+ Q_OBJECT
+ public:
+ /**
+ * Constructor, set the socket.
+ * The socket is already opened.
+ * @param sock The socket
+ * @param peer_id The Peer's BitTorrent ID
+ * @param num_chunks The number of chunks in the file
+ * @param chunk_size Size of each chunk
+ * @param support Which extensions the peer supports
+ * @param local Wether or not it is a local peer
+ */
+ Peer(mse::StreamSocket* sock,
+ const PeerID & peer_id,
+ Uint32 num_chunks,
+ Uint32 chunk_size,
+ Uint32 support,
+ bool local);
+
+ virtual ~Peer();
+
+ /// Get the peer's unique ID.
+ Uint32 getID() const {return id;}
+
+ /// Get the IP address of the Peer.
+ QString getIPAddresss() const;
+
+ /// Get the port of the Peer
+ Uint16 getPort() const;
+
+ /// Get the address of the peer
+ net::Address getAddress() const;
+
+ /// See if the peer has been killed.
+ bool isKilled() const {return killed;}
+
+ /// Get the PacketWriter
+ PacketWriter & getPacketWriter() {return *pwriter;}
+
+ /// Is the Peer choked
+ bool isChoked() const {return choked;}
+
+ /// Is the Peer interested
+ bool isInterested() const {return interested;}
+
+ /// Are we interested in the Peer
+ bool areWeInterested() const {return am_interested;}
+
+ /// Are we choked for the Peer
+ bool areWeChoked() const {return am_choked;}
+
+ /// Are we being snubbed by the Peer
+ bool isSnubbed() const;
+
+ /// Get the upload rate in bytes per sec
+ Uint32 getUploadRate() const;
+
+ /// Get the download rate in bytes per sec
+ Uint32 getDownloadRate() const;
+
+ /// Get the Peer's BitSet
+ const BitSet & getBitSet() const {return pieces;}
+
+ /// Get the Peer's ID
+ const PeerID & getPeerID() const {return peer_id;}
+
+ /// Update the up- and down- speed and handle incoming packets
+ void update(PeerManager* pman);
+
+ /// Get the PeerDownloader.
+ PeerDownloader* getPeerDownloader() {return downloader;}
+
+ /// Get the PeerUploader.
+ PeerUploader* getPeerUploader() {return uploader;}
+
+ /**
+ * Send a chunk of data.
+ * @param data The data
+ * @param len The length
+ * @param proto Indicates wether the packed is data or a protocol message
+ * @return Number of bytes written
+ */
+ Uint32 sendData(const Uint8* data,Uint32 len);
+
+ /**
+ * Reads data from the peer.
+ * @param buf The buffer to store the data
+ * @param len The maximum number of bytes to read
+ * @return The number of bytes read
+ */
+ Uint32 readData(Uint8* buf,Uint32 len);
+
+ /// Get the number of bytes available to read.
+ Uint32 bytesAvailable() const;
+
+ /**
+ * See if all previously written data, has been sent.
+ */
+ bool readyToSend() const;
+
+
+ /**
+ * Close the peers connection.
+ */
+ void closeConnection();
+
+ /**
+ * Kill the Peer.
+ */
+ void kill();
+
+ /**
+ * Get the time when this Peer was choked.
+ */
+ TimeStamp getChokeTime() const {return time_choked;}
+
+ /**
+ * Get the time when this Peer was unchoked.
+ */
+ TimeStamp getUnchokeTime() const {return time_unchoked;}
+
+ /**
+ * See if the peer is a seeder.
+ */
+ bool isSeeder() const;
+
+ /// Get the time in milliseconds since the last time a piece was received.
+ Uint32 getTimeSinceLastPiece() const;
+
+ /// Get the time the peer connection was established.
+ const QTime & getConnectTime() const {return connect_time;}
+
+ /**
+ * Get the percentual amount of data available from peer.
+ */
+ float percentAvailable() const;
+
+ /// See if the peer supports DHT
+ bool isDHTSupported() const {return stats.dht_support;}
+
+ /// Set the ACA score
+ void setACAScore(double s);
+
+ /// Get the stats of the peer
+ virtual const Stats & getStats() const;
+
+ /// Choke the peer
+ void choke();
+
+ /**
+ * Emit the port packet signal.
+ */
+ void emitPortPacket();
+
+ /**
+ * Emit the pex signal
+ */
+ void emitPex(const QByteArray & data);
+
+ /// Disable or enable pex
+ void setPexEnabled(bool on);
+
+ /**
+ * Set the peer's group IDs for traffic
+ * @param up_gid The upload gid
+ * @param down_gid The download gid
+ */
+ void setGroupIDs(Uint32 up_gid,Uint32 down_gid);
+
+ private slots:
+ void dataWritten(int bytes);
+
+ signals:
+ /**
+ * The Peer has a Chunk.
+ * @param p The Peer
+ * @param index Index of Chunk
+ */
+ void haveChunk(Peer* p,Uint32 index);
+
+ /**
+ * The Peer sent a request.
+ * @param req The Request
+ */
+ void request(const Request & req);
+
+ /**
+ * The Peer sent a cancel.
+ * @param req The Request
+ */
+ void canceled(const Request & req);
+
+ /**
+ * The Peer sent a piece of a Chunk.
+ * @param p The Piece
+ */
+ void piece(const Piece & p);
+
+ /**
+ * Recieved a BitSet
+ * @param bs The BitSet
+ */
+ void bitSetRecieved(const BitSet & bs);
+
+ /**
+ * Emitted when the peer is unchoked and interested changes value.
+ */
+ void rerunChoker();
+
+ /**
+ * Got a port packet from this peer.
+ * @param ip The IP
+ * @param port The port
+ */
+ void gotPortPacket(const QString & ip,Uint16 port);
+
+ /**
+ * A Peer Exchange has been received, the QByteArray contains the data.
+ */
+ void pex(const QByteArray & data);
+
+ private:
+ void packetReady(const Uint8* packet,Uint32 size);
+ void handleExtendedPacket(const Uint8* packet,Uint32 size);
+
+ private:
+ mse::StreamSocket* sock;
+ bool choked;
+ bool interested;
+ bool am_choked;
+ bool am_interested;
+ bool killed;
+ TimeStamp time_choked;
+ TimeStamp time_unchoked;
+ Uint32 id;
+ BitSet pieces;
+ PeerID peer_id;
+ Timer snub_timer;
+ PacketReader* preader;
+ PacketWriter* pwriter;
+ PeerDownloader* downloader;
+ PeerUploader* uploader;
+ mutable kt::PeerInterface::Stats stats;
+ QTime connect_time;
+ UTPex* ut_pex;
+ bool pex_allowed;
+ Uint32 utorrent_pex_id;
+
+ friend class PacketWriter;
+ friend class PacketReader;
+ friend class PeerDownloader;
+ };
+}
+
+#endif
diff --git a/libktorrent/torrent/peerdownloader.cpp b/libktorrent/torrent/peerdownloader.cpp
new file mode 100644
index 0000000..0c6cdd8
--- /dev/null
+++ b/libktorrent/torrent/peerdownloader.cpp
@@ -0,0 +1,311 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <math.h>
+#include <util/functions.h>
+#include <util/log.h>
+#include "globals.h"
+#include "peerdownloader.h"
+#include "peer.h"
+#include "piece.h"
+#include "packetwriter.h"
+
+
+namespace bt
+{
+ TimeStampedRequest::TimeStampedRequest()
+ {
+ time_stamp = bt::GetCurrentTime();
+ }
+
+ TimeStampedRequest::TimeStampedRequest(const Request & r) : req(r)
+ {
+ time_stamp = bt::GetCurrentTime();
+ }
+
+ TimeStampedRequest::TimeStampedRequest(const TimeStampedRequest & t)
+ : req(t.req),time_stamp(t.time_stamp)
+ {
+ }
+
+ bool TimeStampedRequest::operator == (const Request & r)
+ {
+ return r == req;
+ }
+
+ bool TimeStampedRequest::operator == (const TimeStampedRequest & r)
+ {
+ return r.req == req;
+ }
+
+ TimeStampedRequest & TimeStampedRequest::operator = (const Request & r)
+ {
+ time_stamp = bt::GetCurrentTime();
+ req = r;
+ return *this;
+ }
+
+ TimeStampedRequest & TimeStampedRequest::operator = (const TimeStampedRequest & r)
+ {
+ time_stamp = r.time_stamp;
+ req = r.req;
+ return *this;
+ }
+
+ PeerDownloader::PeerDownloader(Peer* peer,Uint32 chunk_size) : peer(peer),grabbed(0),chunk_size(chunk_size / MAX_PIECE_LEN)
+ {
+ connect(peer,SIGNAL(piece(const Piece& )),this,SLOT(piece(const Piece& )));
+ connect(peer,SIGNAL(destroyed()),this,SLOT(peerDestroyed()));
+ nearly_done = false;
+ max_wait_queue_size = 25;
+ }
+
+
+ PeerDownloader::~PeerDownloader()
+ {
+ }
+#if 0
+ void PeerDownloader::retransmitRequests()
+ {
+ for (QValueList<Request>::iterator i = reqs.begin();i != reqs.end();i++)
+ peer->getPacketWriter().sendRequest(*i);
+
+ }
+#endif
+
+ bool PeerDownloader::canAddRequest() const
+ {
+ return wait_queue.count() < max_wait_queue_size;
+ }
+
+ Uint32 PeerDownloader::getNumRequests() const
+ {
+ return reqs.count() /*+ wait_queue.count() */;
+ }
+
+ int PeerDownloader::grab()
+ {
+ grabbed++;
+ return grabbed;
+ }
+
+ void PeerDownloader::release()
+ {
+ grabbed--;
+ if (grabbed < 0)
+ grabbed = 0;
+ }
+
+ void PeerDownloader::download(const Request & req)
+ {
+ if (!peer)
+ return;
+
+ wait_queue.append(req);
+ update();
+ }
+
+ void PeerDownloader::cancel(const Request & req)
+ {
+ if (!peer)
+ return;
+
+ if (wait_queue.contains(req))
+ {
+ wait_queue.remove(req);
+ }
+ else if (reqs.contains(req))
+ {
+ reqs.remove(req);
+ peer->getPacketWriter().sendCancel(req);
+ }
+ }
+
+ void PeerDownloader::onRejected(const Request & req)
+ {
+ if (!peer)
+ return;
+
+// Out(SYS_CON|LOG_DEBUG) << "Rejected : " << req.getIndex() << " "
+// << req.getOffset() << " " << req.getLength() << endl;
+ if (reqs.contains(req))
+ {
+ reqs.remove(req);
+ rejected(req);
+ }
+ }
+
+ void PeerDownloader::cancelAll()
+ {
+ if (peer)
+ {
+ QValueList<TimeStampedRequest>::iterator i = reqs.begin();
+ while (i != reqs.end())
+ {
+ TimeStampedRequest & tr = *i;
+ peer->getPacketWriter().sendCancel(tr.req);
+ i++;
+ }
+ }
+
+ wait_queue.clear();
+ reqs.clear();
+ }
+
+ void PeerDownloader::piece(const Piece & p)
+ {
+ Request r(p);
+ if (wait_queue.contains(r))
+ wait_queue.remove(r);
+ else if (reqs.contains(r))
+ reqs.remove(r);
+
+ downloaded(p);
+ update();
+ }
+
+ void PeerDownloader::peerDestroyed()
+ {
+ peer = 0;
+ }
+
+ bool PeerDownloader::isChoked() const
+ {
+ if (peer)
+ return peer->isChoked();
+ else
+ return true;
+ }
+
+ bool PeerDownloader::hasChunk(Uint32 idx) const
+ {
+ if (peer)
+ return peer->getBitSet().get(idx);
+ else
+ return false;
+ }
+
+ Uint32 PeerDownloader::getDownloadRate() const
+ {
+ if (peer)
+ return peer->getDownloadRate();
+ else
+ return 0;
+ }
+
+ void PeerDownloader::checkTimeouts()
+ {
+ TimeStamp now = bt::GetCurrentTime();
+ // we use a 60 second interval
+ const Uint32 MAX_INTERVAL = 60 * 1000;
+ QValueList<TimeStampedRequest>::iterator i = reqs.begin();
+ while (i != reqs.end())
+ {
+ TimeStampedRequest & tr = *i;
+ if (now - tr.time_stamp > MAX_INTERVAL)
+ {
+ // cancel it
+ TimeStampedRequest r = tr;
+ peer->getPacketWriter().sendCancel(r.req);
+
+ // retransmit it
+ peer->getPacketWriter().sendRequest(r.req);
+ r.time_stamp = now;
+
+ // reappend it at the end of the list
+ i = reqs.erase(i);
+ reqs.append(r);
+ Out(SYS_CON|LOG_DEBUG) << "Retransmitting " << r.req.getIndex() << ":" << r.req.getOffset() << endl;
+ }
+ else
+ {
+ // new requests get appended so once we have found one
+ // which hasn't timed out all the following will also not have timed out
+ break;
+ }
+ }
+ }
+
+
+ Uint32 PeerDownloader::getMaxChunkDownloads() const
+ {
+ // get the download rate in KB/sec
+ Uint32 rate_kbs = peer->getDownloadRate();
+ rate_kbs = rate_kbs / 1024;
+ Uint32 num_extra = rate_kbs / 50;
+
+ if (chunk_size >= 16)
+ {
+ return 1 + 16 * num_extra / chunk_size;
+ }
+ else
+ {
+ return 1 + (16 / chunk_size) * num_extra;
+ }
+ }
+
+ void PeerDownloader::choked()
+ {
+ // choke doesn't mean reject when fast extensions are enabled
+ if (peer->getStats().fast_extensions)
+ return;
+
+ QValueList<TimeStampedRequest>::iterator i = reqs.begin();
+ while (i != reqs.end())
+ {
+ TimeStampedRequest & tr = *i;
+ rejected(tr.req);
+ i++;
+ }
+ reqs.clear();
+
+ QValueList<Request>::iterator j = wait_queue.begin();
+ while (j != wait_queue.end())
+ {
+ Request & req = *j;
+ rejected(req);
+ j++;
+ }
+ wait_queue.clear();
+ }
+
+ void PeerDownloader::update()
+ {
+ // modify the interval if necessary
+ double pieces_per_sec = (double)peer->getDownloadRate() / MAX_PIECE_LEN;
+
+ Uint32 max_reqs = 1 + (Uint32)ceil(10*pieces_per_sec);
+
+ while (wait_queue.count() > 0 && reqs.count() < max_reqs)
+ {
+ // get a request from the wait queue and send that
+ Request req = wait_queue.front();
+ wait_queue.pop_front();
+ TimeStampedRequest r = TimeStampedRequest(req);
+ reqs.append(r);
+ peer->getPacketWriter().sendRequest(req);
+ }
+
+ max_wait_queue_size = 2*max_reqs;
+ if (max_wait_queue_size < 10)
+ max_wait_queue_size = 10;
+ }
+}
+
+#include "peerdownloader.moc"
diff --git a/libktorrent/torrent/peerdownloader.h b/libktorrent/torrent/peerdownloader.h
new file mode 100644
index 0000000..4eb37d2
--- /dev/null
+++ b/libktorrent/torrent/peerdownloader.h
@@ -0,0 +1,231 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef BTPEERDOWNLOADER_H
+#define BTPEERDOWNLOADER_H
+
+#include <set>
+#include <qvaluelist.h>
+#include <qobject.h>
+#include "globals.h"
+#include "request.h"
+
+namespace bt
+{
+ class Peer;
+ class Request;
+ class Piece;
+
+ typedef std::set<Uint32> AllowedFastSet;
+ /**
+ * Request with a timestamp.
+ */
+ struct TimeStampedRequest
+ {
+ Request req;
+ TimeStamp time_stamp;
+
+ TimeStampedRequest();
+
+ /**
+ * Constructor, set the request and calculate the timestamp.
+ * @param r The Request
+ */
+ TimeStampedRequest(const Request & r);
+
+ /**
+ * Copy constructor, copy the request and the timestamp
+ * @param r The Request
+ */
+ TimeStampedRequest(const TimeStampedRequest & t);
+
+ /**
+ * Equality operator, compares requests only.
+ * @param r The Request
+ * @return true if equal
+ */
+ bool operator == (const Request & r);
+
+ /**
+ * Equality operator, compares requests only.
+ * @param r The Request
+ * @return true if equal
+ */
+ bool operator == (const TimeStampedRequest & r);
+
+ /**
+ * Assignment operator.
+ * @param r The Request to copy
+ * @return *this
+ */
+ TimeStampedRequest & operator = (const Request & r);
+
+ /**
+ * Assignment operator.
+ * @param r The TimeStampedRequest to copy
+ * @return *this
+ */
+ TimeStampedRequest & operator = (const TimeStampedRequest & r);
+ };
+
+
+ /**
+ * @author Joris Guisson
+ * @brief Class which downloads pieces from a Peer
+ *
+ * This class downloads Piece's from a Peer.
+ */
+ class PeerDownloader : public QObject
+ {
+ Q_OBJECT
+ public:
+ /**
+ * Constructor, set the Peer
+ * @param peer The Peer
+ * @param chunk_size Size of a chunk in bytes
+ */
+ PeerDownloader(Peer* peer,Uint32 chunk_size);
+ virtual ~PeerDownloader();
+
+ /// See if we can add a request to the wait_queue
+ bool canAddRequest() const;
+
+ /// Get the number of active requests
+ Uint32 getNumRequests() const;
+
+ /// Is the Peer choked.
+ bool isChoked() const;
+
+ /// Is NULL (is the Peer set)
+ bool isNull() const {return peer == 0;}
+
+ /**
+ * See if the Peer has a Chunk
+ * @param idx The Chunk's index
+ */
+ bool hasChunk(Uint32 idx) const;
+
+ /// See if this PeerDownloader has nearly finished a chunk
+ bool isNearlyDone() const {return grabbed == 1 && nearly_done;}
+
+ /// Set the nearly done status of the PeerDownloader
+ void setNearlyDone(bool nd) {nearly_done = nd;}
+
+ /**
+ * Grab the Peer, indicates how many ChunkDownload's
+ * are using this PeerDownloader.
+ * @return The number of times this PeerDownloader was grabbed
+ */
+ int grab();
+
+ /**
+ * When a ChunkDownload is ready with this PeerDownloader,
+ * it will release it, so that others can use it.
+ */
+ void release();
+
+ /// Get the number of times this PeerDownloader was grabbed.
+ int getNumGrabbed() const {return grabbed;}
+
+ /// Get the Peer
+ const Peer* getPeer() const {return peer;}
+
+ /// Get the current download rate
+ Uint32 getDownloadRate() const;
+
+ /**
+ * Check for timed out requests.
+ */
+ void checkTimeouts();
+
+ /// Get the maximum number of chunk downloads
+ Uint32 getMaxChunkDownloads() const;
+
+ /**
+ * The peer has been choked, all pending requests are rejected.
+ * (except for allowed fast ones)
+ */
+ void choked();
+
+ public slots:
+ /**
+ * Send a Request. Note that the DownloadCap
+ * may not allow this. (In which case it will
+ * be stored temporarely in the unsent_reqs list)
+ * @param req The Request
+ */
+ void download(const Request & req);
+
+ /**
+ * Cancel a Request.
+ * @param req The Request
+ */
+ void cancel(const Request & req);
+
+ /**
+ * Cancel all Requests
+ */
+ void cancelAll();
+
+ /**
+ * Handles a rejected request.
+ * @param req
+ */
+ void onRejected(const Request & req);
+
+ private slots:
+ void piece(const Piece & p);
+ void peerDestroyed();
+ void update();
+
+ signals:
+ /**
+ * Emited when a Piece has been downloaded.
+ * @param p The Piece
+ */
+ void downloaded(const Piece & p);
+
+ /**
+ * Emitted when a request takes longer then 60 seconds to download.
+ * The sender of the request will have to request it again. This does not apply for
+ * unsent requests. Their timestamps will be updated when they get transmitted.
+ * @param r The request
+ */
+ void timedout(const Request & r);
+
+ /**
+ * A request was rejected.
+ * @param req The Request
+ */
+ void rejected(const Request & req);
+
+
+ private:
+ Peer* peer;
+ QValueList<TimeStampedRequest> reqs;
+ QValueList<Request> wait_queue;
+ Uint32 max_wait_queue_size;
+ int grabbed;
+ Uint32 chunk_size;
+ bool nearly_done;
+ };
+
+}
+
+#endif
diff --git a/libktorrent/torrent/peerid.cpp b/libktorrent/torrent/peerid.cpp
new file mode 100644
index 0000000..5f314b3
--- /dev/null
+++ b/libktorrent/torrent/peerid.cpp
@@ -0,0 +1,253 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <time.h>
+#include <stdlib.h>
+#include <qmap.h>
+#include <klocale.h>
+#include "peerid.h"
+#include "ktversion.h"
+
+namespace bt
+{
+ char RandomLetterOrNumber()
+ {
+ int i = rand() % 62;
+ if (i < 26)
+ return 'a' + i;
+ else if (i < 52)
+ return 'A' + (i - 26);
+ else
+ return '0' + (i - 52);
+ }
+
+
+ PeerID::PeerID()
+ {
+ srand(time(0));
+ memcpy(id,kt::PEER_ID,8);
+ for (int i = 8;i < 20;i++)
+ id[i] = RandomLetterOrNumber();
+ client_name = identifyClient();
+ }
+
+ PeerID::PeerID(const char* pid)
+ {
+ if (pid)
+ memcpy(id,pid,20);
+ else
+ memset(id,0,20);
+ client_name = identifyClient();
+ }
+
+ PeerID::PeerID(const PeerID & pid)
+ {
+ memcpy(id,pid.id,20);
+ client_name = pid.client_name;
+ }
+
+ PeerID::~PeerID()
+ {}
+
+
+
+ PeerID & PeerID::operator = (const PeerID & pid)
+ {
+ memcpy(id,pid.id,20);
+ client_name = pid.client_name;
+ return *this;
+ }
+
+ bool operator == (const PeerID & a,const PeerID & b)
+ {
+ for (int i = 0;i < 20;i++)
+ if (a.id[i] != b.id[i])
+ return false;
+
+ return true;
+ }
+
+ bool operator != (const PeerID & a,const PeerID & b)
+ {
+ return ! operator == (a,b);
+ }
+
+ bool operator < (const PeerID & a,const PeerID & b)
+ {
+ for (int i = 0;i < 20;i++)
+ if (a.id[i] < b.id[i])
+ return true;
+
+ return false;
+ }
+
+ QString PeerID::toString() const
+ {
+ QString r;
+ for (int i = 0;i < 20;i++)
+ r += id[i] == 0 ? ' ' : id[i];
+ return r;
+ }
+
+ QString PeerID::identifyClient() const
+ {
+ if (!client_name.isNull())
+ return client_name;
+
+ QString peer_id = toString();
+ // we only need to create this map once
+ // so make it static
+ static QMap<QString, QString> Map;
+ static bool first = true;
+
+ if (first)
+ {
+ // Keep things a bit alphabetic to make it easier add new ones
+ //AZUREUS STYLE
+ Map["AG"] = "Ares";
+ Map["A~"] = "Ares";
+ Map["AV"] = "Avicora";
+ Map["AX"] = "BitPump";
+ Map["AR"] = "Arctic";
+ Map["AZ"] = "Azureus";
+ Map["BB"] = "BitBuddy";
+ Map["BC"] = "BitComet";
+ Map["BF"] = "Bitflu";
+ Map["BG"] = "BTGetit";
+ Map["BM"] = "BitMagnet";
+ Map["BO"] = "BitsOnWheels";
+ Map["BR"] = "BitRocket";
+ Map["BS"] = "BTSlave";
+ Map["BX"] = "BitTorrent X";
+ Map["CD"] = "Enhanced CTorrent";
+ Map["CT"] = "CTorrent";
+ Map["DE"] = "DelugeTorrent";
+ Map["DP"] = "Propagate Data Client";
+ Map["EB"] = "EBit";
+ Map["ES"] = "electric sheep";
+ Map["FT"] = "FoxTorrent";
+ Map["GS"] = "GSTorrent";
+ Map["G3"] = "G3 Torrent";
+ Map["HL"] = "Halite";
+ Map["HN"] = "Hydranode";
+ Map["KG"] = "KGet";
+ Map["KT"] = "KTorrent"; // lets not forget our own client
+ Map["LH"] = "LH-ABC";
+ Map["lt"] = "libTorrent";
+ Map["LT"] = "libtorrent";
+ Map["LP"] = "Lphant";
+ Map["LW"] = "LimeWire";
+ Map["ML"] = "MLDonkey";
+ Map["MO"] = "MonoTorrent";
+ Map["MP"] = "MooPolice";
+ Map["MT"] = "MoonLight";
+ Map["PD"] = "Pando";
+ Map["qB"] = "qBittorrent";
+ Map["QD"] = "QQDownload";
+ Map["QT"] = "Qt 4 Torrent example";
+ Map["RS"] = "Rufus";
+ Map["RT"] = "Retriever";
+ Map["S~"] = "Shareaza alpha/beta";
+ Map["SB"] = "Swiftbit";
+ Map["SS"] = "SwarmScope";
+ Map["ST"] = "SymTorrent";
+ Map["st"] = "sharktorrent";
+ Map["SZ"] = "Shareaza";
+ Map["TN"] = "Torrent .NET";
+ Map["TR"] = "Transmission";
+ Map["TS"] = "Torrent Storm";
+ Map["TT"] = "TuoTu";
+ Map["UL"] = "uLeecher!";
+ Map["UT"] = QString("%1Torrent").arg(QChar(0x00B5)); // µTorrent, 0x00B5 is unicode for µ
+ Map["WT"] = "BitLet";
+ Map["WY"] = "FireTorrent";
+ Map["XL"] = "Xunlei";
+ Map["XT"] = "Xan Torrent";
+ Map["XX"] = "Xtorrent";
+ Map["ZT"] = "Zip Torrent";
+
+ //SHADOWS STYLE
+ Map["A"] = "ABC";
+ Map["O"] = "Osprey Permaseed";
+ Map["Q"] = "BTQueue";
+ Map["R"] = "Tribler";
+ Map["S"] = "Shadow's";
+ Map["T"] = "BitTornado";
+ Map["U"] = "UPnP NAT BitTorrent";
+ //OTHER
+ Map["Plus"] = "Plus! II";
+ Map["OP"] = "Opera";
+ Map["BOW"] = "Bits on Wheels";
+ Map["M"] = "BitTorrent";
+ Map["exbc"] = "BitComet";
+ Map["Mbrst"] = "Burst!";
+ first = false;
+ }
+
+ QString name = i18n("Unknown client");
+ if (peer_id.at(0) == '-' &&
+ peer_id.at(1).isLetter() &&
+ peer_id.at(2).isLetter() ) //AZ style
+ {
+ QString ID(peer_id.mid(1,2));
+ if (Map.contains(ID))
+ name = Map[ID] + " " + peer_id.at(3) + "." + peer_id.at(4) + "."
+ + peer_id.at(5) + "." + peer_id.at(6);
+ }
+ else if (peer_id.at(0).isLetter() &&
+ peer_id.at(1).isDigit() &&
+ peer_id.at(2).isDigit() ) //Shadow's style
+ {
+ QString ID = QString(peer_id.at(0));
+ if (Map.contains(ID))
+ name = Map[ID] + " " + peer_id.at(1) + "." +
+ peer_id.at(2) + "." + peer_id.at(3);
+ }
+ else if (peer_id.at(0) == 'M' && peer_id.at(2) == '-' && (peer_id.at(4) == '-' || peer_id.at(5) == '-'))
+ {
+ name = Map["M"] + " " + peer_id.at(1) + "." + peer_id.at(3);
+ if(peer_id.at(4) == '-')
+ name += "." + peer_id.at(5);
+ else
+ name += peer_id.at(4) + "." + peer_id.at(6);
+ }
+ else if (peer_id.startsWith("OP"))
+ {
+ name = Map["OP"];
+ }
+ else if ( peer_id.startsWith("exbc") )
+ {
+ name = Map["exbc"];
+ }
+ else if ( peer_id.mid(1,3) == "BOW")
+ {
+ name = Map["BOW"];
+ }
+ else if ( peer_id.startsWith("Plus"))
+ {
+ name = Map["Plus"];
+ }
+ else if ( peer_id.startsWith("Mbrst"))
+ {
+ name = Map["Mbrst"] + " " + peer_id.at(5) + "." + peer_id.at(7);
+ }
+
+ return name;
+ }
+}
diff --git a/libktorrent/torrent/peerid.h b/libktorrent/torrent/peerid.h
new file mode 100644
index 0000000..90ba439
--- /dev/null
+++ b/libktorrent/torrent/peerid.h
@@ -0,0 +1,61 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef BTPEERID_H
+#define BTPEERID_H
+
+#include <qstring.h>
+
+namespace bt
+{
+
+ /**
+ @author Joris Guisson
+ */
+ class PeerID
+ {
+ char id[20];
+ QString client_name;
+ public:
+ PeerID();
+ PeerID(const char* pid);
+ PeerID(const PeerID & pid);
+ virtual ~PeerID();
+
+ PeerID & operator = (const PeerID & pid);
+
+ const char* data() const {return id;}
+
+ QString toString() const;
+
+ /**
+ * Interprets the PeerID to figure out which client it is.
+ * @author Ivan + Joris
+ * @return The name of the client
+ */
+ QString identifyClient() const;
+
+ friend bool operator == (const PeerID & a,const PeerID & b);
+ friend bool operator != (const PeerID & a,const PeerID & b);
+ friend bool operator < (const PeerID & a,const PeerID & b);
+ };
+
+}
+
+#endif
diff --git a/libktorrent/torrent/peermanager.cpp b/libktorrent/torrent/peermanager.cpp
new file mode 100644
index 0000000..d3744fc
--- /dev/null
+++ b/libktorrent/torrent/peermanager.cpp
@@ -0,0 +1,607 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <util/log.h>
+#include <util/file.h>
+#include <util/error.h>
+#include <net/address.h>
+#include "peermanager.h"
+#include "peer.h"
+#include "bnode.h"
+#include "globals.h"
+#include "server.h"
+#include "authenticate.h"
+#include "torrent.h"
+#include "uploader.h"
+#include "downloader.h"
+#include <util/functions.h>
+#include <qhostaddress.h>
+#include <mse/streamsocket.h>
+#include <mse/encryptedauthenticate.h>
+#include <klocale.h>
+#include "ipblocklist.h"
+#include "chunkcounter.h"
+#include "authenticationmonitor.h"
+#include <qdatetime.h>
+
+using namespace kt;
+
+namespace bt
+{
+ Uint32 PeerManager::max_connections = 0;
+ Uint32 PeerManager::max_total_connections = 0;
+ Uint32 PeerManager::total_connections = 0;
+
+ PeerManager::PeerManager(Torrent & tor)
+ : tor(tor),available_chunks(tor.getNumChunks())
+ {
+ killed.setAutoDelete(true);
+ started = false;
+
+ cnt = new ChunkCounter(tor.getNumChunks());
+ num_pending = 0;
+ pex_on = !tor.isPrivate();
+ }
+
+
+ PeerManager::~PeerManager()
+ {
+ delete cnt;
+ Globals::instance().getServer().removePeerManager(this);
+
+ if (peer_list.count() <= total_connections)
+ total_connections -= peer_list.count();
+ else
+ total_connections = 0;
+
+ peer_list.setAutoDelete(true);
+ peer_list.clear();
+ }
+
+ void PeerManager::update()
+ {
+ if (!started)
+ return;
+
+ // update the speed of each peer,
+ // and get ridd of some killed peers
+ QPtrList<Peer>::iterator i = peer_list.begin();
+ while (i != peer_list.end())
+ {
+ Peer* p = *i;
+ if (p->isKilled())
+ {
+ cnt->decBitSet(p->getBitSet());
+ updateAvailableChunks();
+ i = peer_list.erase(i);
+ killed.append(p);
+ peer_map.erase(p->getID());
+ if (total_connections > 0)
+ total_connections--;
+ peerKilled(p);
+ }
+ else
+ {
+ p->update(this);
+ i++;
+ }
+ }
+
+ // connect to some new peers
+ connectToPeers();
+ }
+
+ void PeerManager::killChokedPeers(Uint32 older_then)
+ {
+ Out() << "Getting rid of peers which have been choked for a long time" << endl;
+ TimeStamp now = bt::GetCurrentTime();
+ QPtrList<Peer>::iterator i = peer_list.begin();
+ Uint32 num_killed = 0;
+ while (i != peer_list.end() && num_killed < 20)
+ {
+ Peer* p = *i;
+ if (p->isChoked() && (now - p->getChokeTime()) > older_then)
+ {
+ p->kill();
+ num_killed++;
+ }
+
+ i++;
+ }
+ }
+
+ void PeerManager::setMaxConnections(Uint32 max)
+ {
+ max_connections = max;
+ }
+
+ void PeerManager::setMaxTotalConnections(Uint32 max)
+ {
+ Uint32 sys_max = bt::MaxOpenFiles() - 50; // leave about 50 free for regular files
+ max_total_connections = max;
+ if (max == 0 || max_total_connections > sys_max)
+ max_total_connections = sys_max;
+ }
+
+ void PeerManager::addPotentialPeer(const PotentialPeer & pp)
+ {
+ if (potential_peers.size() > 150)
+ return;
+
+ // avoid duplicates in the potential_peers map
+ std::pair<PPItr,PPItr> r = potential_peers.equal_range(pp.ip);
+ for (PPItr i = r.first;i != r.second;i++)
+ {
+ if (i->second.port == pp.port) // port and IP are the same so return
+ return;
+ }
+
+ potential_peers.insert(std::make_pair(pp.ip,pp));
+ }
+
+ void PeerManager::killSeeders()
+ {
+ QPtrList<Peer>::iterator i = peer_list.begin();
+ while (i != peer_list.end())
+ {
+ Peer* p = *i;
+ if ( p->isSeeder() )
+ p->kill();
+ i++;
+ }
+ }
+
+ void PeerManager::killUninterested()
+ {
+ QPtrList<Peer>::iterator i = peer_list.begin();
+ while (i != peer_list.end())
+ {
+ Peer* p = *i;
+ if ( !p->isInterested() && (p->getConnectTime().secsTo(QTime::currentTime()) > 30) )
+ p->kill();
+ i++;
+ }
+ }
+
+ void PeerManager::onHave(Peer*,Uint32 index)
+ {
+ available_chunks.set(index,true);
+ cnt->inc(index);
+ }
+
+ void PeerManager::onBitSetRecieved(const BitSet & bs)
+ {
+ for (Uint32 i = 0;i < bs.getNumBits();i++)
+ {
+ if (bs.get(i))
+ {
+ available_chunks.set(i,true);
+ cnt->inc(i);
+ }
+ }
+ }
+
+
+ void PeerManager::newConnection(mse::StreamSocket* sock,const PeerID & peer_id,Uint32 support)
+ {
+ Uint32 total = peer_list.count() + num_pending;
+ bool local_not_ok = (max_connections > 0 && total >= max_connections);
+ bool global_not_ok = (max_total_connections > 0 && total_connections >= max_total_connections);
+
+ if (!started || local_not_ok || global_not_ok)
+ {
+ // get rid of bad peer and replace it by another one
+ if (!killBadPeer())
+ {
+ // we failed to find a bad peer, so just delete this one
+ delete sock;
+ return;
+ }
+ }
+
+ createPeer(sock,peer_id,support,false);
+ }
+
+ void PeerManager::peerAuthenticated(Authenticate* auth,bool ok)
+ {
+ if (!started)
+ return;
+
+ if (total_connections > 0)
+ total_connections--;
+
+ num_pending--;
+ if (!ok)
+ {
+ mse::EncryptedAuthenticate* a = dynamic_cast<mse::EncryptedAuthenticate*>(auth);
+ if (a && Globals::instance().getServer().unencryptedConnectionsAllowed())
+ {
+ // if possible try unencrypted
+ QString ip = a->getIP();
+ Uint16 port = a->getPort();
+ Authenticate* st = new Authenticate(ip,port,tor.getInfoHash(),tor.getPeerID(),this);
+ if (auth->isLocal())
+ st->setLocal(true);
+
+ connect(this,SIGNAL(stopped()),st,SLOT(onPeerManagerDestroyed()));
+ AuthenticationMonitor::instance().add(st);
+ num_pending++;
+ total_connections++;
+ }
+ return;
+ }
+
+ if (connectedTo(auth->getPeerID()))
+ {
+ return;
+ }
+
+ createPeer(auth->takeSocket(),auth->getPeerID(),auth->supportedExtensions(),auth->isLocal());
+ }
+
+ void PeerManager::createPeer(mse::StreamSocket* sock,const PeerID & peer_id,Uint32 support,bool local)
+ {
+ Peer* peer = new Peer(sock,peer_id,tor.getNumChunks(),tor.getChunkSize(),support,local);
+
+ connect(peer,SIGNAL(haveChunk(Peer*, Uint32 )),this,SLOT(onHave(Peer*, Uint32 )));
+ connect(peer,SIGNAL(bitSetRecieved(const BitSet& )),
+ this,SLOT(onBitSetRecieved(const BitSet& )));
+ connect(peer,SIGNAL(rerunChoker()),this,SLOT(onRerunChoker()));
+ connect(peer,SIGNAL(pex( const QByteArray& )),this,SLOT(pex( const QByteArray& )));
+
+ peer_list.append(peer);
+ peer_map.insert(peer->getID(),peer);
+ total_connections++;
+ newPeer(peer);
+ peer->setPexEnabled(pex_on);
+ }
+
+ bool PeerManager::connectedTo(const PeerID & peer_id)
+ {
+ if (!started)
+ return false;
+
+ for (Uint32 j = 0;j < peer_list.count();j++)
+ {
+ Peer* p = peer_list.at(j);
+ if (p->getPeerID() == peer_id)
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ bool PeerManager::connectedTo(const QString & ip,Uint16 port) const
+ {
+ PtrMap<Uint32,Peer>::const_iterator i = peer_map.begin();
+ while (i != peer_map.end())
+ {
+ const Peer* p = i->second;
+ if (p->getPort() == port && p->getStats().ip_address == ip)
+ return true;
+ i++;
+ }
+ return false;
+ }
+
+ void PeerManager::connectToPeers()
+ {
+ if (potential_peers.size() == 0)
+ return;
+
+ if (peer_list.count() + num_pending >= max_connections && max_connections > 0)
+ return;
+
+ if (total_connections >= max_total_connections && max_total_connections > 0)
+ return;
+
+ if (num_pending > MAX_SIMULTANIOUS_AUTHS)
+ return;
+
+ if (!mse::StreamSocket::canInitiateNewConnection())
+ return; // to many sockets in SYN_SENT state
+
+ Uint32 num = 0;
+ if (max_connections > 0)
+ {
+ Uint32 available = max_connections - (peer_list.count() + num_pending);
+ num = available >= potential_peers.size() ?
+ potential_peers.size() : available;
+ }
+ else
+ {
+ num = potential_peers.size();
+ }
+
+ if (num + total_connections >= max_total_connections && max_total_connections > 0)
+ num = max_total_connections - total_connections;
+
+ for (Uint32 i = 0;i < num;i++)
+ {
+ if (num_pending > MAX_SIMULTANIOUS_AUTHS)
+ return;
+
+ PPItr itr = potential_peers.begin();
+
+ IPBlocklist& ipfilter = IPBlocklist::instance();
+
+ if (!ipfilter.isBlocked(itr->first) && !connectedTo(itr->first,itr->second.port))
+ {
+ // Out() << "EncryptedAuthenticate : " << pp.ip << ":" << pp.port << endl;
+ Authenticate* auth = 0;
+ const PotentialPeer & pp = itr->second;
+
+ if (Globals::instance().getServer().isEncryptionEnabled())
+ auth = new mse::EncryptedAuthenticate(pp.ip,pp.port,tor.getInfoHash(),tor.getPeerID(),this);
+ else
+ auth = new Authenticate(pp.ip,pp.port,tor.getInfoHash(),tor.getPeerID(),this);
+
+ if (pp.local)
+ auth->setLocal(true);
+
+ connect(this,SIGNAL(stopped()),auth,SLOT(onPeerManagerDestroyed()));
+
+ AuthenticationMonitor::instance().add(auth);
+ num_pending++;
+ total_connections++;
+ }
+ potential_peers.erase(itr);
+ }
+ }
+
+
+
+ Uint32 PeerManager::clearDeadPeers()
+ {
+ Uint32 num = killed.count();
+ killed.clear();
+ return num;
+ }
+
+ void PeerManager::closeAllConnections()
+ {
+ killed.clear();
+
+ if (peer_list.count() <= total_connections)
+ total_connections -= peer_list.count();
+ else
+ total_connections = 0;
+
+ peer_map.clear();
+ peer_list.setAutoDelete(true);
+ peer_list.clear();
+ peer_list.setAutoDelete(false);
+ }
+
+ // pick a random magic number
+ const Uint32 PEER_LIST_HDR_MAGIC = 0xEF12AB34;
+
+ struct PeerListHeader
+ {
+ Uint32 magic;
+ Uint32 num_peers;
+ Uint32 ip_version; // 4 or 6, 6 is for future purposes only (when we support IPv6)
+ };
+
+ struct PeerListEntry
+ {
+ Uint32 ip;
+ Uint16 port;
+ };
+
+ void PeerManager::savePeerList(const QString & file)
+ {
+ bt::File fptr;
+ if (!fptr.open(file,"wb"))
+ return;
+
+ try
+ {
+ PeerListHeader hdr;
+ hdr.magic = PEER_LIST_HDR_MAGIC;
+ // we will save both the active and potential peers
+ hdr.num_peers = peer_list.count() + potential_peers.size();
+ hdr.ip_version = 4;
+
+ fptr.write(&hdr,sizeof(PeerListHeader));
+
+ Out(SYS_GEN|LOG_DEBUG) << "Saving list of peers to " << file << endl;
+ // first the active peers
+ for (QPtrList<Peer>::iterator itr = peer_list.begin(); itr != peer_list.end();itr++)
+ {
+ Peer* p = *itr;
+ PeerListEntry e;
+ net::Address addr = p->getAddress();
+ e.ip = addr.ip();
+ e.port = addr.port();
+ fptr.write(&e,sizeof(PeerListEntry));
+ }
+
+ // now the potential_peers
+ PPItr i = potential_peers.begin();
+ while (i != potential_peers.end())
+ {
+ net::Address addr(i->first,i->second.port);
+ PeerListEntry e;
+ e.ip = addr.ip();
+ e.port = addr.port();
+ fptr.write(&e,sizeof(PeerListEntry));
+ i++;
+ }
+ }
+ catch (bt::Error & err)
+ {
+ Out(SYS_GEN|LOG_DEBUG) << "Error happened during saving of peer list : " << err.toString() << endl;
+ }
+ }
+
+ void PeerManager::loadPeerList(const QString & file)
+ {
+ bt::File fptr;
+ if (!fptr.open(file,"rb"))
+ return;
+
+ try
+ {
+ PeerListHeader hdr;
+ fptr.read(&hdr,sizeof(PeerListHeader));
+ if (hdr.magic != PEER_LIST_HDR_MAGIC || hdr.ip_version != 4)
+ throw Error("Peer list file corrupted");
+
+ Out(SYS_GEN|LOG_DEBUG) << "Loading list of peers from " << file << " (num_peers = " << hdr.num_peers << ")" << endl;
+
+ for (Uint32 i = 0;i < hdr.num_peers && !fptr.eof();i++)
+ {
+ PeerListEntry e;
+ fptr.read(&e,sizeof(PeerListEntry));
+ PotentialPeer pp;
+
+ // convert IP address to string
+ pp.ip = QString("%1.%2.%3.%4")
+ .arg((e.ip & 0xFF000000) >> 24)
+ .arg((e.ip & 0x00FF0000) >> 16)
+ .arg((e.ip & 0x0000FF00) >> 8)
+ .arg( e.ip & 0x000000FF);
+ pp.port = e.port;
+ addPotentialPeer(pp);
+ }
+
+ }
+ catch (bt::Error & err)
+ {
+ Out(SYS_GEN|LOG_DEBUG) << "Error happened during saving of peer list : " << err.toString() << endl;
+ }
+ }
+
+ void PeerManager::start()
+ {
+ started = true;
+ Globals::instance().getServer().addPeerManager(this);
+ }
+
+
+ void PeerManager::stop()
+ {
+ cnt->reset();
+ available_chunks.clear();
+ started = false;
+ Globals::instance().getServer().removePeerManager(this);
+ stopped();
+ num_pending = 0;
+ }
+
+ Peer* PeerManager::findPeer(Uint32 peer_id)
+ {
+ return peer_map.find(peer_id);
+ }
+
+ void PeerManager::onRerunChoker()
+ {
+ // append a 0 ptr to killed
+ // so that the next update in TorrentControl
+ // will be forced to do the choking
+ killed.append(0);
+ }
+
+ void PeerManager::updateAvailableChunks()
+ {
+ for (Uint32 i = 0;i < available_chunks.getNumBits();i++)
+ {
+ available_chunks.set(i,cnt->get(i) > 0);
+ }
+ }
+
+ void PeerManager::peerSourceReady(kt::PeerSource* ps)
+ {
+ PotentialPeer pp;
+ while (ps->takePotentialPeer(pp))
+ addPotentialPeer(pp);
+ }
+
+ bool PeerManager::killBadPeer()
+ {
+ for (PtrMap<Uint32,Peer>::iterator i = peer_map.begin();i != peer_map.end();i++)
+ {
+ Peer* p = i->second;
+ if (p->getStats().aca_score <= -5.0 && p->getStats().aca_score > -50.0)
+ {
+ Out(SYS_GEN|LOG_DEBUG) << "Killing bad peer, to make room for other peers" << endl;
+ p->kill();
+ return true;
+ }
+ }
+ return false;
+ }
+
+ void PeerManager::pex(const QByteArray & arr)
+ {
+ if (!pex_on)
+ return;
+
+ Out(SYS_CON|LOG_NOTICE) << "PEX: found " << (arr.size() / 6) << " peers" << endl;
+ for (Uint32 i = 0;i+6 <= arr.size();i+=6)
+ {
+ Uint8 tmp[6];
+ memcpy(tmp,arr.data() + i,6);
+ PotentialPeer pp;
+ pp.port = ReadUint16(tmp,4);
+ Uint32 ip = ReadUint32(tmp,0);
+ pp.ip = QString("%1.%2.%3.%4")
+ .arg((ip & 0xFF000000) >> 24)
+ .arg((ip & 0x00FF0000) >> 16)
+ .arg((ip & 0x0000FF00) >> 8)
+ .arg( ip & 0x000000FF);
+ pp.local = false;
+
+ addPotentialPeer(pp);
+ }
+ }
+
+
+ void PeerManager::setPexEnabled(bool on)
+ {
+ if (on && tor.isPrivate())
+ return;
+
+ if (pex_on == on)
+ return;
+
+ QPtrList<Peer>::iterator i = peer_list.begin();
+ while (i != peer_list.end())
+ {
+ Peer* p = *i;
+ if (!p->isKilled())
+ p->setPexEnabled(on);
+ i++;
+ }
+ pex_on = on;
+ }
+
+ void PeerManager::setGroupIDs(Uint32 up,Uint32 down)
+ {
+ for (PtrMap<Uint32,Peer>::iterator i = peer_map.begin();i != peer_map.end();i++)
+ {
+ Peer* p = i->second;
+ p->setGroupIDs(up,down);
+ }
+ }
+}
+
+#include "peermanager.moc"
diff --git a/libktorrent/torrent/peermanager.h b/libktorrent/torrent/peermanager.h
new file mode 100644
index 0000000..d5fdb9f
--- /dev/null
+++ b/libktorrent/torrent/peermanager.h
@@ -0,0 +1,251 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef BTPEERMANAGER_H
+#define BTPEERMANAGER_H
+
+#include <map>
+#include <qobject.h>
+#include <qptrlist.h>
+#include <util/ptrmap.h>
+#include "globals.h"
+#include "peerid.h"
+#include <util/bitset.h>
+#include <interfaces/peersource.h>
+
+namespace mse
+{
+ class StreamSocket;
+}
+
+namespace bt
+{
+ class Peer;
+ class ChunkManager;
+ class Torrent;
+ class Authenticate;
+ class ChunkCounter;
+
+
+
+ const Uint32 MAX_SIMULTANIOUS_AUTHS = 20;
+
+ /**
+ * @author Joris Guisson
+ * @brief Manages all the Peers
+ *
+ * This class manages all Peer objects.
+ * It can also open connections to other peers.
+ */
+ class PeerManager : public QObject
+ {
+ Q_OBJECT
+ public:
+ /**
+ * Constructor.
+ * @param tor The Torrent
+ */
+ PeerManager(Torrent & tor);
+ virtual ~PeerManager();
+
+
+ /**
+ * Check for new connections, update down and upload speed of each Peer.
+ * Initiate new connections.
+ */
+ void update();
+
+ /**
+ * Remove dead peers.
+ * @return The number of dead ones removed
+ */
+ Uint32 clearDeadPeers();
+
+ /**
+ * Get the i'th Peer.
+ * @param index
+ * @return Peer or 0 if out of range
+ */
+ Peer* getPeer(Uint32 index) {return peer_list.at(index);}
+
+ /**
+ * Find a Peer based on it's ID
+ * @param peer_id The ID
+ * @return A Peer or 0, if nothing could be found
+ */
+ Peer* findPeer(Uint32 peer_id);
+
+ /**
+ * Try to connect to some peers
+ */
+ void connectToPeers();
+
+ /**
+ * Close all Peer connections.
+ */
+ void closeAllConnections();
+
+ /**
+ * Start listening to incoming requests.
+ */
+ void start();
+
+ /**
+ * Stop listening to incoming requests.
+ */
+ void stop();
+
+ /**
+ * Kill all peers who have been choked longer then @a older_then time.
+ * @param older_then Time in milliseconds
+ */
+ void killChokedPeers(Uint32 older_then);
+
+ Uint32 getNumConnectedPeers() const {return peer_list.count();}
+ Uint32 getNumPending() const {return num_pending;}
+
+ static void setMaxConnections(Uint32 max);
+ static Uint32 getMaxConnections() {return max_connections;}
+
+ static void setMaxTotalConnections(Uint32 max);
+ static Uint32 getMaxTotalConnections() {return max_total_connections;}
+
+ static Uint32 getTotalConnections() {return total_connections;}
+
+ /// Is the peer manager started
+ bool isStarted() const {return started;}
+
+ /// Get the Torrent
+ Torrent & getTorrent() {return tor;}
+
+ /**
+ * A new connection is ready for this PeerManager.
+ * @param sock The socket
+ * @param peer_id The Peer's ID
+ * @param support What extensions the peer supports
+ */
+ void newConnection(mse::StreamSocket* sock,const PeerID & peer_id,Uint32 support);
+
+ /**
+ * Add a potential peer
+ * @param pp The PotentialPeer
+ */
+ void addPotentialPeer(const kt::PotentialPeer & pp);
+
+ /**
+ * Kills all connections to seeders.
+ * This is used when torrent download gets finished
+ * and we should drop all connections to seeders
+ */
+ void killSeeders();
+
+ /**
+ * Kills all peers that are not interested for a long time.
+ * This should be used when torrent is seeding ONLY.
+ */
+ void killUninterested();
+
+ /// Get a BitSet of all available chunks
+ const BitSet & getAvailableChunksBitSet() const {return available_chunks;}
+
+ /// Get the chunk counter.
+ ChunkCounter & getChunkCounter() {return *cnt;};
+
+ /// Are we connected to a Peer given it's PeerID ?
+ bool connectedTo(const PeerID & peer_id);
+
+ /**
+ * A peer has authenticated.
+ * @param auth The Authenticate object
+ * @param ok Wether or not the attempt was succesfull
+ */
+ void peerAuthenticated(Authenticate* auth,bool ok);
+
+ /**
+ * Save the IP's and port numbers of all peers.
+ */
+ void savePeerList(const QString & file);
+
+ /**
+ * Load the peer list again and add them to the potential peers
+ */
+ void loadPeerList(const QString & file);
+
+ typedef QPtrList<Peer>::const_iterator CItr;
+
+ CItr beginPeerList() const {return peer_list.begin();}
+ CItr endPeerList() const {return peer_list.end();}
+
+ /// Is PEX eanbled
+ bool isPexEnabled() const {return pex_on;}
+
+ /// Enable or disable PEX
+ void setPexEnabled(bool on);
+
+ /// Set the group IDs of each peer
+ void setGroupIDs(Uint32 up,Uint32 down);
+
+ public slots:
+ /**
+ * A PeerSource, has new potential peers.
+ * @param ps The PeerSource
+ */
+ void peerSourceReady(kt::PeerSource* ps);
+
+ private:
+ void updateAvailableChunks();
+ bool killBadPeer();
+ void createPeer(mse::StreamSocket* sock,const PeerID & peer_id,Uint32 support,bool local);
+ bool connectedTo(const QString & ip,Uint16 port) const;
+
+ private slots:
+ void onHave(Peer* p,Uint32 index);
+ void onBitSetRecieved(const BitSet & bs);
+ void onRerunChoker();
+ void pex(const QByteArray & arr);
+
+
+ signals:
+ void newPeer(Peer* p);
+ void peerKilled(Peer* p);
+ void stopped();
+
+ private:
+ PtrMap<Uint32,Peer> peer_map;
+ QPtrList<Peer> peer_list;
+ QPtrList<Peer> killed;
+ Torrent & tor;
+ bool started;
+ BitSet available_chunks;
+ ChunkCounter* cnt;
+ Uint32 num_pending;
+ bool pex_on;
+
+ static Uint32 max_connections;
+ static Uint32 max_total_connections;
+ static Uint32 total_connections;
+
+ std::multimap<QString,kt::PotentialPeer> potential_peers;
+
+ typedef std::multimap<QString,kt::PotentialPeer>::iterator PPItr;
+ };
+
+}
+
+#endif
diff --git a/libktorrent/torrent/peersourcemanager.cpp b/libktorrent/torrent/peersourcemanager.cpp
new file mode 100644
index 0000000..fef55b5
--- /dev/null
+++ b/libktorrent/torrent/peersourcemanager.cpp
@@ -0,0 +1,556 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <qfile.h>
+#include <klocale.h>
+#include <functions.h>
+#include <util/log.h>
+#include <torrent/globals.h>
+#include <kademlia/dhtbase.h>
+#include <kademlia/dhttrackerbackend.h>
+#include "tracker.h"
+#include "udptracker.h"
+#include "httptracker.h"
+#include "torrentcontrol.h"
+#include "torrent.h"
+#include "peermanager.h"
+#include "peersourcemanager.h"
+
+namespace bt
+{
+ const Uint32 INITIAL_WAIT_TIME = 30;
+ const Uint32 LONGER_WAIT_TIME = 300;
+ const Uint32 FINAL_WAIT_TIME = 1800;
+
+ PeerSourceManager::PeerSourceManager(TorrentControl* tor,PeerManager* pman)
+ : tor(tor),pman(pman),curr(0),m_dht(0),started(false),pending(false)
+ {
+ failures = 0;
+ trackers.setAutoDelete(true);
+ no_save_custom_trackers = false;
+
+ const TrackerTier* t = tor->getTorrent().getTrackerList();
+ int tier = 1;
+ while (t)
+ {
+ // add all standard trackers
+ const KURL::List & tr = t->urls;
+ KURL::List::const_iterator i = tr.begin();
+ while (i != tr.end())
+ {
+ addTracker(*i,false,tier);
+ i++;
+ }
+
+ tier++;
+ t = t->next;
+ }
+
+ //load custom trackers
+ loadCustomURLs();
+
+ connect(&timer,SIGNAL(timeout()),this,SLOT(updateCurrentManually()));
+ }
+
+ PeerSourceManager::~PeerSourceManager()
+ {
+ saveCustomURLs();
+ additional.setAutoDelete(true);
+ QPtrList<kt::PeerSource>::iterator itr = additional.begin();
+ while (itr != additional.end())
+ {
+ kt::PeerSource* ps = *itr;
+ ps->aboutToBeDestroyed();
+ itr++;
+ }
+ additional.clear();
+ }
+
+ void PeerSourceManager::addTracker(Tracker* trk)
+ {
+ trackers.insert(trk->trackerURL(),trk);
+ connect(trk,SIGNAL(peersReady( kt::PeerSource* )),
+ pman,SLOT(peerSourceReady( kt::PeerSource* )));
+ }
+
+ void PeerSourceManager::addPeerSource(kt::PeerSource* ps)
+ {
+ additional.append(ps);
+ connect(ps,SIGNAL(peersReady( kt::PeerSource* )),
+ pman,SLOT(peerSourceReady( kt::PeerSource* )));
+ }
+
+ void PeerSourceManager::removePeerSource(kt::PeerSource* ps)
+ {
+ disconnect(ps,SIGNAL(peersReady( kt::PeerSource* )),
+ pman,SLOT(peerSourceReady( kt::PeerSource* )));
+ additional.remove(ps);
+ }
+
+ void PeerSourceManager::start()
+ {
+ if (started)
+ return;
+
+ started = true;
+ QPtrList<kt::PeerSource>::iterator i = additional.begin();
+ while (i != additional.end())
+ {
+ (*i)->start();
+ i++;
+ }
+
+ if (!curr)
+ {
+ if (trackers.count() > 0)
+ {
+ switchTracker(selectTracker());
+ tor->resetTrackerStats();
+ curr->start();
+ }
+ }
+ else
+ {
+ tor->resetTrackerStats();
+ curr->start();
+ }
+ }
+
+ void PeerSourceManager::stop(WaitJob* wjob)
+ {
+ if (!started)
+ return;
+
+ started = false;
+ QPtrList<kt::PeerSource>::iterator i = additional.begin();
+ while (i != additional.end())
+ {
+ (*i)->stop();
+ i++;
+ }
+
+ if (curr)
+ curr->stop(wjob);
+
+ timer.stop();
+ statusChanged(i18n("Stopped"));
+ }
+
+ void PeerSourceManager::completed()
+ {
+ QPtrList<kt::PeerSource>::iterator i = additional.begin();
+ while (i != additional.end())
+ {
+ (*i)->completed();
+ i++;
+ }
+
+ if (curr)
+ curr->completed();
+ }
+
+ void PeerSourceManager::manualUpdate()
+ {
+ QPtrList<kt::PeerSource>::iterator i = additional.begin();
+ while (i != additional.end())
+ {
+ (*i)->manualUpdate();
+ i++;
+ }
+
+ if (curr)
+ {
+ timer.stop();
+ curr->manualUpdate();
+ }
+ }
+
+
+
+ KURL PeerSourceManager::getTrackerURL() const
+ {
+ if (curr)
+ return curr->trackerURL();
+ else
+ return KURL();
+ }
+
+ KURL::List PeerSourceManager::getTrackerURLs()
+ {
+ KURL::List urls;
+ const TrackerTier* t = tor->getTorrent().getTrackerList();
+ while (t)
+ {
+ urls += t->urls;
+ t = t->next;
+ }
+
+ urls += custom_trackers;
+ return urls;
+ }
+
+ void PeerSourceManager::addTracker(KURL url, bool custom,int tier)
+ {
+ if (trackers.contains(url))
+ return;
+
+ Tracker* trk = 0;
+ if (url.protocol() == "udp")
+ trk = new UDPTracker(url,tor,tor->getTorrent().getPeerID(),tier);
+ else
+ trk = new HTTPTracker(url,tor,tor->getTorrent().getPeerID(),tier);
+
+ addTracker(trk);
+ if (custom)
+ {
+ custom_trackers.append(url);
+ if (!no_save_custom_trackers)
+ saveCustomURLs();
+ }
+ }
+
+ bool PeerSourceManager::removeTracker(KURL url)
+ {
+ if (!custom_trackers.contains(url))
+ return false;
+
+ custom_trackers.remove(url);
+ Tracker* trk = trackers.find(url);
+ if (curr == trk)
+ {
+ // do a timed delete on the tracker, so the stop signal
+ // has plenty of time to reach it
+ trk->stop();
+ trk->timedDelete(10 * 1000);
+ trackers.setAutoDelete(false);
+ trackers.erase(url);
+ trackers.setAutoDelete(true);
+
+ if (trackers.count() > 0)
+ {
+ switchTracker(selectTracker());
+ tor->resetTrackerStats();
+ curr->start();
+ }
+ }
+ else
+ {
+ // just delete if not the current one
+ trackers.erase(url);
+ }
+ saveCustomURLs();
+ return true;
+ }
+
+ void PeerSourceManager::setTracker(KURL url)
+ {
+ Tracker* trk = trackers.find(url);
+ if (!trk)
+ return;
+
+ if (curr != trk)
+ {
+ if (curr)
+ curr->stop();
+ switchTracker(trk);
+ tor->resetTrackerStats();
+ trk->start();
+ }
+ }
+
+ void PeerSourceManager::restoreDefault()
+ {
+ KURL::List::iterator i = custom_trackers.begin();
+ while (i != custom_trackers.end())
+ {
+ Tracker* t = trackers.find(*i);
+ if (t)
+ {
+ if (curr == t)
+ {
+ if (t->isStarted())
+ t->stop();
+
+ curr = 0;
+ trackers.erase(*i);
+ if (trackers.count() > 0)
+ {
+ switchTracker(trackers.begin()->second);
+ if (started)
+ {
+ tor->resetTrackerStats();
+ curr->start();
+ }
+ }
+ }
+ else
+ {
+ trackers.erase(*i);
+ }
+ }
+ i++;
+ }
+
+ custom_trackers.clear();
+ saveCustomURLs();
+ }
+
+ void PeerSourceManager::saveCustomURLs()
+ {
+ QString trackers_file = tor->getTorDir() + "trackers";
+ QFile file(trackers_file);
+ if(!file.open(IO_WriteOnly))
+ return;
+
+ QTextStream stream(&file);
+ for (KURL::List::iterator i = custom_trackers.begin();i != custom_trackers.end();i++)
+ stream << (*i).prettyURL() << ::endl;
+ }
+
+ void PeerSourceManager::loadCustomURLs()
+ {
+ QString trackers_file = tor->getTorDir() + "trackers";
+ QFile file(trackers_file);
+ if(!file.open(IO_ReadOnly))
+ return;
+
+ no_save_custom_trackers = true;
+ QTextStream stream(&file);
+ while (!stream.atEnd())
+ {
+ KURL url = stream.readLine();
+ addTracker(url,true);
+ }
+ no_save_custom_trackers = false;
+ }
+
+ Tracker* PeerSourceManager::selectTracker()
+ {
+ Tracker* n = 0;
+ PtrMap<KURL,Tracker>::iterator i = trackers.begin();
+ while (i != trackers.end())
+ {
+ Tracker* t = i->second;
+ if (!n)
+ n = t;
+ else if (t->failureCount() < n->failureCount())
+ n = t;
+ else if (t->failureCount() == n->failureCount())
+ n = t->getTier() < n->getTier() ? t : n;
+ i++;
+ }
+
+ if (n)
+ {
+ Out(SYS_TRK|LOG_DEBUG) << "Selected tracker " << n->trackerURL().prettyURL()
+ << " (tier = " << n->getTier() << ")" << endl;
+ }
+
+ return n;
+ }
+
+ void PeerSourceManager::onTrackerError(const QString & err)
+ {
+ failures++;
+ pending = false;
+ if (started)
+ statusChanged(err);
+
+ if (!started)
+ return;
+
+ // select an other tracker
+ Tracker* trk = selectTracker();
+
+ if (!trk)
+ {
+ if (curr->failureCount() > 5)
+ {
+ // we failed to contact the only tracker 5 times in a row, so try again in
+ // 30 minutes
+ curr->setInterval(FINAL_WAIT_TIME);
+ timer.start(FINAL_WAIT_TIME * 1000,true);
+ request_time = QDateTime::currentDateTime();
+ }
+ else if (curr->failureCount() > 2)
+ {
+ // we failed to contact the only tracker 3 times in a row, so try again in
+ // a minute or 5, no need for hammering every 30 seconds
+ curr->setInterval(LONGER_WAIT_TIME);
+ timer.start(LONGER_WAIT_TIME * 1000,true);
+ request_time = QDateTime::currentDateTime();
+ }
+ else
+ {
+ // lets not hammer and wait 30 seconds
+ curr->setInterval(INITIAL_WAIT_TIME);
+ timer.start(INITIAL_WAIT_TIME * 1000,true);
+ request_time = QDateTime::currentDateTime();
+ }
+ }
+ else
+ {
+ curr->stop();
+ // switch to another one
+ switchTracker(trk);
+ if (trk->failureCount() == 0)
+ {
+ tor->resetTrackerStats();
+ curr->start();
+ }
+ else if (trk->failureCount() > 5)
+ {
+ curr->setInterval(FINAL_WAIT_TIME);
+ timer.start(FINAL_WAIT_TIME * 1000,true);
+ request_time = QDateTime::currentDateTime();
+ }
+ else if (trk->failureCount() > 2)
+ {
+ // we tried everybody 3 times and it didn't work
+ // wait 5 minutes and try again
+ curr->setInterval(LONGER_WAIT_TIME);
+ timer.start(LONGER_WAIT_TIME * 1000,true);
+ request_time = QDateTime::currentDateTime();
+ }
+ else
+ {
+ // wait 30 seconds and try again
+ curr->setInterval(INITIAL_WAIT_TIME);
+ timer.start(INITIAL_WAIT_TIME * 1000,true);
+ request_time = QDateTime::currentDateTime();
+ }
+ }
+ }
+
+ void PeerSourceManager::onTrackerOK()
+ {
+ failures = 0;
+ if (started)
+ {
+ timer.start(curr->getInterval() * 1000,true);
+ curr->scrape();
+ }
+ pending = false;
+ if (started)
+ statusChanged(i18n("OK"));
+ request_time = QDateTime::currentDateTime();
+ }
+
+ void PeerSourceManager::onTrackerRequestPending()
+ {
+ if (started)
+ statusChanged(i18n("Announcing"));
+ pending = true;
+ }
+
+ void PeerSourceManager::updateCurrentManually()
+ {
+ if (!curr)
+ return;
+
+ if (!curr->isStarted())
+ tor->resetTrackerStats();
+
+ curr->manualUpdate();
+ }
+
+ void PeerSourceManager::switchTracker(Tracker* trk)
+ {
+ if (curr == trk)
+ return;
+
+ if (curr)
+ {
+ disconnect(curr,SIGNAL(requestFailed( const QString& )),
+ this,SLOT(onTrackerError( const QString& )));
+ disconnect(curr,SIGNAL(requestOK()),this,SLOT(onTrackerOK()));
+ disconnect(curr,SIGNAL(requestPending()),this,SLOT(onTrackerRequestPending()));
+ curr = 0;
+ }
+
+ curr = trk;
+ if (curr)
+ {
+ Out(SYS_TRK|LOG_NOTICE) << "Switching to tracker " << trk->trackerURL() << endl;
+ QObject::connect(curr,SIGNAL(requestFailed( const QString& )),
+ this,SLOT(onTrackerError( const QString& )));
+
+ QObject::connect(curr,SIGNAL(requestOK()),
+ this,SLOT(onTrackerOK()));
+
+ QObject::connect(curr,SIGNAL(requestPending()),
+ this,SLOT(onTrackerRequestPending()));
+ }
+ }
+
+ Uint32 PeerSourceManager::getTimeToNextUpdate() const
+ {
+ if (pending || !started || !curr)
+ return 0;
+
+ return curr->getInterval() - request_time.secsTo(QDateTime::currentDateTime());
+ }
+
+ Uint32 PeerSourceManager::getNumSeeders() const
+ {
+ return curr ? curr->getNumSeeders() : 0;
+ }
+
+
+ Uint32 PeerSourceManager::getNumLeechers() const
+ {
+ return curr ? curr->getNumLeechers() : 0;
+ }
+
+ void PeerSourceManager::addDHT()
+ {
+ if(m_dht)
+ {
+ removePeerSource(m_dht);
+ delete m_dht;
+ }
+
+ m_dht = new dht::DHTTrackerBackend(Globals::instance().getDHT(),tor);
+
+ // add the DHT source
+ addPeerSource(m_dht);
+ }
+
+ void PeerSourceManager::removeDHT()
+ {
+ if(m_dht == 0)
+ {
+ removePeerSource(m_dht);
+ return;
+ }
+
+ removePeerSource(m_dht);
+ delete m_dht;
+ m_dht = 0;
+ }
+
+ bool PeerSourceManager::dhtStarted()
+ {
+ return m_dht != 0;
+ }
+
+
+}
+
+#include "peersourcemanager.moc"
diff --git a/libktorrent/torrent/peersourcemanager.h b/libktorrent/torrent/peersourcemanager.h
new file mode 100644
index 0000000..cdace4e
--- /dev/null
+++ b/libktorrent/torrent/peersourcemanager.h
@@ -0,0 +1,182 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef BTPEERSOURCEMANAGER_H
+#define BTPEERSOURCEMANAGER_H
+
+#include <qtimer.h>
+#include <qdatetime.h>
+#include <qptrlist.h>
+#include <util/ptrmap.h>
+#include <interfaces/trackerslist.h>
+
+namespace kt
+{
+ class PeerSource;
+}
+
+namespace dht
+{
+ class DHTTrackerBackend;
+}
+
+namespace bt
+{
+ class Tracker;
+ class PeerManager;
+ class Torrent;
+ class TorrentControl;
+
+ /**
+ * @author Joris Guisson <joris.guisson@gmail.com>
+ *
+ * This class manages all PeerSources.
+ */
+ class PeerSourceManager : public QObject, public kt::TrackersList
+ {
+ Q_OBJECT
+
+ TorrentControl* tor;
+ PeerManager* pman;
+ PtrMap<KURL,Tracker> trackers;
+ QPtrList<kt::PeerSource> additional;
+ Tracker* curr;
+ dht::DHTTrackerBackend* m_dht;
+ bool started;
+ bool pending;
+ KURL::List custom_trackers;
+ QDateTime request_time;
+ QTimer timer;
+ Uint32 failures;
+ bool no_save_custom_trackers;
+ public:
+ PeerSourceManager(TorrentControl* tor,PeerManager* pman);
+ virtual ~PeerSourceManager();
+
+
+ /**
+ * Add a PeerSource, the difference between PeerSource and Tracker
+ * is that only one Tracker can be used at the same time,
+ * PeerSource can always be used.
+ * @param ps The PeerSource
+ */
+ void addPeerSource(kt::PeerSource* ps);
+
+ /**
+ * See if the PeerSourceManager has been started
+ */
+ bool isStarted() const {return started;}
+
+ /**
+ * Start gathering peers
+ */
+ void start();
+
+ /**
+ * Stop gathering peers
+ * @param wjob WaitJob to wait at exit for the completion of stopped events to the trackers
+ */
+ void stop(WaitJob* wjob = 0);
+
+ /**
+ * Notify peersources and trackrs that the download is complete.
+ */
+ void completed();
+
+ /**
+ * Do a manual update on all peer sources and trackers.
+ */
+ void manualUpdate();
+
+ /**
+ * Remove a Tracker or PeerSource.
+ * @param ps
+ */
+ void removePeerSource(kt::PeerSource* ps);
+
+ virtual KURL getTrackerURL() const;
+ virtual KURL::List getTrackerURLs();
+ virtual void addTracker(KURL url, bool custom = true,int tier = 1);
+ virtual bool removeTracker(KURL url);
+ virtual void setTracker(KURL url);
+ virtual void restoreDefault();
+
+ /**
+ * Get the time to the next tracker update.
+ * @return The time in seconds
+ */
+ Uint32 getTimeToNextUpdate() const;
+
+ /// Get the number of potential seeders
+ Uint32 getNumSeeders() const;
+
+ /// Get the number of potential leechers
+ Uint32 getNumLeechers() const;
+
+ /// Get the number of failures
+ Uint32 getNumFailures() const {return failures;}
+
+ ///Adds DHT as PeerSource for this torrent
+ void addDHT();
+ ///Removes DHT from PeerSourceManager for this torrent.
+ void removeDHT();
+ ///Checks if DHT is enabled
+ bool dhtStarted();
+
+ private slots:
+ /**
+ * The an error happened contacting the tracker.
+ * @param err The error
+ */
+ void onTrackerError(const QString & err);
+
+ /**
+ * Tracker update was OK.
+ * @param
+ */
+ void onTrackerOK();
+
+ /**
+ * Tracker is doing a request.
+ */
+ void onTrackerRequestPending();
+
+ /**
+ * Update the current tracker manually
+ */
+ void updateCurrentManually();
+
+ signals:
+ /**
+ * Status has changed of the tracker.
+ * @param ns The new status
+ */
+ void statusChanged(const QString & ns);
+
+ private:
+ void saveCustomURLs();
+ void loadCustomURLs();
+ void addTracker(Tracker* trk);
+ void switchTracker(Tracker* trk);
+ Tracker* selectTracker();
+ };
+
+}
+
+#endif
diff --git a/libktorrent/torrent/peeruploader.cpp b/libktorrent/torrent/peeruploader.cpp
new file mode 100644
index 0000000..1e0dbca
--- /dev/null
+++ b/libktorrent/torrent/peeruploader.cpp
@@ -0,0 +1,130 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <set>
+#include <ksocketaddress.h>
+#include <util/log.h>
+#include <util/functions.h>
+#include <util/sha1hash.h>
+#include "peeruploader.h"
+#include "peer.h"
+#include "chunkmanager.h"
+#include "packetwriter.h"
+#include "torrent.h"
+
+using namespace KNetwork;
+
+namespace bt
+{
+
+ PeerUploader::PeerUploader(Peer* peer) : peer(peer)
+ {
+ uploaded = 0;
+ }
+
+
+ PeerUploader::~PeerUploader()
+ {}
+
+ void PeerUploader::addRequest(const Request & r)
+ {
+ // Out(SYS_CON|LOG_DEBUG) <<
+ // QString("PeerUploader::addRequest %1 %2 %3\n").arg(r.getIndex()).arg(r.getOffset()).arg(r.getLength()) << endl;
+
+ // allowed fast chunks go to the front of the queue
+ requests.append(r);
+ }
+
+ void PeerUploader::removeRequest(const Request & r)
+ {
+ // Out(SYS_CON|LOG_DEBUG) <<
+ // QString("PeerUploader::removeRequest %1 %2 %3\n").arg(r.getIndex()).arg(r.getOffset()).arg(r.getLength()) << endl;
+ requests.remove(r);
+ peer->getPacketWriter().doNotSendPiece(r,peer->getStats().fast_extensions);
+ }
+
+ Uint32 PeerUploader::update(ChunkManager & cman,Uint32 opt_unchoked)
+ {
+ Uint32 ret = uploaded;
+ uploaded = 0;
+
+ PacketWriter & pw = peer->getPacketWriter();
+
+ // if we have choked the peer do not upload
+ if (peer->areWeChoked())
+ return ret;
+
+ if (peer->isSnubbed() && !peer->areWeChoked() &&
+ !cman.completed() && peer->getID() != opt_unchoked)
+ return ret;
+
+
+ while (requests.count() > 0)
+ {
+ Request r = requests.front();
+
+ Chunk* c = cman.grabChunk(r.getIndex());
+ if (c && c->getData())
+ {
+ if (!pw.sendChunk(r.getIndex(),r.getOffset(),r.getLength(),c))
+ {
+ if (peer->getStats().fast_extensions)
+ pw.sendReject(r);
+ }
+ requests.pop_front();
+ }
+ else
+ {
+ // remove requests we can't satisfy
+ Out(SYS_CON|LOG_DEBUG) << "Cannot satisfy request" << endl;
+ if (peer->getStats().fast_extensions)
+ pw.sendReject(r);
+ requests.pop_front();
+ }
+ }
+
+ return ret;
+ }
+
+ void PeerUploader::clearAllRequests()
+ {
+ bool fast_ext = peer->getStats().fast_extensions;
+ PacketWriter & pw = peer->getPacketWriter();
+ pw.clearPieces(fast_ext);
+
+ if (fast_ext)
+ {
+ // reject all requests
+ // if the peer supports fast extensions,
+ // choke doesn't mean reject all
+ QValueList<Request>::iterator i = requests.begin();
+ while (i != requests.end())
+ {
+ pw.sendReject(*i);
+ i++;
+ }
+ }
+ requests.clear();
+ }
+
+ Uint32 PeerUploader::getNumRequests() const
+ {
+ return requests.count() + peer->getPacketWriter().getNumDataPacketsToWrite();
+ }
+}
diff --git a/libktorrent/torrent/peeruploader.h b/libktorrent/torrent/peeruploader.h
new file mode 100644
index 0000000..94aea74
--- /dev/null
+++ b/libktorrent/torrent/peeruploader.h
@@ -0,0 +1,93 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef BTPEERUPLOADER_H
+#define BTPEERUPLOADER_H
+
+#include <set>
+#include <qvaluelist.h>
+#include "request.h"
+
+
+
+namespace bt
+{
+ class Peer;
+ class ChunkManager;
+
+ const Uint32 ALLOWED_FAST_SIZE = 8;
+
+ /**
+ * @author Joris Guisson
+ * @brief Uploads pieces to a Peer
+ *
+ * This class handles the uploading of pieces to a Peer. It keeps
+ * track of a list of Request objects. All these Requests where sent
+ * by the Peer. It will upload the pieces to the Peer, making sure
+ * that the maximum upload rate isn't surpassed.
+ */
+ class PeerUploader
+ {
+ Peer* peer;
+ QValueList<Request> requests;
+ Uint32 uploaded;
+ public:
+ /**
+ * Constructor. Set the Peer.
+ * @param peer The Peer
+ */
+ PeerUploader(Peer* peer);
+ virtual ~PeerUploader();
+
+ /**
+ * Add a Request to the list of Requests.
+ * @param r The Request
+ */
+ void addRequest(const Request & r);
+
+ /**
+ * Remove a Request from the list of Requests.
+ * @param r The Request
+ */
+ void removeRequest(const Request & r);
+
+ /**
+ * Update the PeerUploader. This will check if there are Request, and
+ * will try to handle them.
+ * @param cman The ChunkManager
+ * @param opt_unchoked ID of optimisticly unchoked peer
+ * @return The number of bytes uploaded
+ */
+ Uint32 update(ChunkManager & cman,Uint32 opt_unchoked);
+
+ /// Get the number of requests
+ Uint32 getNumRequests() const;
+
+
+ void addUploadedBytes(Uint32 bytes) {uploaded += bytes;}
+
+ /**
+ * Clear all pending requests.
+ */
+ void clearAllRequests();
+ };
+
+}
+
+#endif
diff --git a/libktorrent/torrent/piece.cpp b/libktorrent/torrent/piece.cpp
new file mode 100644
index 0000000..0fff862
--- /dev/null
+++ b/libktorrent/torrent/piece.cpp
@@ -0,0 +1,34 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include "piece.h"
+
+namespace bt
+{
+
+ Piece::Piece(Uint32 index, Uint32 off, Uint32 len, Uint32 peer,const Uint8* data)
+ : Request(index, off, len, peer),data(data)
+ {}
+
+
+ Piece::~Piece()
+ {}
+
+
+}
diff --git a/libktorrent/torrent/piece.h b/libktorrent/torrent/piece.h
new file mode 100644
index 0000000..9e749db
--- /dev/null
+++ b/libktorrent/torrent/piece.h
@@ -0,0 +1,44 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef BTPIECE_H
+#define BTPIECE_H
+
+#include "request.h"
+
+namespace bt
+{
+
+ /**
+ @author Joris Guisson
+ */
+ class Piece : public Request
+ {
+ public:
+ Piece(Uint32 index, Uint32 off, Uint32 len, Uint32 peer,const Uint8* data);
+ virtual ~Piece();
+
+ const Uint8* getData() const {return data;}
+ private:
+ const Uint8* data;
+ };
+
+}
+
+#endif
diff --git a/libktorrent/torrent/preallocationthread.cpp b/libktorrent/torrent/preallocationthread.cpp
new file mode 100644
index 0000000..6fb2100
--- /dev/null
+++ b/libktorrent/torrent/preallocationthread.cpp
@@ -0,0 +1,134 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <util/log.h>
+#include <util/error.h>
+#include <qfile.h>
+#include <klocale.h>
+#include "preallocationthread.h"
+#include "chunkmanager.h"
+#include "globals.h"
+
+#ifndef O_LARGEFILE
+# define O_LARGEFILE 0
+#endif
+
+namespace bt
+{
+
+ PreallocationThread::PreallocationThread(ChunkManager* cman) : cman(cman),stopped(false),not_finished(false),done(false)
+ {
+ bytes_written = 0;
+ }
+
+
+ PreallocationThread::~PreallocationThread()
+ {}
+
+ void PreallocationThread::run()
+ {
+ try
+ {
+ cman->preallocateDiskSpace(this);
+ }
+ catch (Error & err)
+ {
+ setErrorMsg(err.toString());
+ }
+
+ mutex.lock();
+ done = true;
+ mutex.unlock();
+ Out(SYS_GEN|LOG_NOTICE) << "PreallocationThread has finished" << endl;
+ }
+
+ void PreallocationThread::stop()
+ {
+ mutex.lock();
+ stopped = true;
+ mutex.unlock();
+ }
+
+ void PreallocationThread::setErrorMsg(const QString & msg)
+ {
+ mutex.lock();
+ error_msg = msg; stopped = true;
+ mutex.unlock();
+ }
+
+ bool PreallocationThread::isStopped() const
+ {
+ mutex.lock();
+ bool tmp = stopped;
+ mutex.unlock();
+ return tmp;
+ }
+
+ bool PreallocationThread::errorHappened() const
+ {
+ mutex.lock();
+ bool ret = !error_msg.isNull();
+ mutex.unlock();
+ return ret;
+ }
+
+ void PreallocationThread::written(Uint64 nb)
+ {
+ mutex.lock();
+ bytes_written += nb;
+ mutex.unlock();
+ }
+
+ Uint64 PreallocationThread::bytesWritten()
+ {
+ mutex.lock();
+ Uint64 tmp = bytes_written;
+ mutex.unlock();
+ return tmp;
+ }
+
+ bool PreallocationThread::isDone() const
+ {
+ mutex.lock();
+ bool tmp = done;
+ mutex.unlock();
+ return tmp;
+ }
+
+ bool PreallocationThread::isNotFinished() const
+ {
+ mutex.lock();
+ bool tmp = not_finished;
+ mutex.unlock();
+ return tmp;
+ }
+
+ void PreallocationThread::setNotFinished()
+ {
+ mutex.lock();
+ not_finished = true;
+ mutex.unlock();
+ }
+}
diff --git a/libktorrent/torrent/preallocationthread.h b/libktorrent/torrent/preallocationthread.h
new file mode 100644
index 0000000..31bd668
--- /dev/null
+++ b/libktorrent/torrent/preallocationthread.h
@@ -0,0 +1,94 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef BTPREALLOCATIONTHREAD_H
+#define BTPREALLOCATIONTHREAD_H
+
+#include <qstring.h>
+#include <qthread.h>
+#include <qmap.h>
+#include <qmutex.h>
+#include <util/constants.h>
+
+
+
+namespace bt
+{
+ class ChunkManager;
+
+ /**
+ * @author Joris Guisson <joris.guisson@gmail.com>
+ *
+ * Thread to preallocate diskspace
+ */
+ class PreallocationThread : public QThread
+ {
+ ChunkManager* cman;
+ bool stopped,not_finished,done;
+ QString error_msg;
+ Uint64 bytes_written;
+ mutable QMutex mutex;
+ public:
+ PreallocationThread(ChunkManager* cman);
+ virtual ~PreallocationThread();
+
+ virtual void run();
+
+
+ /**
+ * Stop the thread.
+ */
+ void stop();
+
+ /**
+ * Set an error message, also calls stop
+ * @param msg The message
+ */
+ void setErrorMsg(const QString & msg);
+
+ /// See if the thread has been stopped
+ bool isStopped() const;
+
+ /// Did an error occur during the preallocation ?
+ bool errorHappened() const;
+
+ /// Get the error_msg
+ const QString & errorMessage() const {return error_msg;}
+
+ /// nb Number of bytes have been written
+ void written(Uint64 nb);
+
+ /// Get the number of bytes written
+ Uint64 bytesWritten();
+
+ /// Allocation was aborted, so the next time the torrent is started it needs to be started again
+ void setNotFinished();
+
+ /// See if the allocation hasn't completed yet
+ bool isNotFinished() const;
+
+ /// See if the thread was done
+ bool isDone() const;
+ private:
+ bool expand(const QString & path,Uint64 max_size);
+ };
+
+}
+
+#endif
diff --git a/libktorrent/torrent/queuemanager.cpp b/libktorrent/torrent/queuemanager.cpp
new file mode 100644
index 0000000..4135f8f
--- /dev/null
+++ b/libktorrent/torrent/queuemanager.cpp
@@ -0,0 +1,811 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * ivasic@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include "queuemanager.h"
+
+#include <qstring.h>
+#include <kmessagebox.h>
+#include <klocale.h>
+#include <util/log.h>
+#include <util/error.h>
+#include <util/sha1hash.h>
+#include <util/waitjob.h>
+#include <util/fileops.h>
+#include <torrent/globals.h>
+#include <torrent/torrent.h>
+#include <torrent/torrentcontrol.h>
+#include <interfaces/torrentinterface.h>
+#include <interfaces/trackerslist.h>
+#include <settings.h>
+
+
+using namespace kt;
+
+namespace bt
+{
+
+ QueueManager::QueueManager() : QObject(),exiting(false)
+ {
+ downloads.setAutoDelete(true);
+ max_downloads = 0;
+ max_seeds = 0; //for testing. Needs to be added to Settings::
+
+ keep_seeding = true; //test. Will be passed from Core
+ paused_state = false;
+ }
+
+
+ QueueManager::~QueueManager()
+ {}
+
+ void QueueManager::append(kt::TorrentInterface* tc)
+ {
+ downloads.append(tc);
+ downloads.sort();
+
+ connect(tc, SIGNAL(diskSpaceLow(kt::TorrentInterface*, bool)), this, SLOT(onLowDiskSpace(kt::TorrentInterface*, bool)));
+ connect(tc, SIGNAL(torrentStopped(kt::TorrentInterface*)), this, SLOT(torrentStopped(kt::TorrentInterface*)));
+ }
+
+ void QueueManager::remove(kt::TorrentInterface* tc)
+ {
+ paused_torrents.erase(tc);
+
+ int index = downloads.findRef(tc);
+
+ if (index != -1)
+ downloads.remove(index);
+ else
+ Out(SYS_GEN | LOG_IMPORTANT) << "Could not delete removed torrent control." << endl;
+ }
+
+ void QueueManager::clear()
+ {
+ Uint32 nd = downloads.count();
+
+ paused_torrents.clear();
+ downloads.clear();
+
+ // wait for a second to allow all http jobs to send the stopped event
+ if (nd > 0)
+ SynchronousWait(1000);
+ }
+
+ kt::TorrentStartResponse QueueManager::start(kt::TorrentInterface* tc, bool user)
+ {
+ const TorrentStats & s = tc->getStats();
+
+ bool start_tc = user;
+
+ bool check_done = false;
+
+ if (tc->isCheckingData(check_done) && !check_done)
+ return kt::BUSY_WITH_DATA_CHECK;
+
+ if (!user)
+ {
+ if (s.completed)
+ start_tc = (max_seeds == 0 || getNumRunning(false, true) < max_seeds);
+ else
+ start_tc = (max_downloads == 0 || getNumRunning(true) < max_downloads);
+ }
+ else
+ {
+ //User started this torrent so make it user controlled
+ tc->setPriority(0);
+ }
+
+ if (start_tc)
+ {
+
+ if (!s.completed) //no need to check diskspace for seeding torrents
+ {
+ //check diskspace
+ bool shortDiskSpace = !tc->checkDiskSpace(false);
+
+ if (shortDiskSpace)
+ {
+ //we're short!
+
+ switch (Settings::startDownloadsOnLowDiskSpace())
+ {
+
+ case 0: //don't start!
+ tc->setPriority(0);
+ return kt::NOT_ENOUGH_DISKSPACE;
+
+ case 1: //ask user
+ if (KMessageBox::questionYesNo(0, i18n("You don't have enough disk space to download this torrent. Are you sure you want to continue?"), i18n("Insufficient disk space for %1").arg(s.torrent_name)) == KMessageBox::No)
+ {
+ tc->setPriority(0);
+ return kt::USER_CANCELED;
+ }
+ else
+ break;
+
+ case 2: //force start
+ break;
+ }
+ }
+ }
+
+ Out(SYS_GEN | LOG_NOTICE) << "Starting download" << endl;
+
+ float ratio = kt::ShareRatio(s);
+
+ float max_ratio = tc->getMaxShareRatio();
+
+ if (s.completed && max_ratio > 0 && ratio >= max_ratio)
+ {
+ if (KMessageBox::questionYesNo(0, i18n("Torrent \"%1\" has reached its maximum share ratio. Ignore the limit and start seeding anyway?").arg(s.torrent_name), i18n("Maximum share ratio limit reached.")) == KMessageBox::Yes)
+ {
+ tc->setMaxShareRatio(0.00f);
+ startSafely(tc);
+ }
+ else
+ return kt::USER_CANCELED;
+ }
+ else
+ startSafely(tc);
+ }
+ else
+ {
+ return kt::QM_LIMITS_REACHED;
+ }
+
+ return kt::START_OK;
+ }
+
+ void QueueManager::stop(kt::TorrentInterface* tc, bool user)
+ {
+ bool check_done = false;
+
+ if (tc->isCheckingData(check_done) && !check_done)
+ return;
+
+ const TorrentStats & s = tc->getStats();
+
+ if (s.running)
+ {
+ stopSafely(tc, user);
+ }
+
+ if (user) //dequeue it
+ tc->setPriority(0);
+ }
+
+ void QueueManager::startall(int type)
+ {
+ QPtrList<kt::TorrentInterface>::iterator i = downloads.begin();
+
+ while (i != downloads.end())
+ {
+ kt::TorrentInterface* tc = *i;
+
+ if (type >= 3)
+ start(tc, true);
+ else
+ {
+ if ((tc->getStats().completed && type == 2) || (!tc->getStats().completed && type == 1) || (type == 3))
+ start(tc, true);
+ }
+
+ i++;
+ }
+ }
+
+ void QueueManager::stopall(int type)
+ {
+ QPtrList<kt::TorrentInterface>::iterator i = downloads.begin();
+
+ while (i != downloads.end())
+ {
+ kt::TorrentInterface* tc = *i;
+
+ const TorrentStats & s = tc->getStats();
+
+ if (tc->getStats().running)
+ {
+ try
+ {
+ if (type >= 3)
+ stopSafely(tc, true);
+ else if ((s.completed && type == 2) || (!s.completed && type == 1))
+ stopSafely(tc, true);
+ }
+ catch (bt::Error & err)
+ {
+ QString msg =
+ i18n("Error stopping torrent %1 : %2")
+ .arg(s.torrent_name).arg(err.toString());
+ KMessageBox::error(0, msg, i18n("Error"));
+ }
+ }
+ else //if torrent is not running but it is queued we need to make it user controlled
+ if ((s.completed && type == 2) || (!s.completed && type == 1) || (type == 3))
+ tc->setPriority(0);
+
+ i++;
+ }
+ }
+
+ void QueueManager::onExit(WaitJob* wjob)
+ {
+ exiting = true;
+ QPtrList<kt::TorrentInterface>::iterator i = downloads.begin();
+
+ while (i != downloads.end())
+ {
+ kt::TorrentInterface* tc = *i;
+
+ if (tc->getStats().running)
+ {
+ stopSafely(tc, false, wjob);
+ }
+
+ i++;
+ }
+ }
+
+ void QueueManager::startNext()
+ {
+ orderQueue();
+ }
+
+ int QueueManager::countDownloads()
+ {
+ int nr = 0;
+ QPtrList<TorrentInterface>::const_iterator i = downloads.begin();
+
+ while (i != downloads.end())
+ {
+ if (!(*i)->getStats().completed)
+ ++nr;
+
+ ++i;
+ }
+
+ return nr;
+ }
+
+ int QueueManager::countSeeds()
+ {
+ int nr = 0;
+ QPtrList<TorrentInterface>::const_iterator i = downloads.begin();
+
+ while (i != downloads.end())
+ {
+ if ((*i)->getStats().completed)
+ ++nr;
+
+ ++i;
+ }
+
+ return nr;
+ }
+
+ int QueueManager::getNumRunning(bool onlyDownload, bool onlySeed)
+ {
+ int nr = 0;
+ // int test = 1;
+ QPtrList<TorrentInterface>::const_iterator i = downloads.begin();
+
+ while (i != downloads.end())
+ {
+ const TorrentInterface* tc = *i;
+
+ const TorrentStats & s = tc->getStats();
+
+ //Out() << "Torrent " << test++ << s.torrent_name << " priority: " << tc->getPriority() << endl;
+ if (s.running)
+ {
+ if (onlyDownload)
+ {
+ if (!s.completed) nr++;
+ }
+ else
+ {
+ if (onlySeed)
+ {
+ if (s.completed) nr++;
+ }
+ else
+ nr++;
+ }
+ }
+
+ i++;
+ }
+
+ // Out() << endl;
+ return nr;
+ }
+
+ int QueueManager::getNumRunning(bool userControlled, bool onlyDownloads, bool onlySeeds)
+ {
+ int nr = 0;
+ // int test = 1;
+ QPtrList<TorrentInterface>::const_iterator i = downloads.begin();
+
+ while (i != downloads.end())
+ {
+ const TorrentInterface* tc = *i;
+
+ const TorrentStats & s = tc->getStats();
+
+ //Out() << "Torrent " << test++ << s.torrent_name << " priority: " << tc->getPriority() << endl;
+ if (s.running)
+ {
+ if (onlyDownloads)
+ {
+ if (!s.completed && (userControlled && s.user_controlled)) nr++;
+ }
+ else
+ {
+ if (onlySeeds)
+ {
+ if (s.completed && (userControlled && s.user_controlled)) nr++;
+ }
+ else
+ if (userControlled && s.user_controlled) nr++;
+ }
+ }
+
+ i++;
+ }
+
+ // Out() << endl;
+ return nr;
+ }
+
+ QPtrList<kt::TorrentInterface>::iterator QueueManager::begin()
+ {
+ return downloads.begin();
+ }
+
+ QPtrList<kt::TorrentInterface>::iterator QueueManager::end()
+ {
+ return downloads.end();
+ }
+
+ void QueueManager::setMaxDownloads(int m)
+ {
+ max_downloads = m;
+ }
+
+ void QueueManager::setMaxSeeds(int m)
+ {
+ max_seeds = m;
+ }
+
+ void QueueManager::setKeepSeeding(bool ks)
+ {
+ keep_seeding = ks;
+ }
+
+ bool QueueManager::allreadyLoaded(const SHA1Hash & ih) const
+ {
+ QPtrList<kt::TorrentInterface>::const_iterator itr = downloads.begin();
+
+ while (itr != downloads.end())
+ {
+ const TorrentControl* tor = (const TorrentControl*)(*itr);
+
+ if (tor->getTorrent().getInfoHash() == ih)
+ return true;
+
+ itr++;
+ }
+
+ return false;
+ }
+
+ void QueueManager::mergeAnnounceList(const SHA1Hash & ih, const TrackerTier* trk)
+
+ {
+ QPtrList<kt::TorrentInterface>::iterator itr = downloads.begin();
+
+ while (itr != downloads.end())
+ {
+ TorrentControl* tor = (TorrentControl*)(*itr);
+
+ if (tor->getTorrent().getInfoHash() == ih)
+ {
+ TrackersList* ta = tor->getTrackersList();
+ ta->merge(trk);
+ return;
+ }
+
+ itr++;
+ }
+ }
+
+ void QueueManager::orderQueue()
+ {
+ if (!downloads.count())
+ return;
+
+ if (paused_state || exiting)
+ return;
+
+ downloads.sort();
+
+ QPtrList<TorrentInterface>::const_iterator it = downloads.begin();
+ QPtrList<TorrentInterface>::const_iterator its = downloads.end();
+
+
+ if (max_downloads != 0 || max_seeds != 0)
+ {
+ bt::QueuePtrList download_queue;
+ bt::QueuePtrList seed_queue;
+
+ int user_downloading = 0;
+ int user_seeding = 0;
+
+ for (; it != downloads.end(); ++it)
+ {
+ TorrentInterface* tc = *it;
+
+ const TorrentStats & s = tc->getStats();
+
+ if (s.running && s.user_controlled)
+ {
+ if (!s.completed)
+ ++user_downloading;
+ else
+ ++user_seeding;
+ }
+
+ if (!s.user_controlled && !tc->isMovingFiles() && !s.stopped_by_error)
+ {
+ if (s.completed)
+ seed_queue.append(tc);
+ else
+ download_queue.append(tc);
+ }
+ }
+
+ int max_qm_downloads = max_downloads - user_downloading;
+
+ int max_qm_seeds = max_seeds - user_seeding;
+
+ //stop all QM started torrents
+
+ for (Uint32 i = max_qm_downloads; i < download_queue.count() && max_downloads; ++i)
+ {
+ TorrentInterface* tc = download_queue.at(i);
+
+ const TorrentStats & s = tc->getStats();
+
+ if (s.running && !s.user_controlled && !s.completed)
+ {
+ Out(SYS_GEN | LOG_DEBUG) << "QM Stopping: " << s.torrent_name << endl;
+ stop(tc);
+ }
+ }
+
+ //stop all QM started torrents
+ for (Uint32 i = max_qm_seeds; i < seed_queue.count() && max_seeds; ++i)
+ {
+ TorrentInterface* tc = seed_queue.at(i);
+
+ const TorrentStats & s = tc->getStats();
+
+ if (s.running && !s.user_controlled && s.completed)
+ {
+ Out(SYS_GEN | LOG_NOTICE) << "QM Stopping: " << s.torrent_name << endl;
+ stop(tc);
+ }
+ }
+
+ //Now start all needed torrents
+ if (max_downloads == 0)
+ max_qm_downloads = download_queue.count();
+
+ if (max_seeds == 0)
+ max_qm_seeds = seed_queue.count();
+
+ int counter = 0;
+
+ for (Uint32 i = 0; counter < max_qm_downloads && i < download_queue.count(); ++i)
+ {
+ TorrentInterface* tc = download_queue.at(i);
+
+ const TorrentStats & s = tc->getStats();
+
+ if (!s.running && !s.completed && !s.user_controlled)
+ {
+ start(tc, false);
+
+ if (tc->getStats().stopped_by_error)
+ {
+ tc->setPriority(0);
+ continue;
+ }
+ }
+
+ ++counter;
+ }
+
+ counter = 0;
+
+ for (Uint32 i = 0; counter < max_qm_seeds && i < seed_queue.count(); ++i)
+ {
+ TorrentInterface* tc = seed_queue.at(i);
+
+ const TorrentStats & s = tc->getStats();
+
+ if (!s.running && s.completed && !s.user_controlled)
+ {
+ start(tc, false);
+
+ if (tc->getStats().stopped_by_error)
+ {
+ tc->setPriority(0);
+ continue;
+ }
+ }
+
+ ++counter;
+ }
+ }
+ else
+ {
+ //no limits at all
+
+ for (it = downloads.begin(); it != downloads.end(); ++it)
+ {
+ TorrentInterface* tc = *it;
+
+ const TorrentStats & s = tc->getStats();
+
+ if (!s.running && !s.user_controlled && !s.stopped_by_error && !tc->isMovingFiles())
+ {
+ start(tc, false);
+ if (tc->getStats().stopped_by_error)
+ tc->setPriority(0);
+ }
+ }
+ }
+
+ }
+
+ void QueueManager::torrentFinished(kt::TorrentInterface* tc)
+ {
+ //dequeue this tc
+ tc->setPriority(0);
+ //make sure the max_seeds is not reached
+// if(max_seeds !=0 && max_seeds < getNumRunning(false,true))
+// tc->stop(true);
+
+ if (keep_seeding)
+ torrentAdded(tc, false, false);
+ else
+ stop(tc,true);
+
+ orderQueue();
+ }
+
+ void QueueManager::torrentAdded(kt::TorrentInterface* tc, bool user, bool start_torrent)
+ {
+ if (!user)
+ {
+ QPtrList<TorrentInterface>::const_iterator it = downloads.begin();
+
+ while (it != downloads.end())
+ {
+ TorrentInterface* _tc = *it;
+ int p = _tc->getPriority();
+
+ if (p == 0)
+ break;
+ else
+ _tc->setPriority(++p);
+
+ ++it;
+ }
+
+ tc->setPriority(1);
+ }
+ else
+ {
+ tc->setPriority(0);
+ if(start_torrent)
+ start(tc, true);
+ }
+
+ orderQueue();
+ }
+
+ void QueueManager::torrentRemoved(kt::TorrentInterface* tc)
+ {
+ remove(tc);
+
+ orderQueue();
+ }
+
+ void QueueManager::setPausedState(bool pause)
+ {
+ paused_state = pause;
+ if (!pause)
+ {
+ std::set<kt::TorrentInterface*>::iterator it = paused_torrents.begin();
+ while (it != paused_torrents.end())
+ {
+ TorrentInterface* tc = *it;
+ startSafely(tc);
+ it++;
+ }
+
+ paused_torrents.clear();
+ orderQueue();
+ }
+ else
+ {
+ QPtrList<TorrentInterface>::const_iterator it = downloads.begin();
+ for (; it != downloads.end(); it++)
+ {
+ TorrentInterface* tc = *it;
+
+ const TorrentStats & s = tc->getStats();
+
+ if (s.running)
+ {
+ paused_torrents.insert(tc);
+ stopSafely(tc, false);
+ }
+ }
+ }
+ }
+
+ void QueueManager::enqueue(kt::TorrentInterface* tc)
+ {
+ //if a seeding torrent reached its maximum share ratio or maximum seed time don't enqueue it...
+ if (tc->getStats().completed && (tc->overMaxRatio() || tc->overMaxSeedTime()))
+ {
+ Out(SYS_GEN | LOG_IMPORTANT) << "Torrent has reached max share ratio or max seed time and cannot be started automatically." << endl;
+ emit queuingNotPossible(tc);
+ return;
+ }
+
+ torrentAdded(tc, false, false);
+ }
+
+ void QueueManager::dequeue(kt::TorrentInterface* tc)
+ {
+ int tp = tc->getPriority();
+ bool completed = tc->getStats().completed;
+ QPtrList<TorrentInterface>::const_iterator it = downloads.begin();
+
+ while (it != downloads.end())
+ {
+ TorrentInterface* _tc = *it;
+ bool _completed = _tc->getStats().completed;
+
+ if (tc == _tc || (_completed != completed))
+ {
+ ++it;
+ continue;
+ }
+
+ int p = _tc->getPriority();
+
+ if (p < tp)
+ break;
+ else
+ _tc->setPriority(--p);
+
+ ++it;
+ }
+
+ tc->setPriority(0);
+
+ orderQueue();
+ }
+
+ void QueueManager::queue(kt::TorrentInterface* tc)
+ {
+ if (tc->getPriority() == 0)
+ enqueue(tc);
+ else
+ dequeue(tc);
+ }
+
+ void QueueManager::startSafely(kt::TorrentInterface* tc)
+ {
+ try
+ {
+ tc->start();
+ }
+ catch (bt::Error & err)
+ {
+ const TorrentStats & s = tc->getStats();
+
+ QString msg =
+ i18n("Error starting torrent %1 : %2")
+ .arg(s.torrent_name).arg(err.toString());
+
+ KMessageBox::error(0, msg, i18n("Error"));
+ }
+ }
+
+ void QueueManager::stopSafely(kt::TorrentInterface* tc, bool user, WaitJob* wjob)
+ {
+ try
+ {
+ tc->stop(user, wjob);
+ }
+ catch (bt::Error & err)
+ {
+ const TorrentStats & s = tc->getStats();
+
+ QString msg =
+ i18n("Error stopping torrent %1 : %2")
+ .arg(s.torrent_name).arg(err.toString());
+
+ KMessageBox::error(0, msg, i18n("Error"));
+ }
+ }
+
+ void QueueManager::onLowDiskSpace(kt::TorrentInterface* tc, bool toStop)
+ {
+ if(toStop)
+ {
+ stop(tc, false);
+ }
+
+ //then emit the signal to inform trayicon to show passive popup
+ emit lowDiskSpace(tc, toStop);
+ }
+
+ void QueueManager::torrentStopped(kt::TorrentInterface* tc )
+ {
+ orderQueue();
+ }
+
+ /////////////////////////////////////////////////////////////////////////////////////////////
+
+
+ QueuePtrList::QueuePtrList() : QPtrList<kt::TorrentInterface>()
+ {}
+
+ QueuePtrList::~QueuePtrList()
+ {}
+
+ int QueuePtrList::compareItems(QPtrCollection::Item item1, QPtrCollection::Item item2)
+ {
+ kt::TorrentInterface* tc1 = (kt::TorrentInterface*) item1;
+ kt::TorrentInterface* tc2 = (kt::TorrentInterface*) item2;
+
+ if (tc1->getPriority() == tc2->getPriority())
+ return 0;
+
+ if (tc1->getPriority() == 0 && tc2->getPriority() != 0)
+ return 1;
+ else if (tc1->getPriority() != 0 && tc2->getPriority() == 0)
+ return -1;
+
+ return tc1->getPriority() > tc2->getPriority() ? -1 : 1;
+
+ return 0;
+ }
+}
+
+#include "queuemanager.moc"
diff --git a/libktorrent/torrent/queuemanager.h b/libktorrent/torrent/queuemanager.h
new file mode 100644
index 0000000..658c252
--- /dev/null
+++ b/libktorrent/torrent/queuemanager.h
@@ -0,0 +1,173 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * ivasic@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef QUEUEMANAGER_H
+#define QUEUEMANAGER_H
+
+#include <set>
+#include <qobject.h>
+#include <qptrlist.h>
+
+#include <interfaces/torrentinterface.h>
+
+namespace kt
+{
+ class TrackersList;
+}
+
+namespace bt
+{
+ class SHA1Hash;
+ class AnnounceList;
+ struct TrackerTier;
+ class WaitJob;
+
+ class QueuePtrList : public QPtrList<kt::TorrentInterface>
+ {
+ public:
+ QueuePtrList();
+ virtual ~QueuePtrList();
+
+ protected:
+ int compareItems(QPtrCollection::Item item1, QPtrCollection::Item item2);
+ };
+
+ /**
+ * @author Ivan Vasic
+ * @brief This class contains list of all TorrentControls and is responsible for starting/stopping them
+ */
+ class QueueManager : public QObject
+ {
+ Q_OBJECT
+
+ public:
+ QueueManager();
+ virtual ~QueueManager();
+
+ void append(kt::TorrentInterface* tc);
+ void remove(kt::TorrentInterface* tc);
+ void clear();
+
+ kt::TorrentStartResponse start(kt::TorrentInterface* tc, bool user = true);
+ void stop(kt::TorrentInterface* tc, bool user = false);
+
+ void stopall(int type);
+ void startall(int type);
+
+ /**
+ * Stop all running torrents
+ * @param wjob WaitJob which waits for stopped events to reach the tracker
+ */
+ void onExit(WaitJob* wjob);
+
+ /**
+ * Enqueue/Dequeue function. Places a torrent in queue.
+ * If the torrent is already in queue this will remove it from queue.
+ * @param tc TorrentControl pointer.
+ */
+ void queue(kt::TorrentInterface* tc);
+
+ int count() { return downloads.count(); }
+ int countDownloads();
+ int countSeeds();
+
+ int getNumRunning(bool onlyDownload = false, bool onlySeed = false);
+ int getNumRunning(bool userControlled, bool onlyDownloads, bool onlySeeds);
+
+ void startNext();
+
+ typedef QPtrList<kt::TorrentInterface>::iterator iterator;
+
+ iterator begin();
+ iterator end();
+
+ /**
+ * See if we already loaded a torrent.
+ * @param ih The info hash of a torrent
+ * @return true if we do, false if we don't
+ */
+ bool allreadyLoaded(const SHA1Hash & ih) const;
+
+
+ /**
+ * Merge announce lists to a torrent
+ * @param ih The info_hash of the torrent to merge to
+ * @param trk First tier of trackers
+ */
+ void mergeAnnounceList(const SHA1Hash & ih,const TrackerTier* trk);
+
+ void setMaxDownloads(int m);
+ void setMaxSeeds(int m);
+
+ void setKeepSeeding(bool ks);
+
+ /**
+ * Sets global paused state for QueueManager and stopps all running torrents.
+ * No torrents will be automatically started/stopped with QM.
+ */
+ void setPausedState(bool pause);
+
+ /**
+ * Places all torrents from downloads in the right order in queue.
+ * Use this when torrent priorities get changed
+ */
+ void orderQueue();
+
+ signals:
+ /**
+ * User tried to enqueue a torrent that has reached max share ratio. It's not possible.
+ * Signal should be connected to SysTray slot which shows appropriate KPassivePopup info.
+ * @param tc The torrent in question.
+ */
+ void queuingNotPossible(kt::TorrentInterface* tc);
+
+ /**
+ * Diskspace is running low.
+ * Signal should be connected to SysTray slot which shows appropriate KPassivePopup info.
+ * @param tc The torrent in question.
+ */
+ void lowDiskSpace(kt::TorrentInterface* tc, bool stopped);
+
+ public slots:
+ void torrentFinished(kt::TorrentInterface* tc);
+ void torrentAdded(kt::TorrentInterface* tc, bool user, bool start_torrent);
+ void torrentRemoved(kt::TorrentInterface* tc);
+ void torrentStopped(kt::TorrentInterface* tc);
+ void onLowDiskSpace(kt::TorrentInterface* tc, bool toStop);
+
+ private:
+ void enqueue(kt::TorrentInterface* tc);
+ void dequeue(kt::TorrentInterface* tc);
+ void startSafely(kt::TorrentInterface* tc);
+ void stopSafely(kt::TorrentInterface* tc,bool user,WaitJob* wjob = 0);
+
+ bt::QueuePtrList downloads;
+ std::set<kt::TorrentInterface*> paused_torrents;
+
+ int max_downloads;
+ int max_seeds;
+
+
+ bool paused_state;
+ bool keep_seeding;
+ bool exiting;
+ };
+}
+#endif
diff --git a/libktorrent/torrent/request.cpp b/libktorrent/torrent/request.cpp
new file mode 100644
index 0000000..413eb11
--- /dev/null
+++ b/libktorrent/torrent/request.cpp
@@ -0,0 +1,52 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include "request.h"
+
+namespace bt
+{
+ Request::Request() : index(0),off(0),len(0),peer(0)
+ {}
+
+ Request::Request(Uint32 index,Uint32 off,Uint32 len,Uint32 peer)
+ : index(index),off(off),len(len),peer(peer)
+ {}
+
+ Request::Request(const Request & r)
+ : index(r.index),off(r.off),len(r.len),peer(r.peer)
+ {}
+
+ Request::~Request()
+ {}
+
+
+ Request & Request::operator = (const Request & r)
+ {
+ index = r.index;
+ off = r.off;
+ len = r.len;
+ peer = r.peer;
+ return *this;
+ }
+
+ bool operator == (const Request & a,const Request & b)
+ {
+ return a.index == b.index && a.len == b.len && a.off == b.off;
+ }
+}
diff --git a/libktorrent/torrent/request.h b/libktorrent/torrent/request.h
new file mode 100644
index 0000000..aeeff78
--- /dev/null
+++ b/libktorrent/torrent/request.h
@@ -0,0 +1,96 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef BTREQUEST_H
+#define BTREQUEST_H
+
+#include "globals.h"
+
+
+namespace bt
+{
+
+ /**
+ * @author Joris Guisson
+ * @brief Request of a piece sent to other peers
+ *
+ * This class keeps track of a request of a piece.
+ * The Request consists of an index (the index of the chunk),
+ * offset into the chunk and the length of a piece.
+ *
+ * The PeerID of the Peer who sent the request is also kept.
+ */
+ class Request
+ {
+ public:
+ /**
+ * Constructor, set everything to 0.
+ */
+ Request();
+
+ /**
+ * Constructor, set the index, offset,length and peer
+ * @param index The index of the chunk
+ * @param off The offset into the chunk
+ * @param len The length of the piece
+ * @param peer The ID of the Peer who sent the request
+ */
+ Request(Uint32 index,Uint32 off,Uint32 len,Uint32 peer);
+
+ /**
+ * Copy constructor.
+ * @param r Request to copy
+ */
+ Request(const Request & r);
+ virtual ~Request();
+
+ /// Get the index of the chunk
+ Uint32 getIndex() const {return index;}
+
+ /// Get the offset into the chunk
+ Uint32 getOffset() const {return off;}
+
+ /// Get the length of a the piece
+ Uint32 getLength() const {return len;}
+
+ /// Get the sending Peer
+ Uint32 getPeer() const {return peer;}
+
+ /**
+ * Assignmenth operator.
+ * @param r The Request to copy
+ */
+ Request & operator = (const Request & r);
+
+ /**
+ * Compare two requests. Return true if they are the same.
+ * This only compares the index,offset and length.
+ * @param a The first request
+ * @param b The second request
+ * @return true if they are equal
+ */
+ friend bool operator == (const Request & a,const Request & b);
+ private:
+ Uint32 index,off,len;
+ Uint32 peer;
+ };
+
+}
+
+#endif
diff --git a/libktorrent/torrent/server.cpp b/libktorrent/torrent/server.cpp
new file mode 100644
index 0000000..e3d00ae
--- /dev/null
+++ b/libktorrent/torrent/server.cpp
@@ -0,0 +1,200 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+
+#include <qserversocket.h>
+#include <mse/streamsocket.h>
+#include <util/sha1hash.h>
+#include <util/log.h>
+#include <net/portlist.h>
+#include <mse/encryptedserverauthenticate.h>
+#include "globals.h"
+#include "torrent.h"
+#include "server.h"
+#include "peermanager.h"
+#include "serverauthenticate.h"
+#include "ipblocklist.h"
+#include "authenticationmonitor.h"
+
+
+namespace bt
+{
+
+ class ServerSocket : public QServerSocket
+ {
+ Server* srv;
+ public:
+ ServerSocket(Server* srv,Uint16 port) : QServerSocket(port),srv(srv)
+ {
+ QSocketDevice* sd = socketDevice();
+ if (sd)
+ sd->setAddressReusable(true);
+ }
+
+ virtual ~ServerSocket()
+ {}
+
+ virtual void newConnection(int socket)
+ {
+ srv->newConnection(socket);
+ }
+ };
+
+
+
+
+ Server::Server(Uint16 port) : sock(0),port(0)
+ {
+ changePort(port);
+ encryption = false;
+ allow_unencrypted = true;
+ }
+
+
+ Server::~Server()
+ {
+ delete sock;
+ }
+
+ bool Server::isOK() const
+ {
+ return sock->ok();
+ }
+
+ void Server::changePort(Uint16 p)
+ {
+ if (p == port)
+ return;
+
+
+ if (sock && sock->ok())
+ Globals::instance().getPortList().removePort(port,net::TCP);
+
+ port = p;
+ delete sock;
+ sock = new ServerSocket(this,port);
+ if (isOK())
+ Globals::instance().getPortList().addNewPort(port,net::TCP,true);
+ }
+
+ void Server::addPeerManager(PeerManager* pman)
+ {
+ peer_managers.append(pman);
+ }
+
+ void Server::removePeerManager(PeerManager* pman)
+ {
+ peer_managers.remove(pman);
+ }
+
+ void Server::newConnection(int socket)
+ {
+ mse::StreamSocket* s = new mse::StreamSocket(socket);
+ if (peer_managers.count() == 0)
+ {
+ s->close();
+ delete s;
+ }
+ else
+ {
+ IPBlocklist& ipfilter = IPBlocklist::instance();
+ QString IP(s->getRemoteIPAddress());
+ if (ipfilter.isBlocked( IP ))
+ {
+ delete s;
+ return;
+ }
+
+ ServerAuthenticate* auth = 0;
+
+ if (encryption)
+ auth = new mse::EncryptedServerAuthenticate(s,this);
+ else
+ auth = new ServerAuthenticate(s,this);
+
+ AuthenticationMonitor::instance().add(auth);
+ }
+ }
+
+ void Server::close()
+ {
+ delete sock;
+ sock= 0;
+ }
+
+ Uint16 Server::getPortInUse() const
+ {
+ return port;
+ }
+
+ PeerManager* Server::findPeerManager(const SHA1Hash & hash)
+ {
+ QPtrList<PeerManager>::iterator i = peer_managers.begin();
+ while (i != peer_managers.end())
+ {
+ PeerManager* pm = *i;
+ if (pm && pm->getTorrent().getInfoHash() == hash)
+ {
+ if (!pm->isStarted())
+ return 0;
+ else
+ return pm;
+ }
+ i++;
+ }
+ return 0;
+ }
+
+ bool Server::findInfoHash(const SHA1Hash & skey,SHA1Hash & info_hash)
+ {
+ Uint8 buf[24];
+ memcpy(buf,"req2",4);
+ QPtrList<PeerManager>::iterator i = peer_managers.begin();
+ while (i != peer_managers.end())
+ {
+ PeerManager* pm = *i;
+ memcpy(buf+4,pm->getTorrent().getInfoHash().getData(),20);
+ if (SHA1Hash::generate(buf,24) == skey)
+ {
+ info_hash = pm->getTorrent().getInfoHash();
+ return true;
+ }
+ i++;
+ }
+ return false;
+ }
+
+ void Server::onError(int)
+ {
+ }
+
+
+ void Server::enableEncryption(bool allow_unencrypted)
+ {
+ encryption = true;
+ this->allow_unencrypted = allow_unencrypted;
+ }
+
+ void Server::disableEncryption()
+ {
+ encryption = false;
+ }
+}
+
+#include "server.moc"
diff --git a/libktorrent/torrent/server.h b/libktorrent/torrent/server.h
new file mode 100644
index 0000000..99c06eb
--- /dev/null
+++ b/libktorrent/torrent/server.h
@@ -0,0 +1,125 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef BTSERVER_H
+#define BTSERVER_H
+
+#include <qptrlist.h>
+#include <qobject.h>
+#include "globals.h"
+
+namespace bt
+{
+ class PeerManager;
+ class ServerAuthenticate;
+ class SHA1Hash;
+ class ServerSocket;
+
+
+ /**
+ * @author Joris Guisson
+ *
+ * Class which listens for incoming connections.
+ * Handles authentication and then hands of the new
+ * connections to a PeerManager.
+ *
+ * All PeerManager's should register with this class when they
+ * are created and should unregister when they are destroyed.
+ */
+ class Server : public QObject
+ {
+ Q_OBJECT
+
+ QPtrList<PeerManager> peer_managers;
+ ServerSocket* sock;
+ Uint16 port;
+ bool encryption;
+ bool allow_unencrypted;
+ public:
+ Server(Uint16 port);
+ virtual ~Server();
+
+ /// Check if everything is ok (are we successfully listening on the port)
+ bool isOK() const;
+
+ /**
+ * Change the port.
+ * @param port The new port
+ */
+ void changePort(Uint16 port);
+
+ /// Get the port in use
+ Uint16 getPortInUse() const;
+
+ /**
+ * Add a PeerManager.
+ * @param pman The PeerManager
+ */
+ void addPeerManager(PeerManager* pman);
+
+ /**
+ * Remove a PeerManager.
+ * @param pman The PeerManager
+ */
+ void removePeerManager(PeerManager* pman);
+
+ /**
+ * Find the PeerManager given the info_hash of it's torrent.
+ * @param hash The info_hash
+ * @return The PeerManager or 0 if one can't be found
+ */
+ PeerManager* findPeerManager(const SHA1Hash & hash);
+
+ /**
+ * Find the info_hash based on the skey hash. The skey hash is a hash
+ * of 'req2' followed by the info_hash. This function finds the info_hash
+ * which matches the skey hash.
+ * @param skey HASH('req2',info_hash)
+ * @param info_hash which matches
+ * @return true If one was found
+ */
+ bool findInfoHash(const SHA1Hash & skey,SHA1Hash & info_hash);
+
+ /**
+ * Enable encryption.
+ * @param allow_unencrypted Allow unencrypted connections (if encryption fails)
+ */
+ void enableEncryption(bool allow_unencrypted);
+
+ /**
+ * Disable encrypted authentication.
+ */
+ void disableEncryption();
+
+ bool isEncryptionEnabled() const {return encryption;}
+ bool unencryptedConnectionsAllowed() const {return allow_unencrypted;}
+
+ void close();
+
+ private slots:
+ void newConnection(int sock);
+ void onError(int);
+
+ private:
+ friend class ServerSocket;
+ };
+
+}
+
+#endif
diff --git a/libktorrent/torrent/serverauthenticate.cpp b/libktorrent/torrent/serverauthenticate.cpp
new file mode 100644
index 0000000..479f0ce
--- /dev/null
+++ b/libktorrent/torrent/serverauthenticate.cpp
@@ -0,0 +1,126 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <mse/streamsocket.h>
+#include <util/sha1hash.h>
+#include <util/log.h>
+#include <util/log.h>
+#include "globals.h"
+#include "server.h"
+#include "peermanager.h"
+#include "serverauthenticate.h"
+#include "peerid.h"
+#include "torrent.h"
+#include "ipblocklist.h"
+
+
+namespace bt
+{
+ bool ServerAuthenticate::s_firewalled = true;
+
+
+ ServerAuthenticate::ServerAuthenticate(mse::StreamSocket* sock,Server* server)
+ : AuthenticateBase(sock),server(server)
+ {
+ }
+
+
+ ServerAuthenticate::~ServerAuthenticate()
+ {
+ }
+
+
+ void ServerAuthenticate::onFinish(bool succes)
+ {
+ Out(SYS_CON|LOG_NOTICE) << "Authentication(S) to " << sock->getRemoteIPAddress()
+ << " : " << (succes ? "ok" : "failure") << endl;
+ finished = true;
+ setFirewalled(false);
+
+ if (!succes)
+ {
+ sock->deleteLater();
+ sock = 0;
+ }
+
+ timer.stop();
+ }
+
+ void ServerAuthenticate::handshakeRecieved(bool full)
+ {
+ Uint8* hs = handshake;
+ IPBlocklist& ipfilter = IPBlocklist::instance();
+
+ QString IP = sock->getRemoteIPAddress();
+
+ if (ipfilter.isBlocked( IP ))
+ {
+ onFinish(false);
+ return;
+ }
+
+ // try to find a PeerManager which has te right info hash
+ SHA1Hash rh(hs+28);
+ PeerManager* pman = server->findPeerManager(rh);
+ if (!pman)
+ {
+ Out(SYS_GEN|LOG_DEBUG) << "Cannot find PeerManager for hash : " << rh.toString() << endl;
+ onFinish(false);
+ return;
+ }
+
+ if (full)
+ {
+ // check if we aren't connecting to ourself
+ char tmp[21];
+ tmp[20] = '\0';
+ memcpy(tmp,hs+48,20);
+ PeerID peer_id = PeerID(tmp);
+ if (pman->getTorrent().getPeerID() == peer_id)
+ {
+ Out(SYS_CON|LOG_NOTICE) << "Lets not connect to our self" << endl;
+ onFinish(false);
+ return;
+ }
+
+ // check if we aren't already connected to the client
+ if (pman->connectedTo(peer_id))
+ {
+ Out(SYS_CON|LOG_NOTICE) << "Already connected to " << peer_id.toString() << endl;
+ onFinish(false);
+ return;
+ }
+
+
+ // send handshake and finish off
+ sendHandshake(rh,pman->getTorrent().getPeerID());
+ onFinish(true);
+ // hand over connection
+ pman->newConnection(sock,peer_id,supportedExtensions());
+ sock = 0;
+ }
+ else
+ {
+ // if the handshake wasn't fully received just send our handshake
+ sendHandshake(rh,pman->getTorrent().getPeerID());
+ }
+ }
+}
+
+#include "serverauthenticate.moc"
diff --git a/libktorrent/torrent/serverauthenticate.h b/libktorrent/torrent/serverauthenticate.h
new file mode 100644
index 0000000..70d3739
--- /dev/null
+++ b/libktorrent/torrent/serverauthenticate.h
@@ -0,0 +1,72 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef BTSERVERAUTHENTICATE_H
+#define BTSERVERAUTHENTICATE_H
+
+#include "authenticatebase.h"
+
+namespace bt
+{
+ class Server;
+ class SHA1Hash;
+ class PeerID;
+
+ /**
+ * @author Joris Guisson
+ *
+ * Handles the authentication of incoming connections on the Server.
+ * Once the authentication is finished, the socket gets handed over
+ * to the right PeerManager.
+ */
+ class ServerAuthenticate : public AuthenticateBase
+ {
+ Q_OBJECT
+ public:
+ ServerAuthenticate(mse::StreamSocket* sock,Server* server);
+ virtual ~ServerAuthenticate();
+
+ static bool isFirewalled();
+ static void setFirewalled(bool Firewalled);
+
+ protected:
+ void onFinish(bool succes);
+ void handshakeRecieved(bool full);
+
+ protected:
+ Server* server;
+
+ private:
+ static bool s_firewalled;
+ };
+
+}
+
+inline bool bt::ServerAuthenticate::isFirewalled()
+{
+ return s_firewalled;
+}
+
+inline void bt::ServerAuthenticate::setFirewalled(bool Firewalled)
+{
+ s_firewalled = Firewalled;
+}
+
+
+#endif
diff --git a/libktorrent/torrent/singlefilecache.cpp b/libktorrent/torrent/singlefilecache.cpp
new file mode 100644
index 0000000..7d31bef
--- /dev/null
+++ b/libktorrent/torrent/singlefilecache.cpp
@@ -0,0 +1,232 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <klocale.h>
+#include <qfileinfo.h>
+#include <qstringlist.h>
+#include <util/fileops.h>
+#include <util/error.h>
+#include <util/functions.h>
+#include <util/log.h>
+#include "torrent.h"
+#include "chunk.h"
+#include "globals.h"
+#include "cachefile.h"
+#include "singlefilecache.h"
+#include "preallocationthread.h"
+
+
+namespace bt
+{
+
+ SingleFileCache::SingleFileCache(Torrent& tor,const QString & tmpdir,const QString & datadir)
+ : Cache(tor,tmpdir,datadir),fd(0)
+ {
+ cache_file = tmpdir + "cache";
+ output_file = QFileInfo(cache_file).readLink();
+ }
+
+
+ SingleFileCache::~SingleFileCache()
+ {}
+
+ void SingleFileCache::changeTmpDir(const QString & ndir)
+ {
+ Cache::changeTmpDir(ndir);
+ cache_file = tmpdir + "cache";
+ }
+
+ KIO::Job* SingleFileCache::moveDataFiles(const QString & ndir)
+ {
+ return KIO::move(KURL::fromPathOrURL(output_file),KURL::fromPathOrURL(ndir));
+ }
+
+ void SingleFileCache::moveDataFilesCompleted(KIO::Job* /*job*/)
+ {
+ }
+
+ void bt::SingleFileCache::changeOutputPath(const QString & outputpath)
+ {
+ bt::Delete(cache_file);
+ output_file = outputpath;
+ datadir = output_file.left(output_file.findRev(bt::DirSeparator()));
+
+ bt::SymLink(output_file, cache_file);
+ }
+
+ bool SingleFileCache::prep(Chunk* c)
+ {
+ if (mmap_failures >= 3)
+ {
+ // mmap continuously fails, so stop using it
+ c->allocate();
+ c->setStatus(Chunk::BUFFERED);
+ }
+ else
+ {
+ Uint64 off = c->getIndex() * tor.getChunkSize();
+ Uint8* buf = (Uint8*)fd->map(c,off,c->getSize(),CacheFile::RW);
+ if (!buf)
+ {
+ mmap_failures++;
+ // buffer it if mmapping fails
+ Out(SYS_GEN|LOG_IMPORTANT) << "Warning : mmap failure, falling back to buffered mode" << endl;
+ c->allocate();
+ c->setStatus(Chunk::BUFFERED);
+ }
+ else
+ {
+ c->setData(buf,Chunk::MMAPPED);
+ }
+ }
+ return true;
+ }
+
+ void SingleFileCache::load(Chunk* c)
+ {
+ Uint64 off = c->getIndex() * tor.getChunkSize();
+ Uint8* buf = 0;
+ if (mmap_failures >= 3 || !(buf = (Uint8*)fd->map(c,off,c->getSize(),CacheFile::READ)))
+ {
+ c->allocate();
+ c->setStatus(Chunk::BUFFERED);
+ fd->read(c->getData(),c->getSize(),off);
+ if (mmap_failures < 3)
+ mmap_failures++;
+ }
+ else
+ {
+ c->setData(buf,Chunk::MMAPPED);
+ }
+ }
+
+ void SingleFileCache::save(Chunk* c)
+ {
+ // unmap the chunk if it is mapped
+ if (c->getStatus() == Chunk::MMAPPED)
+ {
+ fd->unmap(c->getData(),c->getSize());
+ c->clear();
+ c->setStatus(Chunk::ON_DISK);
+ }
+ else if (c->getStatus() == Chunk::BUFFERED)
+ {
+ Uint64 off = c->getIndex() * tor.getChunkSize();
+ fd->write(c->getData(),c->getSize(),off);
+ c->clear();
+ c->setStatus(Chunk::ON_DISK);
+ }
+ }
+
+ void SingleFileCache::create()
+ {
+ QFileInfo fi(cache_file);
+ if (!fi.exists())
+ {
+ QString out_file = fi.readLink();
+
+ if (out_file.isNull())
+ out_file = datadir + tor.getNameSuggestion();
+
+ if (!bt::Exists(out_file))
+ bt::Touch(out_file);
+ else
+ preexisting_files = true;
+
+ if (bt::Exists(cache_file))
+ bt::Delete(cache_file);
+
+ bt::SymLink(out_file,cache_file);
+ output_file = out_file;
+ }
+ else
+ {
+ QString out_file = fi.readLink();
+ if (!bt::Exists(out_file))
+ bt::Touch(out_file);
+ else
+ preexisting_files = true;
+ }
+ }
+
+ void SingleFileCache::close()
+ {
+ if (fd)
+ {
+ fd->close();
+ delete fd;
+ fd = 0;
+ }
+ }
+
+ void SingleFileCache::open()
+ {
+ if (fd)
+ return;
+
+ try
+ {
+ fd = new CacheFile();
+ fd->open(cache_file,tor.getFileLength());
+ }
+ catch (...)
+ {
+ fd->close();
+ delete fd;
+ fd = 0;
+ throw;
+ }
+ }
+
+ void SingleFileCache::preallocateDiskSpace(PreallocationThread* prealloc)
+ {
+ if (!fd)
+ open();
+
+ if (!prealloc->isStopped())
+ fd->preallocate(prealloc);
+ else
+ prealloc->setNotFinished();
+ }
+
+ bool SingleFileCache::hasMissingFiles(QStringList & sl)
+ {
+ QFileInfo fi(cache_file);
+ if (!fi.exists())
+ {
+ QString out_file = fi.readLink();
+ sl.append(fi.readLink());
+ return true;
+ }
+ return false;
+ }
+
+ void SingleFileCache::deleteDataFiles()
+ {
+ bt::Delete(output_file);
+ }
+
+ Uint64 SingleFileCache::diskUsage()
+ {
+ if (!fd)
+ open();
+
+ return fd->diskUsage();
+ }
+}
diff --git a/libktorrent/torrent/singlefilecache.h b/libktorrent/torrent/singlefilecache.h
new file mode 100644
index 0000000..faa71b6
--- /dev/null
+++ b/libktorrent/torrent/singlefilecache.h
@@ -0,0 +1,64 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef BTSINGLEFILECACHE_H
+#define BTSINGLEFILECACHE_H
+
+#include "cache.h"
+
+namespace bt
+{
+ class CacheFile;
+
+
+ /**
+ * @author Joris Guisson
+ * @brief Cache for single file torrents
+ *
+ * This class implements Cache for a single file torrent
+ */
+ class SingleFileCache : public Cache
+ {
+ QString cache_file;
+ QString output_file;
+ CacheFile* fd;
+ public:
+ SingleFileCache(Torrent& tor,const QString & tmpdir,const QString & datadir);
+ virtual ~SingleFileCache();
+
+ virtual bool prep(Chunk* c);
+ virtual void load(Chunk* c);
+ virtual void save(Chunk* c);
+ virtual void create();
+ virtual void close();
+ virtual void open();
+ virtual void changeTmpDir(const QString & ndir);
+ virtual KIO::Job* moveDataFiles(const QString & ndir);
+ virtual void moveDataFilesCompleted(KIO::Job* job);
+ virtual void changeOutputPath(const QString& outputpath);
+ virtual QString getOutputPath() const {return output_file;}
+ virtual void preallocateDiskSpace(PreallocationThread* prealloc);
+ virtual bool hasMissingFiles(QStringList & sl);
+ virtual void deleteDataFiles();
+ virtual Uint64 diskUsage();
+ };
+
+}
+
+#endif
diff --git a/libktorrent/torrent/speedestimater.cpp b/libktorrent/torrent/speedestimater.cpp
new file mode 100644
index 0000000..f12b5ac
--- /dev/null
+++ b/libktorrent/torrent/speedestimater.cpp
@@ -0,0 +1,105 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <qpair.h>
+#include <qvaluelist.h>
+#include <util/log.h>
+#include <util/timer.h>
+#include "speedestimater.h"
+#include <util/functions.h>
+
+namespace bt
+{
+ class SpeedEstimater::SpeedEstimaterPriv
+ {
+ float rate;
+ QValueList<QPair<Uint32,TimeStamp> > dlrate;
+ public:
+ SpeedEstimaterPriv() : rate(0) {}
+ ~SpeedEstimaterPriv() {}
+
+ void data(Uint32 bytes)
+ {
+ dlrate.append(qMakePair(bytes,GetCurrentTime()));
+ }
+
+ void update()
+ {
+ TimeStamp now = GetCurrentTime();
+
+ Uint32 bytes = 0,oldest = now;
+ QValueList<QPair<Uint32,TimeStamp> >::iterator i = dlrate.begin();
+ while (i != dlrate.end())
+ {
+ QPair<Uint32,TimeStamp> & p = *i;
+ if (now - p.second > 3000)
+ {
+ i = dlrate.erase(i);
+ }
+ else
+ {
+ if (p.second < oldest)
+ oldest = p.second;
+
+ bytes += p.first;
+ i++;
+ }
+ }
+
+ Uint32 d = 3000;
+
+ if (bytes == 0)
+ {
+ rate = 0;
+ }
+ else
+ {
+ // Out() << "bytes = " << bytes << " d = " << d << endl;
+ rate = (float) bytes / (d * 0.001f);
+ }
+ }
+
+ float getRate() const {return rate;}
+ };
+
+ SpeedEstimater::SpeedEstimater()
+ {
+ download_rate = 0;
+ down = new SpeedEstimaterPriv();
+ }
+
+
+ SpeedEstimater::~SpeedEstimater()
+ {
+ delete down;
+ }
+
+
+
+ void SpeedEstimater::onRead(Uint32 bytes)
+ {
+ down->data(bytes);
+ }
+
+ void SpeedEstimater::update()
+ {
+ down->update();
+ download_rate = down->getRate();
+ }
+}
diff --git a/libktorrent/torrent/speedestimater.h b/libktorrent/torrent/speedestimater.h
new file mode 100644
index 0000000..16bbdcc
--- /dev/null
+++ b/libktorrent/torrent/speedestimater.h
@@ -0,0 +1,55 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef BTSPEEDESTIMATER_H
+#define BTSPEEDESTIMATER_H
+
+
+#include "globals.h"
+
+namespace bt
+{
+
+ /**
+ * @author Joris Guisson
+ * @brief Estimates download speed
+ *
+ * This class estimates the download speed.
+ */
+ class SpeedEstimater
+ {
+ class SpeedEstimaterPriv;
+ public:
+ SpeedEstimater();
+ virtual ~SpeedEstimater();
+
+
+ void onRead(Uint32 bytes);
+ void update();
+
+ double downloadRate() const {return download_rate;}
+
+ private:
+ double download_rate;
+ SpeedEstimaterPriv* down;
+ };
+
+}
+
+#endif
diff --git a/libktorrent/torrent/statsfile.cpp b/libktorrent/torrent/statsfile.cpp
new file mode 100644
index 0000000..2ffd3ae
--- /dev/null
+++ b/libktorrent/torrent/statsfile.cpp
@@ -0,0 +1,120 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include "statsfile.h"
+
+#include "globals.h"
+#include <util/log.h>
+#include <util/functions.h>
+
+#include <qstring.h>
+#include <qfile.h>
+#include <qtextstream.h>
+
+namespace bt
+{
+
+ StatsFile::StatsFile(QString filename)
+ :m_filename(filename)
+ {
+ m_file.setName(filename);
+ readSync();
+ }
+
+ StatsFile::~StatsFile()
+ {
+ close();
+ }
+
+ void StatsFile::close()
+ {
+ m_file.close();
+ }
+
+ void StatsFile::write(QString key, QString value)
+ {
+ m_values.insert(key.stripWhiteSpace(), value.stripWhiteSpace());
+ }
+
+ QString StatsFile::readString(QString key)
+ {
+ return m_values[key].stripWhiteSpace();
+ }
+
+ Uint64 StatsFile::readUint64(QString key)
+ {
+ bool ok = true;
+ Uint64 val = readString(key).toULongLong(&ok);
+ return val;
+ }
+
+ int StatsFile::readInt(QString key)
+ {
+ bool ok = true;
+ int val = readString(key).toInt(&ok);
+ return val;
+ }
+
+ bool StatsFile::readBoolean(QString key)
+ {
+ return (bool) readInt(key);
+ }
+
+ unsigned long StatsFile::readULong(QString key)
+ {
+ bool ok = true;
+ return readString(key).toULong(&ok);
+ }
+
+ float bt::StatsFile::readFloat( QString key )
+ {
+ bool ok = true;
+ return readString(key).toFloat(&ok);
+ }
+
+ void StatsFile::readSync()
+ {
+ if (!m_file.open(IO_ReadOnly))
+ return;
+
+ QTextStream in(&m_file);
+ while (!in.atEnd())
+ {
+ QString line = in.readLine();
+ QString tmp = line.left(line.find('='));
+ m_values.insert(tmp, line.mid(tmp.length()+1));
+ }
+ close();
+ }
+
+ void StatsFile::writeSync()
+ {
+ if (!m_file.open(IO_WriteOnly))
+ return;
+ QTextStream out(&m_file);
+ QMap<QString, QString>::iterator it = m_values.begin();
+ while(it!=m_values.end())
+ {
+ out << it.key() << "=" << it.data() << ::endl;
+ ++it;
+ }
+ close();
+ }
+
+}
diff --git a/libktorrent/torrent/statsfile.h b/libktorrent/torrent/statsfile.h
new file mode 100644
index 0000000..9f7a145
--- /dev/null
+++ b/libktorrent/torrent/statsfile.h
@@ -0,0 +1,91 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef BTSTATSFILE_H
+#define BTSTATSFILE_H
+
+#include <qstring.h>
+#include <qfile.h>
+#include <qmap.h>
+
+#include <util/constants.h>
+
+namespace bt
+{
+
+ /**
+ * @brief This class is used for loading/storing torrent stats in a file.
+ * @author Ivan Vasic <ivasic@gmail.com>
+ */
+ class StatsFile
+ {
+ public:
+ /**
+ * @brief A constructor.
+ * Constructs StatsFile object and calls readSync().
+ */
+ StatsFile(QString filename);
+ ~StatsFile();
+
+ ///Closes QFile
+ void close();
+
+ /**
+ * @brief Main read function.
+ * @return QString value that correspodents to key.
+ * @param key - QString stats key.
+ */
+ QString readString(QString key);
+
+ Uint64 readUint64(QString key);
+ bool readBoolean(QString key);
+ int readInt(QString key);
+ unsigned long readULong(QString key);
+ float readFloat(QString key);
+
+ /**
+ * @brief Writes key and value.
+ * It only inserts pair of key/value to the m_values. To make changes to file call writeSync().
+ * @param key - QString key
+ * @param value - QString value.
+ */
+ void write(QString key, QString value);
+
+ ///Reads data from stats file to m_values.
+ void readSync();
+
+ ///Writes data from m_values to stats file.
+ void writeSync();
+
+ /**
+ * See if there is a key in the stats file
+ * @param key The key
+ * @return true if key is in the stats file
+ */
+ bool hasKey(const QString & key) const {return m_values.contains(key);}
+
+ private:
+ QString m_filename;
+ QFile m_file;
+
+ QMap<QString, QString> m_values;
+ };
+}
+
+#endif
diff --git a/libktorrent/torrent/timeestimator.cpp b/libktorrent/torrent/timeestimator.cpp
new file mode 100644
index 0000000..7d18300
--- /dev/null
+++ b/libktorrent/torrent/timeestimator.cpp
@@ -0,0 +1,278 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ivan Vasić *
+ * ivasic@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <math.h>
+#include "timeestimator.h"
+#include "torrentcontrol.h"
+#include "settings.h"
+
+#include <torrent/globals.h>
+#include <util/log.h>
+#include <util/constants.h>
+
+using namespace kt;
+
+namespace bt
+{
+ TimeEstimator::TimeEstimator(TorrentControl* tc)
+ : m_tc(tc)
+ {
+ m_samples = new SampleQueue(20);
+ m_lastAvg = 0;
+ m_perc = -1;
+
+ //default is KT algorithm
+ m_algorithm = (ETAlgorithm) Settings::eta();
+ }
+
+
+ TimeEstimator::~TimeEstimator()
+ {
+ delete m_samples;
+ }
+
+ Uint32 TimeEstimator::estimate()
+ {
+ const TorrentStats& s = m_tc->getStats();
+
+ // only estimate when we are downloading or stalled
+ if (!(s.status == kt::DOWNLOADING || s.status == kt::STALLED))
+ return (Uint32) - 1;
+
+ //ones without pre-calculations
+ switch (m_algorithm)
+ {
+
+ case ETA_CSA:
+ return estimateCSA();
+
+ case ETA_GASA:
+ return estimateGASA();
+
+ case ETA_KT:
+ return estimateKT();
+ }
+
+ //complicated ones :)
+
+ Uint32 sample = (Uint32) s.download_rate;
+ //push new sample
+ m_samples->push(sample);
+
+
+ switch (m_algorithm)
+ {
+ case ETA_MAVG:
+ return estimateMAVG();
+
+ case ETA_WINX:
+ return estimateWINX();
+
+ default:
+ return -1;
+ }
+ }
+
+ Uint32 TimeEstimator::estimateCSA()
+ {
+ const TorrentStats& s = m_tc->getStats();
+
+ if (s.download_rate == 0)
+ return (Uint32) - 1;
+
+ return (int)floor((float)s.bytes_left_to_download / (float)s.download_rate);
+ }
+
+ Uint32 TimeEstimator::estimateGASA()
+ {
+ const TorrentStats& s = m_tc->getStats();
+
+ if (m_tc->getRunningTimeDL() > 0 && s.bytes_downloaded > 0)
+ {
+ double avg_speed = (double) s.bytes_downloaded / (double) m_tc->getRunningTimeDL();
+ return (Uint32) floor((double) s.bytes_left_to_download / avg_speed);
+ }
+
+ return (Uint32) - 1;
+ }
+
+ Uint32 TimeEstimator::estimateWINX()
+ {
+ const TorrentStats& s = m_tc->getStats();
+
+ if (m_samples->sum() > 0 && m_samples->count() > 0)
+ return (Uint32) floor((double) s.bytes_left_to_download / ((double) m_samples->sum() / (double) m_samples->count()));
+
+ return (Uint32) - 1;
+ }
+
+ Uint32 TimeEstimator::estimateMAVG()
+ {
+ const TorrentStats& s = m_tc->getStats();
+
+ if (m_samples->count() > 0)
+ {
+ double lavg;
+
+ if (m_lastAvg == 0)
+ lavg = (Uint32) m_samples->sum() / m_samples->count();
+ else
+ lavg = m_lastAvg - ((double) m_samples->first() / (double) m_samples->count()) + ((double) m_samples->last() / (double) m_samples->count());
+
+ m_lastAvg = (Uint32) floor(lavg);
+
+ if (lavg > 0)
+ return (Uint32) floor((double) s.bytes_left_to_download / ((lavg + (m_samples->sum() / m_samples->count())) / 2));
+
+ return (Uint32) - 1;
+ }
+
+ return (Uint32) - 1;
+ }
+
+}
+
+bt::SampleQueue::SampleQueue(int max)
+ : m_size(max), m_count(0)
+{
+ m_samples = new Uint32[max];
+
+ for (int i = 0; i < m_size; ++i)
+ m_samples[i] = 0;
+
+ m_end = -1;
+
+ m_start = 0;
+}
+
+bt::SampleQueue::~ SampleQueue()
+{
+ delete [] m_samples;
+}
+
+void bt::SampleQueue::push(Uint32 sample)
+{
+ if (m_count < m_size)
+ {
+ //it's not full yet
+ m_samples[(++m_end) % m_size ] = sample;
+ m_count++;
+
+ return;
+ }
+
+ //since it's full I'll just replace the oldest value with new one and update all variables.
+ m_end = (++m_end) % m_size;
+
+ m_start = (++m_start) % m_size;
+
+ m_samples[m_end] = sample;
+}
+
+Uint32 bt::SampleQueue::first()
+{
+ return m_samples[m_start];
+}
+
+Uint32 bt::SampleQueue::last()
+{
+ return m_samples[m_end];
+}
+
+bool bt::SampleQueue::isFull()
+{
+ return m_count >= m_size;
+}
+
+int bt::SampleQueue::count()
+{
+ return m_count;
+}
+
+Uint32 bt::SampleQueue::sum()
+{
+ Uint32 s = 0;
+
+ for (int i = 0; i < m_count; ++i)
+ s += m_samples[i];
+
+ return s;
+}
+
+void bt::TimeEstimator::setAlgorithm(const ETAlgorithm& theValue)
+{
+ m_algorithm = theValue;
+}
+
+Uint32 bt::TimeEstimator::estimateKT()
+{
+ const TorrentStats& s = m_tc->getStats();
+
+ Uint32 sample = (Uint32) s.download_rate;
+
+ //push new sample
+ m_samples->push(sample);
+
+ double perc = (double) s.bytes_downloaded / (double) s.total_bytes;
+
+ int percentage = (int)(perc) * 100;
+
+ //calculate percentage increasement
+ double delta = 1 - 1 / (perc / m_perc);
+
+ //remember last percentage
+ m_perc = perc;
+
+
+ if (s.bytes_downloaded < 1024*1024*100 && sample > 0) // < 100KB
+ {
+ m_lastETA = estimateGASA();
+ return m_lastETA;
+ }
+
+ if (percentage >= 99 && sample > 0 && s.bytes_left_to_download <= 10*1024*1024*1024) //1% of a very large torrent could be hundreds of MB so limit it to 10MB
+ {
+
+ if (!m_samples->isFull())
+ {
+ m_lastETA = estimateWINX();
+
+ if (m_lastETA == (Uint32) - 1)
+ m_lastETA = estimateGASA();
+
+ return m_lastETA;
+ }
+ else
+ {
+ m_lastETA = (Uint32) - 1;
+
+ if (delta > 0.0001)
+ m_lastETA = estimateMAVG();
+
+ if (m_lastETA == (Uint32) - 1)
+ m_lastETA = estimateGASA();
+ }
+
+ return m_lastETA;
+ }
+
+ m_lastETA = estimateGASA();
+
+ return m_lastETA;
+}
diff --git a/libktorrent/torrent/timeestimator.h b/libktorrent/torrent/timeestimator.h
new file mode 100644
index 0000000..972e239
--- /dev/null
+++ b/libktorrent/torrent/timeestimator.h
@@ -0,0 +1,119 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ivan Vasić *
+ * ivasic@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef BTTIMEESTIMATOR_H
+#define BTTIMEESTIMATOR_H
+
+#include <util/constants.h>
+
+namespace bt
+{
+ class TorrentControl;
+
+ /**
+ * Simple queue class for samples. Optimized for speed and size
+ * without posibility to dynamically resize itself.
+ * @author Ivan Vasic <ivasic@gmail.com>
+ */
+ class SampleQueue
+ {
+ public:
+ SampleQueue(int max);
+ ~SampleQueue();
+
+ /**
+ * Inserts new sample into the queue. The oldest sample is overwritten.
+ */
+ void push(Uint32 sample);
+
+ Uint32 first();
+ Uint32 last();
+
+ bool isFull();
+
+ /**
+ * This function will return the number of samples in queue until it counts m_size number of elements.
+ * After this point it will always return m_size since no samples are being deleted.
+ */
+ int count();
+
+ /**
+ * Returns the sum of all samples.
+ */
+ Uint32 sum();
+
+ private:
+ int m_size;
+ int m_count;
+
+ int m_start;
+ int m_end;
+
+ Uint32* m_samples;
+ };
+
+ /**
+ * ETA estimator class. It will use different algorithms for different download phases.
+ * @author Ivan Vasic <ivasic@gmail.com>
+ */
+ class TimeEstimator
+ {
+ public:
+
+ enum ETAlgorithm
+ {
+ ETA_KT, //ktorrent default algorithm - combination of the following according to our tests
+ ETA_CSA, //current speed algorithm
+ ETA_GASA, //global average speed algorithm
+ ETA_WINX, //window of X algorithm
+ ETA_MAVG //moving average algorithm
+ };
+
+ TimeEstimator(TorrentControl* tc);
+ ~TimeEstimator();
+
+ ///Returns ETA for m_tc torrent.
+ Uint32 estimate();
+
+ void setAlgorithm(const ETAlgorithm& theValue);
+ ETAlgorithm algorithm() const { return m_algorithm; }
+
+ private:
+
+ Uint32 estimateCSA();
+ Uint32 estimateGASA();
+ Uint32 estimateWINX();
+ Uint32 estimateMAVG();
+ Uint32 estimateKT();
+
+ TorrentControl* m_tc;
+ SampleQueue* m_samples;
+
+ Uint32 m_lastAvg;
+ Uint32 m_lastETA;
+
+ //last percentage
+ double m_perc;
+
+ ETAlgorithm m_algorithm;
+ };
+
+}
+
+#endif
diff --git a/libktorrent/torrent/torrent.cpp b/libktorrent/torrent/torrent.cpp
new file mode 100644
index 0000000..6b8739b
--- /dev/null
+++ b/libktorrent/torrent/torrent.cpp
@@ -0,0 +1,449 @@
+/***************************************************************************
+ * Copyright (C) 2005 by *
+ * Joris Guisson <joris.guisson@gmail.com> *
+ * Ivan Vasic <ivasic@gmail.com> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <qfile.h>
+#include <qdatastream.h>
+#include <qstringlist.h>
+#include <util/log.h>
+#include <util/functions.h>
+#include <util/error.h>
+#include <util/sha1hashgen.h>
+#include <time.h>
+#include <stdlib.h>
+#include "torrent.h"
+#include "bdecoder.h"
+#include "bnode.h"
+#include "announcelist.h"
+
+#include <klocale.h>
+
+namespace bt
+{
+
+ Torrent::Torrent() : piece_length(0),file_length(0),priv_torrent(false)
+ {
+ encoding = "utf8";
+ trackers = 0;
+ }
+
+
+ Torrent::~Torrent()
+ {
+ delete trackers;
+ }
+
+
+ void Torrent::load(const QByteArray & data,bool verbose)
+ {
+ BNode* node = 0;
+
+ try
+ {
+ BDecoder decoder(data,verbose);
+ node = decoder.decode();
+ BDictNode* dict = dynamic_cast<BDictNode*>(node);
+ if (!dict)
+ throw Error(i18n("Corrupted torrent!"));
+
+ // see if we can find an encoding node
+ BValueNode* enc = dict->getValue("encoding");
+ if (enc)
+ {
+ encoding = enc->data().toString();
+ Out() << "Encoding : " << encoding << endl;
+ }
+
+ BValueNode* announce = dict->getValue("announce");
+ BListNode* nodes = dict->getList("nodes");
+ if (!announce && !nodes)
+ throw Error(i18n("Torrent has no announce or nodes field"));
+
+ if (announce)
+ loadTrackerURL(announce);
+
+ if (nodes) // DHT torrrents have a node key
+ loadNodes(nodes);
+
+ loadInfo(dict->getDict("info"));
+ loadAnnounceList(dict->getData("announce-list"));
+ BNode* n = dict->getData("info");
+ SHA1HashGen hg;
+ Uint8* info = (Uint8*)data.data();
+ info_hash = hg.generate(info + n->getOffset(),n->getLength());
+ delete node;
+ }
+ catch (...)
+ {
+ delete node;
+ throw;
+ }
+ }
+
+ void Torrent::load(const QString & file,bool verbose)
+ {
+ QFile fptr(file);
+ if (!fptr.open(IO_ReadOnly))
+ throw Error(i18n(" Unable to open torrent file %1 : %2")
+ .arg(file).arg(fptr.errorString()));
+
+ QByteArray data(fptr.size());
+ // Out() << "File size = " << fptr.size() << endl;
+ fptr.readBlock(data.data(),fptr.size());
+
+ load(data,verbose);
+ }
+
+ void Torrent::loadInfo(BDictNode* dict)
+ {
+ if (!dict)
+ throw Error(i18n("Corrupted torrent!"));
+
+ loadPieceLength(dict->getValue("piece length"));
+ BValueNode* n = dict->getValue("length");
+ if (n)
+ loadFileLength(n);
+ else
+ loadFiles(dict->getList("files"));
+
+ loadHash(dict->getValue("pieces"));
+ loadName(dict->getValue("name"));
+ n = dict->getValue("private");
+ if (n && n->data().toInt() == 1)
+ priv_torrent = true;
+
+ // do a safety check to see if the number of hashes matches the file_length
+ Uint32 num_chunks = (file_length / this->piece_length);
+ if (file_length % piece_length > 0)
+ num_chunks++;
+
+ if (num_chunks != hash_pieces.count())
+ {
+ Out(SYS_GEN|LOG_DEBUG) << "File sizes and number of hashes do not match for " << name_suggestion << endl;
+ throw Error(i18n("Corrupted torrent!"));
+ }
+ }
+
+ void Torrent::loadFiles(BListNode* node)
+ {
+ Out() << "Multi file torrent" << endl;
+ if (!node)
+ throw Error(i18n("Corrupted torrent!"));
+ Uint32 idx = 0;
+ BListNode* fl = node;
+ for (Uint32 i = 0;i < fl->getNumChildren();i++)
+ {
+ BDictNode* d = fl->getDict(i);
+ if (!d)
+ throw Error(i18n("Corrupted torrent!"));
+
+ BListNode* ln = d->getList("path");
+ if (!ln)
+ throw Error(i18n("Corrupted torrent!"));
+
+ QString path;
+ for (Uint32 j = 0;j < ln->getNumChildren();j++)
+ {
+ BValueNode* v = ln->getValue(j);
+ if (!v || v->data().getType() != Value::STRING)
+ throw Error(i18n("Corrupted torrent!"));
+
+ QString sd = v->data().toString(encoding);
+ path += sd;
+ if (j + 1 < ln->getNumChildren())
+ path += bt::DirSeparator();
+ }
+
+ // we do not want empty dirs
+ if (path.endsWith(bt::DirSeparator()))
+ continue;
+
+ if (!checkPathForDirectoryTraversal(path))
+ throw Error(i18n("Corrupted torrent!"));
+
+ BValueNode* v = d->getValue("length");
+ if (!v)
+ throw Error(i18n("Corrupted torrent!"));
+
+ if (v->data().getType() == Value::INT || v->data().getType() == Value::INT64)
+ {
+ Uint64 s = v->data().toInt64();
+ TorrentFile file(idx,path,file_length,s,piece_length);
+
+ // update file_length
+ file_length += s;
+ files.append(file);
+ }
+ else
+ {
+ throw Error(i18n("Corrupted torrent!"));
+ }
+ idx++;
+ }
+ }
+
+ void Torrent::loadTrackerURL(BValueNode* node)
+ {
+ if (!node || node->data().getType() != Value::STRING)
+ throw Error(i18n("Corrupted torrent!"));
+
+ // tracker_urls.append(KURL(node->data().toString(encoding).stripWhiteSpace()));
+ if (!trackers)
+ trackers = new TrackerTier();
+
+ trackers->urls.append(KURL(node->data().toString(encoding).stripWhiteSpace()));
+ }
+
+ void Torrent::loadPieceLength(BValueNode* node)
+ {
+ if (!node)
+ throw Error(i18n("Corrupted torrent!"));
+
+ if (node->data().getType() == Value::INT)
+ piece_length = node->data().toInt();
+ else if (node->data().getType() == Value::INT64)
+ piece_length = node->data().toInt64();
+ else
+ throw Error(i18n("Corrupted torrent!"));
+ }
+
+ void Torrent::loadFileLength(BValueNode* node)
+ {
+ if (!node)
+ throw Error(i18n("Corrupted torrent!"));
+
+ if (node->data().getType() == Value::INT)
+ file_length = node->data().toInt();
+ else if (node->data().getType() == Value::INT64)
+ file_length = node->data().toInt64();
+ else
+ throw Error(i18n("Corrupted torrent!"));
+ }
+
+ void Torrent::loadHash(BValueNode* node)
+ {
+ if (!node || node->data().getType() != Value::STRING)
+ throw Error(i18n("Corrupted torrent!"));
+
+
+ QByteArray hash_string = node->data().toByteArray();
+ for (unsigned int i = 0;i < hash_string.size();i+=20)
+ {
+ Uint8 h[20];
+ memcpy(h,hash_string.data()+i,20);
+ SHA1Hash hash(h);
+ hash_pieces.append(hash);
+ }
+ }
+
+ void Torrent::loadName(BValueNode* node)
+ {
+ if (!node || node->data().getType() != Value::STRING)
+ throw Error(i18n("Corrupted torrent!"));
+
+ name_suggestion = node->data().toString(encoding);
+ }
+
+ void Torrent::loadAnnounceList(BNode* node)
+ {
+ if (!node)
+ return;
+
+ BListNode* ml = dynamic_cast<BListNode*>(node);
+ if (!ml)
+ return;
+
+ if (!trackers)
+ trackers = new TrackerTier();
+
+ TrackerTier* tier = trackers;
+ //ml->printDebugInfo();
+ for (Uint32 i = 0;i < ml->getNumChildren();i++)
+ {
+ BListNode* url = dynamic_cast<BListNode*>(ml->getChild(i));
+ if (!url)
+ throw Error(i18n("Parse Error"));
+
+ for (Uint32 j = 0;j < url->getNumChildren();j++)
+ {
+ BValueNode* vn = dynamic_cast<BValueNode*>(url->getChild(j));
+ if (!vn)
+ throw Error(i18n("Parse Error"));
+
+ KURL url(vn->data().toString().stripWhiteSpace());
+ tier->urls.append(url);
+ //Out() << "Added tracker " << url << endl;
+ }
+ tier->next = new TrackerTier();
+ tier = tier->next;
+ }
+ }
+
+ void Torrent::loadNodes(BListNode* node)
+ {
+ for (Uint32 i = 0;i < node->getNumChildren();i++)
+ {
+ BListNode* c = node->getList(i);
+ if (!c || c->getNumChildren() != 2)
+ throw Error(i18n("Corrupted torrent!"));
+
+ // first child is the IP, second the port
+ BValueNode* ip = c->getValue(0);
+ BValueNode* port = c->getValue(1);
+ if (!ip || !port)
+ throw Error(i18n("Corrupted torrent!"));
+
+ if (ip->data().getType() != Value::STRING)
+ throw Error(i18n("Corrupted torrent!"));
+
+ if (port->data().getType() != Value::INT)
+ throw Error(i18n("Corrupted torrent!"));
+
+ // add the DHT node
+ kt::DHTNode n;
+ n.ip = ip->data().toString();
+ n.port = port->data().toInt();
+ nodes.append(n);
+ }
+ }
+
+ void Torrent::debugPrintInfo()
+ {
+ Out() << "Name : " << name_suggestion << endl;
+
+// for (KURL::List::iterator i = tracker_urls.begin();i != tracker_urls.end();i++)
+// Out() << "Tracker URL : " << *i << endl;
+
+ Out() << "Piece Length : " << piece_length << endl;
+ if (this->isMultiFile())
+ {
+ Out() << "Files : " << endl;
+ Out() << "===================================" << endl;
+ for (Uint32 i = 0;i < getNumFiles();i++)
+ {
+ TorrentFile & tf = getFile(i);
+ Out() << "Path : " << tf.getPath() << endl;
+ Out() << "Size : " << tf.getSize() << endl;
+ Out() << "First Chunk : " << tf.getFirstChunk() << endl;
+ Out() << "Last Chunk : " << tf.getLastChunk() << endl;
+ Out() << "First Chunk Off : " << tf.getFirstChunkOffset() << endl;
+ Out() << "Last Chunk Size : " << tf.getLastChunkSize() << endl;
+ Out() << "===================================" << endl;
+ }
+ }
+ else
+ {
+ Out() << "File Length : " << file_length << endl;
+ }
+ Out() << "Pieces : " << hash_pieces.size() << endl;
+ }
+
+ bool Torrent::verifyHash(const SHA1Hash & h,Uint32 index)
+ {
+ if (index >= hash_pieces.count())
+ return false;
+
+ const SHA1Hash & ph = hash_pieces[index];
+ return ph == h;
+ }
+
+ const SHA1Hash & Torrent::getHash(Uint32 idx) const
+ {
+ if (idx >= hash_pieces.count())
+ throw Error(QString("Torrent::getHash %1 is out of bounds").arg(idx));
+
+ return hash_pieces[idx];
+ }
+
+ TorrentFile & Torrent::getFile(Uint32 idx)
+ {
+ if (idx >= files.size())
+ return TorrentFile::null;
+
+ return files.at(idx);
+ }
+
+ const TorrentFile & Torrent::getFile(Uint32 idx) const
+ {
+ if (idx >= files.size())
+ return TorrentFile::null;
+
+ return files.at(idx);
+ }
+
+ unsigned int Torrent::getNumTrackerURLs() const
+ {
+ Uint32 count = 0;
+ TrackerTier* tt = trackers;
+ while (tt)
+ {
+ count += tt->urls.count();
+ tt = tt->next;
+ }
+ return count;
+ }
+
+ void Torrent::calcChunkPos(Uint32 chunk,QValueList<Uint32> & file_list) const
+ {
+ file_list.clear();
+ if (chunk >= hash_pieces.size() || files.empty())
+ return;
+
+ for (Uint32 i = 0;i < files.count();i++)
+ {
+ const TorrentFile & f = files[i];
+ if (chunk >= f.getFirstChunk() && chunk <= f.getLastChunk() && f.getSize() != 0)
+ file_list.append(f.getIndex());
+ }
+ }
+
+ bool Torrent::isMultimedia() const
+ {
+ return IsMultimediaFile(this->getNameSuggestion());
+ }
+
+ void Torrent::updateFilePercentage(const BitSet & bs)
+ {
+ for (Uint32 i = 0;i < files.count();i++)
+ {
+ TorrentFile & f = files[i];
+ f.updateNumDownloadedChunks(bs);
+ }
+ }
+
+ void Torrent::updateFilePercentage(Uint32 chunk,const BitSet & bs)
+ {
+ QValueList<Uint32> cfiles;
+ calcChunkPos(chunk,cfiles);
+
+ QValueList<Uint32>::iterator i = cfiles.begin();
+ while (i != cfiles.end())
+ {
+ TorrentFile & f = getFile(*i);
+ f.updateNumDownloadedChunks(bs);
+ i++;
+ }
+ }
+
+ bool Torrent::checkPathForDirectoryTraversal(const QString & p)
+ {
+ QStringList sl = QStringList::split(bt::DirSeparator(),p);
+ return !sl.contains("..");
+ }
+}
diff --git a/libktorrent/torrent/torrent.h b/libktorrent/torrent/torrent.h
new file mode 100644
index 0000000..04c45cd
--- /dev/null
+++ b/libktorrent/torrent/torrent.h
@@ -0,0 +1,218 @@
+/***************************************************************************
+ * Copyright (C) 2005 by *
+ * Joris Guisson <joris.guisson@gmail.com> *
+ * Ivan Vasic <ivasic@gmail.com> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef BTTORRENT_H
+#define BTTORRENT_H
+
+#include <kurl.h>
+#include <qvaluevector.h>
+#include <util/sha1hash.h>
+#include <util/constants.h>
+#include <interfaces/torrentinterface.h>
+#include "globals.h"
+#include "peerid.h"
+#include "torrentfile.h"
+
+
+
+namespace bt
+{
+ class BNode;
+ class BValueNode;
+ class BDictNode;
+ class BListNode;
+
+
+ struct TrackerTier
+ {
+ KURL::List urls;
+ TrackerTier* next;
+
+ TrackerTier() : next(0)
+ {}
+
+ ~TrackerTier()
+ {
+ delete next;
+ }
+ };
+
+
+ /**
+ * @author Joris Guisson
+ * @brief Loads a .torrent file
+ *
+ * Loads a torrent file and calculates some miscelanious other data,
+ * like the info_hash and the peer_id.
+ */
+ class Torrent
+ {
+ public:
+ Torrent();
+ virtual ~Torrent();
+
+ /**
+ * Load a .torrent file.
+ * @param file The file
+ * @param verbose Wether to print information to the log
+ * @throw Error if something goes wrong
+ */
+ void load(const QString & file,bool verbose);
+
+ /**
+ * Load a .torrent file.
+ * @param data The data
+ * @param verbose Wether to print information to the log
+ * @throw Error if something goes wrong
+ */
+ void load(const QByteArray & data,bool verbose);
+
+ void debugPrintInfo();
+
+ /// Get the number of chunks.
+ Uint32 getNumChunks() const {return hash_pieces.size();}
+
+ /// Get the size of a chunk.
+ Uint64 getChunkSize() const {return piece_length;}
+
+ /// Get the info_hash.
+ const SHA1Hash & getInfoHash() const {return info_hash;}
+
+ /// Get our peer_id.
+ const PeerID & getPeerID() const {return peer_id;}
+
+ /// Get the file size in number of bytes.
+ Uint64 getFileLength() const {return file_length;}
+
+ /// Get the suggested name.
+ QString getNameSuggestion() const {return name_suggestion;}
+
+ /**
+ * Verify wether a hash matches the hash
+ * of a Chunk
+ * @param h The hash
+ * @param index The index of the chunk
+ * @return true if they match
+ */
+ bool verifyHash(const SHA1Hash & h,Uint32 index);
+
+ /// Get the number of tracker URL's
+ unsigned int getNumTrackerURLs() const;
+
+ /**
+ * Get the hash of a Chunk. Throws an Error
+ * if idx is out of bounds.
+ * @param idx Index of Chunk
+ * @return The SHA1 hash of the chunk
+ */
+ const SHA1Hash & getHash(Uint32 idx) const;
+
+ /// See if we have a multi file torrent.
+ bool isMultiFile() const {return files.count() > 0;}
+
+ /// Get the number of files in a multi file torrent.
+ /// If we have a single file torrent, this will return 0.
+ Uint32 getNumFiles() const {return files.count();}
+
+ /**
+ * Get a TorrentFile. If the index is out of range, or
+ * we have a single file torrent we return a null TorrentFile.
+ * @param idx Index of the file
+ * @param A reference to the file
+ */
+ TorrentFile & getFile(Uint32 idx);
+
+ /**
+ * Get a TorrentFile. If the index is out of range, or
+ * we have a single file torrent we return a null TorrentFile.
+ * @param idx Index of the file
+ * @param A reference to the file
+ */
+ const TorrentFile & getFile(Uint32 idx) const;
+
+ /**
+ * Calculate in which file(s) a Chunk lies. A list will
+ * get filled with the indices of all the files. The list gets cleared at
+ * the beginning. If something is wrong only the list will
+ * get cleared.
+ * @param chunk The index of the chunk
+ * @param file_list This list will be filled with all the indices
+ */
+ void calcChunkPos(Uint32 chunk,QValueList<Uint32> & file_list) const;
+
+ /**
+ * Checks if torrent file is audio or video.
+ **/
+ bool isMultimedia() const;
+
+ /// See if the torrent is private
+ bool isPrivate() const {return priv_torrent;}
+
+ ///Gets a pointer to AnnounceList
+ const TrackerTier* getTrackerList() const { return trackers; }
+
+ /// Get the number of initial DHT nodes
+ Uint32 getNumDHTNodes() const {return nodes.count();}
+
+ /// Get a DHT node
+ const kt::DHTNode & getDHTNode(Uint32 i) {return nodes[i];}
+
+ /**
+ * Update the percentage of all files.
+ * @param bs The BitSet with all downloaded chunks
+ */
+ void updateFilePercentage(const BitSet & bs);
+
+ /**
+ * Update the percentage of a all files which have a particular chunk.
+ * @param bs The BitSet with all downloaded chunks
+ */
+ void updateFilePercentage(Uint32 chunk,const BitSet & bs);
+
+
+ private:
+ void loadInfo(BDictNode* node);
+ void loadTrackerURL(BValueNode* node);
+ void loadPieceLength(BValueNode* node);
+ void loadFileLength(BValueNode* node);
+ void loadHash(BValueNode* node);
+ void loadName(BValueNode* node);
+ void loadFiles(BListNode* node);
+ void loadNodes(BListNode* node);
+ void loadAnnounceList(BNode* node);
+ bool checkPathForDirectoryTraversal(const QString & p);
+
+ private:
+ TrackerTier* trackers;
+ QString name_suggestion;
+ Uint64 piece_length;
+ Uint64 file_length;
+ SHA1Hash info_hash;
+ PeerID peer_id;
+ QValueVector<SHA1Hash> hash_pieces;
+ QValueVector<TorrentFile> files;
+ QValueVector<kt::DHTNode> nodes;
+ QString encoding;
+ bool priv_torrent;
+ };
+
+}
+
+#endif
diff --git a/libktorrent/torrent/torrentcontrol.cpp b/libktorrent/torrent/torrentcontrol.cpp
new file mode 100644
index 0000000..71b4e64
--- /dev/null
+++ b/libktorrent/torrent/torrentcontrol.cpp
@@ -0,0 +1,1770 @@
+/***************************************************************************
+ * Copyright (C) 2005 by *
+ * Joris Guisson <joris.guisson@gmail.com> *
+ * Ivan Vasic <ivasic@gmail.com> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <qdir.h>
+#include <qfile.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kfiledialog.h>
+#include <qtextstream.h>
+#include <util/log.h>
+#include <util/error.h>
+#include <util/bitset.h>
+#include <util/functions.h>
+#include <util/fileops.h>
+#include <util/waitjob.h>
+#include <interfaces/functions.h>
+#include <interfaces/trackerslist.h>
+#include <datachecker/singledatachecker.h>
+#include <datachecker/multidatachecker.h>
+#include <datachecker/datacheckerlistener.h>
+#include <datachecker/datacheckerthread.h>
+#include <migrate/ccmigrate.h>
+#include <migrate/cachemigrate.h>
+#include <kademlia/dhtbase.h>
+
+#include "downloader.h"
+#include "uploader.h"
+#include "peersourcemanager.h"
+#include "chunkmanager.h"
+#include "torrent.h"
+#include "peermanager.h"
+
+#include "torrentfile.h"
+#include "torrentcontrol.h"
+
+#include "peer.h"
+#include "choker.h"
+
+#include "globals.h"
+#include "server.h"
+#include "packetwriter.h"
+#include "httptracker.h"
+#include "udptracker.h"
+#include "downloadcap.h"
+#include "uploadcap.h"
+#include "queuemanager.h"
+#include "statsfile.h"
+#include "announcelist.h"
+#include "preallocationthread.h"
+#include "timeestimator.h"
+#include "settings.h"
+
+#include <net/socketmonitor.h>
+
+
+using namespace kt;
+
+namespace bt
+{
+
+
+
+ TorrentControl::TorrentControl()
+ : tor(0),psman(0),cman(0),pman(0),down(0),up(0),choke(0),tmon(0),prealloc(false)
+ {
+ istats.last_announce = 0;
+ stats.imported_bytes = 0;
+ stats.trk_bytes_downloaded = 0;
+ stats.trk_bytes_uploaded = 0;
+ stats.running = false;
+ stats.started = false;
+ stats.stopped_by_error = false;
+ stats.session_bytes_downloaded = 0;
+ stats.session_bytes_uploaded = 0;
+ istats.session_bytes_uploaded = 0;
+ old_datadir = QString::null;
+ stats.status = NOT_STARTED;
+ stats.autostart = true;
+ stats.user_controlled = false;
+ stats.priv_torrent = false;
+ stats.seeders_connected_to = stats.seeders_total = 0;
+ stats.leechers_connected_to = stats.leechers_total = 0;
+ istats.running_time_dl = istats.running_time_ul = 0;
+ istats.prev_bytes_dl = 0;
+ istats.prev_bytes_ul = 0;
+ istats.trk_prev_bytes_dl = istats.trk_prev_bytes_ul = 0;
+ istats.io_error = false;
+ istats.priority = 0;
+ stats.max_share_ratio = 0.00f;
+ istats.custom_output_name = false;
+ istats.diskspace_warning_emitted = false;
+ stats.max_seed_time = 0;
+ updateStats();
+ prealoc_thread = 0;
+ dcheck_thread = 0;
+ istats.dht_on = false;
+ stats.num_corrupted_chunks = 0;
+
+ m_eta = new TimeEstimator(this);
+ // by default no torrent limits
+ upload_gid = download_gid = 0;
+ upload_limit = download_limit = 0;
+ moving_files = false;
+ }
+
+
+
+
+ TorrentControl::~TorrentControl()
+ {
+ if (stats.running)
+ stop(false);
+
+ if (tmon)
+ tmon->destroyed();
+ delete choke;
+ delete down;
+ delete up;
+ delete cman;
+ delete pman;
+ delete psman;
+ delete tor;
+ delete m_eta;
+ }
+
+ void TorrentControl::update()
+ {
+ UpdateCurrentTime();
+ if (stats.status == kt::CHECKING_DATA || moving_files)
+ return;
+
+ if (istats.io_error)
+ {
+ stop(false);
+ emit stoppedByError(this, error_msg);
+ return;
+ }
+
+ if (prealoc_thread)
+ {
+ if (prealoc_thread->isDone())
+ {
+ // thread done
+ if (prealoc_thread->errorHappened())
+ {
+ // upon error just call onIOError and return
+ onIOError(prealoc_thread->errorMessage());
+ delete prealoc_thread;
+ prealoc_thread = 0;
+ prealloc = true; // still need to do preallocation
+ return;
+ }
+ else
+ {
+ // continue the startup of the torrent
+ delete prealoc_thread;
+ prealoc_thread = 0;
+ prealloc = false;
+ stats.status = kt::NOT_STARTED;
+ saveStats();
+ continueStart();
+ }
+ }
+ else
+ return; // preallocation still going on, so just return
+ }
+
+
+ try
+ {
+ // first update peermanager
+ pman->update();
+ bool comp = stats.completed;
+
+ //helper var, check if needed to move completed files somewhere
+ bool moveCompleted = false;
+
+ // then the downloader and uploader
+ up->update(choke->getOptimisticlyUnchokedPeerID());
+ down->update();
+
+ stats.completed = cman->completed();
+ if (stats.completed && !comp)
+ {
+ pman->killSeeders();
+ QDateTime now = QDateTime::currentDateTime();
+ istats.running_time_dl += istats.time_started_dl.secsTo(now);
+ updateStatusMsg();
+ updateStats();
+
+ // download has just been completed
+ // only sent completed to tracker when we have all chunks (so no excluded chunks)
+ if (cman->haveAllChunks())
+ psman->completed();
+
+ finished(this);
+
+ //Move completed download to specified directory if needed
+ if(Settings::useCompletedDir())
+ {
+ moveCompleted = true;
+ }
+ }
+ else if (!stats.completed && comp)
+ {
+ // restart download if necesarry
+ // when user selects that files which were previously excluded,
+ // should now be downloaded
+ if (!psman->isStarted())
+ psman->start();
+ else
+ psman->manualUpdate();
+ istats.last_announce = bt::GetCurrentTime();
+ istats.time_started_dl = QDateTime::currentDateTime();
+ }
+ updateStatusMsg();
+
+ // get rid of dead Peers
+ Uint32 num_cleared = pman->clearDeadPeers();
+
+ // we may need to update the choker
+ if (choker_update_timer.getElapsedSinceUpdate() >= 10000 || num_cleared > 0)
+ {
+ // also get rid of seeders & uninterested when download is finished
+ // no need to keep them around, but also no need to do this
+ // every update, so once every 10 seconds is fine
+ if (stats.completed)
+ {
+ pman->killSeeders();
+ }
+
+ doChoking();
+ choker_update_timer.update();
+ // a good opportunity to make sure we are not keeping to much in memory
+ cman->checkMemoryUsage();
+ }
+
+ // to satisfy people obsessed with their share ratio
+ if (stats_save_timer.getElapsedSinceUpdate() >= 5*60*1000)
+ {
+ saveStats();
+ stats_save_timer.update();
+ }
+
+ // Update DownloadCap
+ updateStats();
+
+ if (stats.download_rate > 0)
+ stalled_timer.update();
+
+ // do a manual update if we are stalled for more then 2 minutes
+ // we do not do this for private torrents
+ if (stalled_timer.getElapsedSinceUpdate() > 120000 && !stats.completed &&
+ !stats.priv_torrent)
+ {
+ Out(SYS_TRK|LOG_NOTICE) << "Stalled for too long, time to get some fresh blood" << endl;
+ psman->manualUpdate();
+ stalled_timer.update();
+ }
+
+ if(overMaxRatio() || overMaxSeedTime())
+ {
+ if(istats.priority!=0) //if it's queued make sure to dequeue it
+ {
+ setPriority(0);
+ stats.user_controlled = true;
+ }
+
+ stop(true);
+ emit seedingAutoStopped(this, overMaxRatio() ? kt::MAX_RATIO_REACHED : kt::MAX_SEED_TIME_REACHED);
+ }
+
+ //Update diskspace if needed (every 1 min)
+ if(!stats.completed && stats.running && bt::GetCurrentTime() - last_diskspace_check >= 60 * 1000)
+ {
+ checkDiskSpace(true);
+ }
+
+ //Move completed files if needed:
+ if (moveCompleted)
+ {
+ QString outdir = Settings::completedDir();
+ if(!outdir.endsWith(bt::DirSeparator()))
+ outdir += bt::DirSeparator();
+
+ changeOutputDir(outdir);
+ }
+ }
+ catch (Error & e)
+ {
+ onIOError(e.toString());
+ }
+ }
+
+ void TorrentControl::onIOError(const QString & msg)
+ {
+ Out(SYS_DIO|LOG_IMPORTANT) << "Error : " << msg << endl;
+ stats.stopped_by_error = true;
+ stats.status = ERROR;
+ error_msg = msg;
+ istats.io_error = true;
+ }
+
+ void TorrentControl::start()
+ {
+ // do not start running torrents
+ if (stats.running || stats.status == kt::ALLOCATING_DISKSPACE || moving_files)
+ return;
+
+ stats.stopped_by_error = false;
+ istats.diskspace_warning_emitted = false;
+ istats.io_error = false;
+ try
+ {
+ bool ret = true;
+ aboutToBeStarted(this,ret);
+ if (!ret)
+ return;
+ }
+ catch (Error & err)
+ {
+ // something went wrong when files were recreated, set error and rethrow
+ onIOError(err.toString());
+ return;
+ }
+
+ try
+ {
+ cman->start();
+ }
+ catch (Error & e)
+ {
+ onIOError(e.toString());
+ throw;
+ }
+
+ istats.time_started_ul = istats.time_started_dl = QDateTime::currentDateTime();
+ resetTrackerStats();
+
+ if (prealloc)
+ {
+ // only start preallocation if we are allowed by the settings
+ if (Settings::diskPrealloc() && !cman->haveAllChunks())
+ {
+ Out(SYS_GEN|LOG_NOTICE) << "Pre-allocating diskspace" << endl;
+ prealoc_thread = new PreallocationThread(cman);
+ stats.running = true;
+ stats.status = kt::ALLOCATING_DISKSPACE;
+ prealoc_thread->start();
+ return;
+ }
+ else
+ {
+ prealloc = false;
+ }
+ }
+
+ continueStart();
+ }
+
+ void TorrentControl::continueStart()
+ {
+ // continues start after the prealoc_thread has finished preallocation
+ pman->start();
+ pman->loadPeerList(datadir + "peer_list");
+ try
+ {
+ down->loadDownloads(datadir + "current_chunks");
+ }
+ catch (Error & e)
+ {
+ // print out warning in case of failure
+ // we can still continue the download
+ Out(SYS_GEN|LOG_NOTICE) << "Warning : " << e.toString() << endl;
+ }
+
+ loadStats();
+ stats.running = true;
+ stats.started = true;
+ stats.autostart = true;
+ choker_update_timer.update();
+ stats_save_timer.update();
+
+
+ stalled_timer.update();
+ psman->start();
+ istats.last_announce = bt::GetCurrentTime();
+ stalled_timer.update();
+ }
+
+
+ void TorrentControl::stop(bool user,WaitJob* wjob)
+ {
+ QDateTime now = QDateTime::currentDateTime();
+ if(!stats.completed)
+ istats.running_time_dl += istats.time_started_dl.secsTo(now);
+ istats.running_time_ul += istats.time_started_ul.secsTo(now);
+ istats.time_started_ul = istats.time_started_dl = now;
+
+ // stop preallocation thread if necesarry
+ if (prealoc_thread)
+ {
+ prealoc_thread->stop();
+ prealoc_thread->wait();
+
+ if (prealoc_thread->errorHappened() || prealoc_thread->isNotFinished())
+ {
+ delete prealoc_thread;
+ prealoc_thread = 0;
+ prealloc = true;
+ saveStats(); // save stats, so that we will start preallocating the next time
+ }
+ else
+ {
+ delete prealoc_thread;
+ prealoc_thread = 0;
+ prealloc = false;
+ }
+ }
+
+ if (stats.running)
+ {
+ psman->stop(wjob);
+
+ if (tmon)
+ tmon->stopped();
+
+ try
+ {
+ down->saveDownloads(datadir + "current_chunks");
+ }
+ catch (Error & e)
+ {
+ // print out warning in case of failure
+ // it doesn't corrupt the data, so just a couple of lost chunks
+ Out(SYS_GEN|LOG_NOTICE) << "Warning : " << e.toString() << endl;
+ }
+
+ down->clearDownloads();
+ if (user)
+ {
+ //make this torrent user controlled
+ setPriority(0);
+ stats.autostart = false;
+ }
+ }
+ pman->savePeerList(datadir + "peer_list");
+ pman->stop();
+ pman->closeAllConnections();
+ pman->clearDeadPeers();
+ cman->stop();
+
+ stats.running = false;
+ saveStats();
+ updateStatusMsg();
+ updateStats();
+ stats.trk_bytes_downloaded = 0;
+ stats.trk_bytes_uploaded = 0;
+
+ emit torrentStopped(this);
+ }
+
+ void TorrentControl::setMonitor(kt::MonitorInterface* tmo)
+ {
+ tmon = tmo;
+ down->setMonitor(tmon);
+ if (tmon)
+ {
+ for (Uint32 i = 0;i < pman->getNumConnectedPeers();i++)
+ tmon->peerAdded(pman->getPeer(i));
+ }
+ }
+
+
+ void TorrentControl::init(QueueManager* qman,
+ const QString & torrent,
+ const QString & tmpdir,
+ const QString & ddir,
+ const QString & default_save_dir)
+ {
+ // first load the torrent file
+ tor = new Torrent();
+ try
+ {
+ tor->load(torrent,false);
+ }
+ catch (...)
+ {
+ delete tor;
+ tor = 0;
+ throw Error(i18n("An error occurred while loading the torrent."
+ " The torrent is probably corrupt or is not a torrent file.\n%1").arg(torrent));
+ }
+
+ initInternal(qman,tmpdir,ddir,default_save_dir,torrent.startsWith(tmpdir));
+
+ // copy torrent in tor dir
+ QString tor_copy = datadir + "torrent";
+ if (tor_copy != torrent)
+ {
+ bt::CopyFile(torrent,tor_copy);
+ }
+
+ }
+
+
+ void TorrentControl::init(QueueManager* qman, const QByteArray & data,const QString & tmpdir,
+ const QString & ddir,const QString & default_save_dir)
+ {
+ // first load the torrent file
+ tor = new Torrent();
+ try
+ {
+ tor->load(data,false);
+ }
+ catch (...)
+ {
+ delete tor;
+ tor = 0;
+ throw Error(i18n("An error occurred while loading the torrent."
+ " The torrent is probably corrupt or is not a torrent file."));
+ }
+
+ initInternal(qman,tmpdir,ddir,default_save_dir,true);
+ // copy data into torrent file
+ QString tor_copy = datadir + "torrent";
+ QFile fptr(tor_copy);
+ if (!fptr.open(IO_WriteOnly))
+ throw Error(i18n("Unable to create %1 : %2")
+ .arg(tor_copy).arg(fptr.errorString()));
+
+ fptr.writeBlock(data.data(),data.size());
+ }
+
+ void TorrentControl::checkExisting(QueueManager* qman)
+ {
+ // check if we haven't already loaded the torrent
+ // only do this when qman isn't 0
+ if (qman && qman->allreadyLoaded(tor->getInfoHash()))
+ {
+ if (!stats.priv_torrent)
+ {
+ qman->mergeAnnounceList(tor->getInfoHash(),tor->getTrackerList());
+
+ throw Error(i18n("You are already downloading this torrent %1, the list of trackers of both torrents has been merged.").arg(tor->getNameSuggestion()));
+ }
+ else
+ {
+ throw Error(i18n("You are already downloading the torrent %1")
+ .arg(tor->getNameSuggestion()));
+ }
+ }
+ }
+
+ void TorrentControl::setupDirs(const QString & tmpdir,const QString & ddir)
+ {
+ datadir = tmpdir;
+
+ if (!datadir.endsWith(DirSeparator()))
+ datadir += DirSeparator();
+
+ outputdir = ddir.stripWhiteSpace();
+ if (outputdir.length() > 0 && !outputdir.endsWith(DirSeparator()))
+ outputdir += DirSeparator();
+
+ if (!bt::Exists(datadir))
+ {
+ bt::MakeDir(datadir);
+ }
+ }
+
+ void TorrentControl::setupStats()
+ {
+ stats.completed = false;
+ stats.running = false;
+ stats.torrent_name = tor->getNameSuggestion();
+ stats.multi_file_torrent = tor->isMultiFile();
+ stats.total_bytes = tor->getFileLength();
+ stats.priv_torrent = tor->isPrivate();
+
+ // check the stats file for the custom_output_name variable
+ StatsFile st(datadir + "stats");
+ if (st.hasKey("CUSTOM_OUTPUT_NAME") && st.readULong("CUSTOM_OUTPUT_NAME") == 1)
+ {
+ istats.custom_output_name = true;
+ }
+
+ // load outputdir if outputdir is null
+ if (outputdir.isNull() || outputdir.length() == 0)
+ loadOutputDir();
+ }
+
+ void TorrentControl::setupData(const QString & ddir)
+ {
+ // create PeerManager and Tracker
+ pman = new PeerManager(*tor);
+ //Out() << "Tracker url " << url << " " << url.protocol() << " " << url.prettyURL() << endl;
+ psman = new PeerSourceManager(this,pman);
+ connect(psman,SIGNAL(statusChanged( const QString& )),
+ this,SLOT(trackerStatusChanged( const QString& )));
+
+
+ // Create chunkmanager, load the index file if it exists
+ // else create all the necesarry files
+ cman = new ChunkManager(*tor,datadir,outputdir,istats.custom_output_name);
+ // outputdir is null, see if the cache has figured out what it is
+ if (outputdir.length() == 0)
+ outputdir = cman->getDataDir();
+
+ // store the outputdir into the output_path variable, so others can access it
+
+ connect(cman,SIGNAL(updateStats()),this,SLOT(updateStats()));
+ if (bt::Exists(datadir + "index"))
+ cman->loadIndexFile();
+
+ stats.completed = cman->completed();
+
+ // create downloader,uploader and choker
+ down = new Downloader(*tor,*pman,*cman);
+ connect(down,SIGNAL(ioError(const QString& )),
+ this,SLOT(onIOError(const QString& )));
+ up = new Uploader(*cman,*pman);
+ choke = new Choker(*pman,*cman);
+
+
+ connect(pman,SIGNAL(newPeer(Peer* )),this,SLOT(onNewPeer(Peer* )));
+ connect(pman,SIGNAL(peerKilled(Peer* )),this,SLOT(onPeerRemoved(Peer* )));
+ connect(cman,SIGNAL(excluded(Uint32, Uint32 )),down,SLOT(onExcluded(Uint32, Uint32 )));
+ connect(cman,SIGNAL(included( Uint32, Uint32 )),down,SLOT(onIncluded( Uint32, Uint32 )));
+ connect(cman,SIGNAL(corrupted( Uint32 )),this,SLOT(corrupted( Uint32 )));
+ }
+
+ void TorrentControl::initInternal(QueueManager* qman,
+ const QString & tmpdir,
+ const QString & ddir,
+ const QString & default_save_dir,
+ bool first_time)
+ {
+ checkExisting(qman);
+ setupDirs(tmpdir,ddir);
+ setupStats();
+
+ if (!first_time)
+ {
+ // if we do not need to copy the torrent, it is an existing download and we need to see
+ // if it is not an old download
+ try
+ {
+ migrateTorrent(default_save_dir);
+ }
+ catch (Error & err)
+ {
+
+ throw Error(
+ i18n("Cannot migrate %1 : %2")
+ .arg(tor->getNameSuggestion()).arg(err.toString()));
+ }
+ }
+ setupData(ddir);
+
+ updateStatusMsg();
+
+ // to get rid of phantom bytes we need to take into account
+ // the data from downloads already in progress
+ try
+ {
+ Uint64 db = down->bytesDownloaded();
+ Uint64 cb = down->getDownloadedBytesOfCurrentChunksFile(datadir + "current_chunks");
+ istats.prev_bytes_dl = db + cb;
+
+ // Out() << "Downloaded : " << kt::BytesToString(db) << endl;
+ // Out() << "current_chunks : " << kt::BytesToString(cb) << endl;
+ }
+ catch (Error & e)
+ {
+ // print out warning in case of failure
+ Out() << "Warning : " << e.toString() << endl;
+ istats.prev_bytes_dl = down->bytesDownloaded();
+ }
+
+ loadStats();
+ updateStats();
+ saveStats();
+ stats.output_path = cman->getOutputPath();
+ /* if (stats.output_path.isNull())
+ {
+ cman->createFiles();
+ stats.output_path = cman->getOutputPath();
+ }*/
+ Out() << "OutputPath = " << stats.output_path << endl;
+ }
+
+
+
+ bool TorrentControl::announceAllowed()
+ {
+ if(istats.last_announce == 0)
+ return true;
+
+ if (psman && psman->getNumFailures() == 0)
+ return bt::GetCurrentTime() - istats.last_announce >= 60 * 1000;
+ else
+ return true;
+ }
+
+ void TorrentControl::updateTracker()
+ {
+ if (stats.running && announceAllowed())
+ {
+ psman->manualUpdate();
+ istats.last_announce = bt::GetCurrentTime();
+ }
+ }
+
+ void TorrentControl::onNewPeer(Peer* p)
+ {
+ connect(p,SIGNAL(gotPortPacket( const QString&, Uint16 )),
+ this,SLOT(onPortPacket( const QString&, Uint16 )));
+
+ if (p->getStats().fast_extensions)
+ {
+ const BitSet & bs = cman->getBitSet();
+ if (bs.allOn())
+ p->getPacketWriter().sendHaveAll();
+ else if (bs.numOnBits() == 0)
+ p->getPacketWriter().sendHaveNone();
+ else
+ p->getPacketWriter().sendBitSet(bs);
+ }
+ else
+ {
+ p->getPacketWriter().sendBitSet(cman->getBitSet());
+ }
+
+ if (!stats.completed)
+ p->getPacketWriter().sendInterested();
+
+ if (!stats.priv_torrent)
+ {
+ if (p->isDHTSupported())
+ p->getPacketWriter().sendPort(Globals::instance().getDHT().getPort());
+ else
+ // WORKAROUND so we can contact µTorrent's DHT
+ // They do not properly support the standard and do not turn on
+ // the DHT bit in the handshake, so we just ping each peer by default.
+ p->emitPortPacket();
+ }
+
+ // set group ID's for traffic shaping
+ p->setGroupIDs(upload_gid,download_gid);
+
+ if (tmon)
+ tmon->peerAdded(p);
+ }
+
+ void TorrentControl::onPeerRemoved(Peer* p)
+ {
+ disconnect(p,SIGNAL(gotPortPacket( const QString&, Uint16 )),
+ this,SLOT(onPortPacket( const QString&, Uint16 )));
+ if (tmon)
+ tmon->peerRemoved(p);
+ }
+
+ void TorrentControl::doChoking()
+ {
+ choke->update(stats.completed,stats);
+ }
+
+ bool TorrentControl::changeDataDir(const QString & new_dir)
+ {
+ int pos = datadir.findRev(bt::DirSeparator(),-2);
+ if (pos == -1)
+ {
+ Out(SYS_GEN|LOG_DEBUG) << "Could not find torX part in " << datadir << endl;
+ return false;
+ }
+
+ QString ndatadir = new_dir + datadir.mid(pos + 1);
+
+ Out(SYS_GEN|LOG_DEBUG) << datadir << " -> " << ndatadir << endl;
+ try
+ {
+ bt::Move(datadir,ndatadir);
+ old_datadir = datadir;
+ datadir = ndatadir;
+ }
+ catch (Error & err)
+ {
+ Out(SYS_GEN|LOG_IMPORTANT) << "Could not move " << datadir << " to " << ndatadir << endl;
+ return false;
+ }
+
+ cman->changeDataDir(datadir);
+ return true;
+ }
+
+ bool TorrentControl::changeOutputDir(const QString & new_dir, bool moveFiles)
+ {
+ if (moving_files)
+ return false;
+
+ Out(SYS_GEN|LOG_NOTICE) << "Moving data for torrent " << stats.torrent_name << " to " << new_dir << endl;
+
+ restart_torrent_after_move_data_files = false;
+
+ //check if torrent is running and stop it before moving data
+ if(stats.running)
+ {
+ restart_torrent_after_move_data_files = true;
+ this->stop(false);
+ }
+
+ moving_files = true;
+ try
+ {
+ QString nd;
+ if (istats.custom_output_name)
+ {
+ int slash_pos = stats.output_path.findRev(bt::DirSeparator(),-2);
+ nd = new_dir + stats.output_path.mid(slash_pos + 1);
+ }
+ else
+ {
+ nd = new_dir + tor->getNameSuggestion();
+ }
+
+ if (stats.output_path != nd)
+ {
+ KIO::Job* j = 0;
+ if(moveFiles)
+ {
+ if (stats.multi_file_torrent)
+ j = cman->moveDataFiles(nd);
+ else
+ j = cman->moveDataFiles(new_dir);
+ }
+
+ move_data_files_destination_path = nd;
+ if (j)
+ {
+ connect(j,SIGNAL(result(KIO::Job*)),this,SLOT(moveDataFilesJobDone(KIO::Job*)));
+ return true;
+ }
+ else
+ {
+ moveDataFilesJobDone(0);
+ }
+ }
+ else
+ {
+ Out(SYS_GEN|LOG_NOTICE) << "Source is the same as destination, so doing nothing" << endl;
+ }
+ }
+ catch (Error& err)
+ {
+ Out(SYS_GEN|LOG_IMPORTANT) << "Could not move " << stats.output_path << " to " << new_dir << ". Exception: " << err.toString() << endl;
+ moving_files = false;
+ return false;
+ }
+
+ moving_files = false;
+ if (restart_torrent_after_move_data_files)
+ {
+ this->start();
+ }
+
+ return true;
+ }
+
+ void TorrentControl::moveDataFilesJobDone(KIO::Job* job)
+ {
+ if (job)
+ cman->moveDataFilesCompleted(job);
+
+ if (!job || (job && !job->error()))
+ {
+ cman->changeOutputPath(move_data_files_destination_path);
+ outputdir = stats.output_path = move_data_files_destination_path;
+ istats.custom_output_name = true;
+
+ saveStats();
+ Out(SYS_GEN|LOG_NOTICE) << "Data directory changed for torrent " << "'" << stats.torrent_name << "' to: " << move_data_files_destination_path << endl;
+ }
+ else if (job->error())
+ {
+ Out(SYS_GEN|LOG_IMPORTANT) << "Could not move " << stats.output_path << " to " << move_data_files_destination_path << endl;
+ }
+
+ moving_files = false;
+ if (restart_torrent_after_move_data_files)
+ {
+ this->start();
+ }
+ }
+
+
+ void TorrentControl::rollback()
+ {
+ try
+ {
+ bt::Move(datadir,old_datadir);
+ datadir = old_datadir;
+ cman->changeDataDir(datadir);
+ }
+ catch (Error & err)
+ {
+ Out(SYS_GEN|LOG_IMPORTANT) << "Could not move " << datadir << " to " << old_datadir << endl;
+ }
+ }
+
+ void TorrentControl::updateStatusMsg()
+ {
+ if (stats.stopped_by_error)
+ stats.status = kt::ERROR;
+ else if (!stats.started)
+ stats.status = kt::NOT_STARTED;
+ else if(!stats.running && !stats.user_controlled)
+ stats.status = kt::QUEUED;
+ else if (!stats.running && stats.completed && (overMaxRatio() || overMaxSeedTime()))
+ stats.status = kt::SEEDING_COMPLETE;
+ else if (!stats.running && stats.completed)
+ stats.status = kt::DOWNLOAD_COMPLETE;
+ else if (!stats.running)
+ stats.status = kt::STOPPED;
+ else if (stats.running && stats.completed)
+ stats.status = kt::SEEDING;
+ else if (stats.running)
+ // protocol messages are also included in speed calculation, so lets not compare with 0
+ stats.status = down->downloadRate() > 100 ?
+ kt::DOWNLOADING : kt::STALLED;
+ }
+
+ const BitSet & TorrentControl::downloadedChunksBitSet() const
+ {
+ if (cman)
+ return cman->getBitSet();
+ else
+ return BitSet::null;
+ }
+
+ const BitSet & TorrentControl::availableChunksBitSet() const
+ {
+ if (!pman)
+ return BitSet::null;
+ else
+ return pman->getAvailableChunksBitSet();
+ }
+
+ const BitSet & TorrentControl::excludedChunksBitSet() const
+ {
+ if (!cman)
+ return BitSet::null;
+ else
+ return cman->getExcludedBitSet();
+ }
+
+ const BitSet & TorrentControl::onlySeedChunksBitSet() const
+ {
+ if (!cman)
+ return BitSet::null;
+ else
+ return cman->getOnlySeedBitSet();
+ }
+
+ void TorrentControl::saveStats()
+ {
+ StatsFile st(datadir + "stats");
+
+ st.write("OUTPUTDIR", cman->getDataDir());
+
+ if (cman->getDataDir() != outputdir)
+ outputdir = cman->getDataDir();
+
+ st.write("UPLOADED", QString::number(up->bytesUploaded()));
+
+ if (stats.running)
+ {
+ QDateTime now = QDateTime::currentDateTime();
+ st.write("RUNNING_TIME_DL",QString("%1").arg(istats.running_time_dl + istats.time_started_dl.secsTo(now)));
+ st.write("RUNNING_TIME_UL",QString("%1").arg(istats.running_time_ul + istats.time_started_ul.secsTo(now)));
+ }
+ else
+ {
+ st.write("RUNNING_TIME_DL", QString("%1").arg(istats.running_time_dl));
+ st.write("RUNNING_TIME_UL", QString("%1").arg(istats.running_time_ul));
+ }
+
+ st.write("PRIORITY", QString("%1").arg(istats.priority));
+ st.write("AUTOSTART", QString("%1").arg(stats.autostart));
+ st.write("IMPORTED", QString("%1").arg(stats.imported_bytes));
+ st.write("CUSTOM_OUTPUT_NAME",istats.custom_output_name ? "1" : "0");
+ st.write("MAX_RATIO", QString("%1").arg(stats.max_share_ratio,0,'f',2));
+ st.write("MAX_SEED_TIME",QString::number(stats.max_seed_time));
+ st.write("RESTART_DISK_PREALLOCATION",prealloc ? "1" : "0");
+
+ if(!stats.priv_torrent)
+ {
+ //save dht and pex
+ st.write("DHT", isFeatureEnabled(kt::DHT_FEATURE) ? "1" : "0");
+ st.write("UT_PEX", isFeatureEnabled(kt::UT_PEX_FEATURE) ? "1" : "0");
+ }
+
+ st.write("UPLOAD_LIMIT",QString::number(upload_limit));
+ st.write("DOWNLOAD_LIMIT",QString::number(download_limit));
+
+ st.writeSync();
+ }
+
+ void TorrentControl::loadStats()
+ {
+ StatsFile st(datadir + "stats");
+
+ Uint64 val = st.readUint64("UPLOADED");
+ // stats.session_bytes_uploaded will be calculated based upon prev_bytes_ul
+ // seeing that this will change here, we need to save it
+ istats.session_bytes_uploaded = stats.session_bytes_uploaded;
+ istats.prev_bytes_ul = val;
+ up->setBytesUploaded(val);
+
+ this->istats.running_time_dl = st.readULong("RUNNING_TIME_DL");
+ this->istats.running_time_ul = st.readULong("RUNNING_TIME_UL");
+ outputdir = st.readString("OUTPUTDIR").stripWhiteSpace();
+ if (st.hasKey("CUSTOM_OUTPUT_NAME") && st.readULong("CUSTOM_OUTPUT_NAME") == 1)
+ {
+ istats.custom_output_name = true;
+ }
+
+ setPriority(st.readInt("PRIORITY"));
+ stats.user_controlled = istats.priority == 0 ? true : false;
+ stats.autostart = st.readBoolean("AUTOSTART");
+
+ stats.imported_bytes = st.readUint64("IMPORTED");
+ float rat = st.readFloat("MAX_RATIO");
+ stats.max_share_ratio = rat;
+ if (st.hasKey("RESTART_DISK_PREALLOCATION"))
+ prealloc = st.readString("RESTART_DISK_PREALLOCATION") == "1";
+
+ stats.max_seed_time = st.readFloat("MAX_SEED_TIME");
+
+ if (!stats.priv_torrent)
+ {
+ if(st.hasKey("DHT"))
+ istats.dht_on = st.readBoolean("DHT");
+ else
+ istats.dht_on = true;
+
+ setFeatureEnabled(kt::DHT_FEATURE,istats.dht_on);
+ if (st.hasKey("UT_PEX"))
+ setFeatureEnabled(kt::UT_PEX_FEATURE,st.readBoolean("UT_PEX"));
+ }
+
+ net::SocketMonitor & smon = net::SocketMonitor::instance();
+
+ Uint32 nl = st.readInt("UPLOAD_LIMIT");
+ if (nl != upload_limit)
+ {
+ if (nl > 0)
+ {
+ if (upload_gid)
+ smon.setGroupLimit(net::SocketMonitor::UPLOAD_GROUP,upload_gid,nl);
+ else
+ upload_gid = smon.newGroup(net::SocketMonitor::UPLOAD_GROUP,nl);
+ }
+ else
+ {
+ smon.removeGroup(net::SocketMonitor::UPLOAD_GROUP,upload_gid);
+ upload_gid = 0;
+ }
+ }
+ upload_limit = nl;
+
+ nl = st.readInt("DOWNLOAD_LIMIT");
+ if (nl != download_limit)
+ {
+ if (nl > 0)
+ {
+ if (download_gid)
+ smon.setGroupLimit(net::SocketMonitor::DOWNLOAD_GROUP,download_gid,nl);
+ else
+ download_gid = smon.newGroup(net::SocketMonitor::DOWNLOAD_GROUP,nl);
+ }
+ else
+ {
+ smon.removeGroup(net::SocketMonitor::DOWNLOAD_GROUP,download_gid);
+ download_gid = 0;
+ }
+ }
+ download_limit = nl;
+ }
+
+ void TorrentControl::loadOutputDir()
+ {
+ StatsFile st(datadir + "stats");
+ if (!st.hasKey("OUTPUTDIR"))
+ return;
+
+ outputdir = st.readString("OUTPUTDIR").stripWhiteSpace();
+ if (st.hasKey("CUSTOM_OUTPUT_NAME") && st.readULong("CUSTOM_OUTPUT_NAME") == 1)
+ {
+ istats.custom_output_name = true;
+ }
+ }
+
+ bool TorrentControl::readyForPreview(int start_chunk, int end_chunk)
+ {
+ if ( !tor->isMultimedia() && !tor->isMultiFile()) return false;
+
+ const BitSet & bs = downloadedChunksBitSet();
+ for(int i = start_chunk; i<end_chunk; ++i)
+ {
+ if ( !bs.get(i) ) return false;
+ }
+ return true;
+ }
+
+ Uint32 TorrentControl::getTimeToNextTrackerUpdate() const
+ {
+ if (psman)
+ return psman->getTimeToNextUpdate();
+ else
+ return 0;
+ }
+
+ void TorrentControl::updateStats()
+ {
+ stats.num_chunks_downloading = down ? down->numActiveDownloads() : 0;
+ stats.num_peers = pman ? pman->getNumConnectedPeers() : 0;
+ stats.upload_rate = up && stats.running ? up->uploadRate() : 0;
+ stats.download_rate = down && stats.running ? down->downloadRate() : 0;
+ stats.bytes_left = cman ? cman->bytesLeft() : 0;
+ stats.bytes_left_to_download = cman ? cman->bytesLeftToDownload() : 0;
+ stats.bytes_uploaded = up ? up->bytesUploaded() : 0;
+ stats.bytes_downloaded = down ? down->bytesDownloaded() : 0;
+ stats.total_chunks = tor ? tor->getNumChunks() : 0;
+ stats.num_chunks_downloaded = cman ? cman->chunksDownloaded() : 0;
+ stats.num_chunks_excluded = cman ? cman->chunksExcluded() : 0;
+ stats.chunk_size = tor ? tor->getChunkSize() : 0;
+ stats.num_chunks_left = cman ? cman->chunksLeft() : 0;
+ stats.total_bytes_to_download = (tor && cman) ? tor->getFileLength() - cman->bytesExcluded() : 0;
+
+ if (stats.bytes_downloaded >= istats.prev_bytes_dl)
+ stats.session_bytes_downloaded = stats.bytes_downloaded - istats.prev_bytes_dl;
+ else
+ stats.session_bytes_downloaded = 0;
+
+ if (stats.bytes_uploaded >= istats.prev_bytes_ul)
+ stats.session_bytes_uploaded = (stats.bytes_uploaded - istats.prev_bytes_ul) + istats.session_bytes_uploaded;
+ else
+ stats.session_bytes_uploaded = istats.session_bytes_uploaded;
+ /*
+ Safety check, it is possible that stats.bytes_downloaded gets subtracted in Downloader.
+ Which can cause stats.bytes_downloaded to be smaller the istats.trk_prev_bytes_dl.
+ This can screw up your download ratio.
+ */
+ if (stats.bytes_downloaded >= istats.trk_prev_bytes_dl)
+ stats.trk_bytes_downloaded = stats.bytes_downloaded - istats.trk_prev_bytes_dl;
+ else
+ stats.trk_bytes_downloaded = 0;
+
+ if (stats.bytes_uploaded >= istats.trk_prev_bytes_ul)
+ stats.trk_bytes_uploaded = stats.bytes_uploaded - istats.trk_prev_bytes_ul;
+ else
+ stats.trk_bytes_uploaded = 0;
+
+ getSeederInfo(stats.seeders_total,stats.seeders_connected_to);
+ getLeecherInfo(stats.leechers_total,stats.leechers_connected_to);
+ }
+
+ void TorrentControl::getSeederInfo(Uint32 & total,Uint32 & connected_to) const
+ {
+ total = 0;
+ connected_to = 0;
+ if (!pman || !psman)
+ return;
+
+ for (Uint32 i = 0;i < pman->getNumConnectedPeers();i++)
+ {
+ if (pman->getPeer(i)->isSeeder())
+ connected_to++;
+ }
+ total = psman->getNumSeeders();
+ if (total == 0)
+ total = connected_to;
+ }
+
+ void TorrentControl::getLeecherInfo(Uint32 & total,Uint32 & connected_to) const
+ {
+ total = 0;
+ connected_to = 0;
+ if (!pman || !psman)
+ return;
+
+ for (Uint32 i = 0;i < pman->getNumConnectedPeers();i++)
+ {
+ if (!pman->getPeer(i)->isSeeder())
+ connected_to++;
+ }
+ total = psman->getNumLeechers();
+ if (total == 0)
+ total = connected_to;
+ }
+
+ Uint32 TorrentControl::getRunningTimeDL() const
+ {
+ if (!stats.running || stats.completed)
+ return istats.running_time_dl;
+ else
+ return istats.running_time_dl + istats.time_started_dl.secsTo(QDateTime::currentDateTime());
+ }
+
+ Uint32 TorrentControl::getRunningTimeUL() const
+ {
+ if (!stats.running)
+ return istats.running_time_ul;
+ else
+ return istats.running_time_ul + istats.time_started_ul.secsTo(QDateTime::currentDateTime());
+ }
+
+ Uint32 TorrentControl::getNumFiles() const
+ {
+ if (tor && tor->isMultiFile())
+ return tor->getNumFiles();
+ else
+ return 0;
+ }
+
+ TorrentFileInterface & TorrentControl::getTorrentFile(Uint32 index)
+ {
+ if (tor)
+ return tor->getFile(index);
+ else
+ return TorrentFile::null;
+ }
+
+ void TorrentControl::migrateTorrent(const QString & default_save_dir)
+ {
+ if (bt::Exists(datadir + "current_chunks") && bt::IsPreMMap(datadir + "current_chunks"))
+ {
+ // in case of error copy torX dir to migrate-failed-tor
+ QString dd = datadir;
+ int pos = dd.findRev("tor");
+ if (pos != - 1)
+ {
+ dd = dd.replace(pos,3,"migrate-failed-tor");
+ Out() << "Copying " << datadir << " to " << dd << endl;
+ bt::CopyDir(datadir,dd,true);
+ }
+
+ bt::MigrateCurrentChunks(*tor,datadir + "current_chunks");
+ if (outputdir.isNull() && bt::IsCacheMigrateNeeded(*tor,datadir + "cache"))
+ {
+ // if the output dir is NULL
+ if (default_save_dir.isNull())
+ {
+ KMessageBox::information(0,
+ i18n("The torrent %1 was started with a previous version of KTorrent."
+ " To make sure this torrent still works with this version of KTorrent, "
+ "we will migrate this torrent. You will be asked for a location to save "
+ "the torrent to. If you press cancel, we will select your home directory.")
+ .arg(tor->getNameSuggestion()));
+ outputdir = KFileDialog::getExistingDirectory(QString::null, 0,i18n("Select Folder to Save To"));
+ if (outputdir.isNull())
+ outputdir = QDir::homeDirPath();
+ }
+ else
+ {
+ outputdir = default_save_dir;
+ }
+
+ if (!outputdir.endsWith(bt::DirSeparator()))
+ outputdir += bt::DirSeparator();
+
+ bt::MigrateCache(*tor,datadir + "cache",outputdir);
+ }
+
+ // delete backup
+ if (pos != - 1)
+ bt::Delete(dd);
+ }
+ }
+
+ void TorrentControl::setPriority(int p)
+ {
+ istats.priority = p;
+ stats.user_controlled = p == 0 ? true : false;
+ if(p)
+ stats.status = kt::QUEUED;
+ else
+ updateStatusMsg();
+
+ saveStats();
+ }
+
+ void TorrentControl::setMaxShareRatio(float ratio)
+ {
+ if(ratio == 1.00f)
+ {
+ if (stats.max_share_ratio != ratio)
+ stats.max_share_ratio = ratio;
+ }
+ else
+ stats.max_share_ratio = ratio;
+
+ if(stats.completed && !stats.running && !stats.user_controlled && (kt::ShareRatio(stats) >= stats.max_share_ratio))
+ setPriority(0); //dequeue it
+
+ saveStats();
+ emit maxRatioChanged(this);
+ }
+
+ void TorrentControl::setMaxSeedTime(float hours)
+ {
+ stats.max_seed_time = hours;
+ saveStats();
+ }
+
+ bool TorrentControl::overMaxRatio()
+ {
+ if(stats.completed && stats.bytes_uploaded != 0 && stats.bytes_downloaded != 0 && stats.max_share_ratio > 0)
+ {
+ if(kt::ShareRatio(stats) >= stats.max_share_ratio)
+ return true;
+ }
+
+ return false;
+ }
+
+ bool TorrentControl::overMaxSeedTime()
+ {
+ if(stats.completed && stats.bytes_uploaded != 0 && stats.bytes_downloaded != 0 && stats.max_seed_time > 0)
+ {
+ Uint32 dl = getRunningTimeDL();
+ Uint32 ul = getRunningTimeUL();
+ if ((ul - dl) / 3600.0f > stats.max_seed_time)
+ return true;
+ }
+
+ return false;
+ }
+
+
+ QString TorrentControl::statusToString() const
+ {
+ switch (stats.status)
+ {
+ case kt::NOT_STARTED :
+ return i18n("Not started");
+ case kt::DOWNLOAD_COMPLETE :
+ return i18n("Download completed");
+ case kt::SEEDING_COMPLETE :
+ return i18n("Seeding completed");
+ case kt::SEEDING :
+ return i18n("Seeding");
+ case kt::DOWNLOADING:
+ return i18n("Downloading");
+ case kt::STALLED:
+ return i18n("Stalled");
+ case kt::STOPPED:
+ return i18n("Stopped");
+ case kt::ERROR :
+ return i18n("Error: ") + getShortErrorMessage();
+ case kt::ALLOCATING_DISKSPACE:
+ return i18n("Allocating diskspace");
+ case kt::QUEUED:
+ return i18n("Queued");
+ case kt::CHECKING_DATA:
+ return i18n("Checking data");
+ case kt::NO_SPACE_LEFT:
+ return i18n("Stopped. No space left on device.");
+ }
+ return QString::null;
+ }
+
+ TrackersList* TorrentControl::getTrackersList()
+ {
+ return psman;
+ }
+
+ const TrackersList* TorrentControl::getTrackersList() const
+ {
+ return psman;
+ }
+
+ void TorrentControl::onPortPacket(const QString & ip,Uint16 port)
+ {
+ if (Globals::instance().getDHT().isRunning() && !stats.priv_torrent)
+ Globals::instance().getDHT().portRecieved(ip,port);
+ }
+
+ void TorrentControl::startDataCheck(bt::DataCheckerListener* lst,bool auto_import)
+ {
+ if (stats.status == kt::ALLOCATING_DISKSPACE)
+ return;
+
+
+ DataChecker* dc = 0;
+ stats.status = kt::CHECKING_DATA;
+ stats.num_corrupted_chunks = 0; // reset the number of corrupted chunks found
+ if (stats.multi_file_torrent)
+ dc = new MultiDataChecker();
+ else
+ dc = new SingleDataChecker();
+
+ dc->setListener(lst);
+
+ dcheck_thread = new DataCheckerThread(dc,stats.output_path,*tor,datadir + "dnd" + bt::DirSeparator());
+
+ // dc->check(stats.output_path,*tor,datadir + "dnd" + bt::DirSeparator());
+ dcheck_thread->start();
+ }
+
+ void TorrentControl::afterDataCheck()
+ {
+ DataChecker* dc = dcheck_thread->getDataChecker();
+ DataCheckerListener* lst = dc->getListener();
+
+ bool err = !dcheck_thread->getError().isNull();
+ if (err)
+ {
+ // show a queued error message when an error has occurred
+ KMessageBox::queuedMessageBox(0,KMessageBox::Error,dcheck_thread->getError());
+ lst->stop();
+ }
+
+ if (lst && !lst->isStopped())
+ {
+ down->dataChecked(dc->getDownloaded());
+ // update chunk manager
+ cman->dataChecked(dc->getDownloaded());
+ if (lst->isAutoImport())
+ {
+ down->recalcDownloaded();
+ stats.imported_bytes = down->bytesDownloaded();
+ if (cman->haveAllChunks())
+ stats.completed = true;
+ }
+ else
+ {
+ Uint64 downloaded = stats.bytes_downloaded;
+ down->recalcDownloaded();
+ updateStats();
+ if (stats.bytes_downloaded > downloaded)
+ stats.imported_bytes = stats.bytes_downloaded - downloaded;
+
+ if (cman->haveAllChunks())
+ stats.completed = true;
+ }
+ }
+
+ stats.status = kt::NOT_STARTED;
+ // update the status
+ updateStatusMsg();
+ updateStats();
+ if (lst)
+ lst->finished();
+ delete dcheck_thread;
+ dcheck_thread = 0;
+ }
+
+ bool TorrentControl::isCheckingData(bool & finished) const
+ {
+ if (dcheck_thread)
+ {
+ finished = !dcheck_thread->isRunning();
+ return true;
+ }
+ return false;
+ }
+
+ bool TorrentControl::hasExistingFiles() const
+ {
+ return cman->hasExistingFiles();
+ }
+
+ bool TorrentControl::hasMissingFiles(QStringList & sl)
+ {
+ return cman->hasMissingFiles(sl);
+ }
+
+ void TorrentControl::recreateMissingFiles()
+ {
+ try
+ {
+ cman->recreateMissingFiles();
+ prealloc = true; // set prealloc to true so files will be truncated again
+ down->dataChecked(cman->getBitSet()); // update chunk selector
+ }
+ catch (Error & err)
+ {
+ onIOError(err.toString());
+ throw;
+ }
+ }
+
+ void TorrentControl::dndMissingFiles()
+ {
+ try
+ {
+ cman->dndMissingFiles();
+ prealloc = true; // set prealloc to true so files will be truncated again
+ missingFilesMarkedDND(this);
+ down->dataChecked(cman->getBitSet()); // update chunk selector
+ }
+ catch (Error & err)
+ {
+ onIOError(err.toString());
+ throw;
+ }
+ }
+
+ void TorrentControl::handleError(const QString & err)
+ {
+ onIOError(err);
+ }
+
+ Uint32 TorrentControl::getNumDHTNodes() const
+ {
+ return tor->getNumDHTNodes();
+ }
+
+ const kt::DHTNode & TorrentControl::getDHTNode(Uint32 i) const
+ {
+ return tor->getDHTNode(i);
+ }
+
+ void TorrentControl::deleteDataFiles()
+ {
+ cman->deleteDataFiles();
+ }
+
+ const bt::SHA1Hash & TorrentControl::getInfoHash() const
+ {
+ return tor->getInfoHash();
+ }
+
+ void TorrentControl::resetTrackerStats()
+ {
+ istats.trk_prev_bytes_dl = stats.bytes_downloaded,
+ istats.trk_prev_bytes_ul = stats.bytes_uploaded,
+ stats.trk_bytes_downloaded = 0;
+ stats.trk_bytes_uploaded = 0;
+ }
+
+ void TorrentControl::trackerStatusChanged(const QString & ns)
+ {
+ stats.trackerstatus = ns;
+ }
+
+ void TorrentControl::addPeerSource(kt::PeerSource* ps)
+ {
+ if (psman)
+ psman->addPeerSource(ps);
+ }
+
+ void TorrentControl::removePeerSource(kt::PeerSource* ps)
+ {
+ if (psman)
+ psman->removePeerSource(ps);
+ }
+
+ void TorrentControl::corrupted(Uint32 chunk)
+ {
+ // make sure we will redownload the chunk
+ down->corrupted(chunk);
+ if (stats.completed)
+ stats.completed = false;
+
+ // emit signal to show a systray message
+ stats.num_corrupted_chunks++;
+ corruptedDataFound(this);
+ }
+
+ Uint32 TorrentControl::getETA()
+ {
+ return m_eta->estimate();
+ }
+
+
+
+ const bt::PeerID & TorrentControl::getOwnPeerID() const
+ {
+ return tor->getPeerID();
+ }
+
+
+ bool TorrentControl::isFeatureEnabled(TorrentFeature tf)
+ {
+ switch (tf)
+ {
+ case kt::DHT_FEATURE:
+ return psman->dhtStarted();
+ case kt::UT_PEX_FEATURE:
+ return pman->isPexEnabled();
+ default:
+ return false;
+ }
+ }
+
+ void TorrentControl::setFeatureEnabled(TorrentFeature tf,bool on)
+ {
+ switch (tf)
+ {
+ case kt::DHT_FEATURE:
+ if (on)
+ {
+ if(!stats.priv_torrent)
+ {
+ psman->addDHT();
+ istats.dht_on = psman->dhtStarted();
+ saveStats();
+ }
+ }
+ else
+ {
+ psman->removeDHT();
+ istats.dht_on = false;
+ saveStats();
+ }
+ break;
+ case kt::UT_PEX_FEATURE:
+ if (on)
+ {
+ if (!stats.priv_torrent && !pman->isPexEnabled())
+ {
+ pman->setPexEnabled(true);
+ }
+ }
+ else
+ {
+ pman->setPexEnabled(false);
+ }
+ break;
+ }
+ }
+
+ void TorrentControl::createFiles()
+ {
+ cman->createFiles(true);
+ stats.output_path = cman->getOutputPath();
+ }
+
+ bool TorrentControl::checkDiskSpace(bool emit_sig)
+ {
+ last_diskspace_check = bt::GetCurrentTime();
+
+ //calculate free disk space
+ Uint64 bytes_free = 0;
+ if (FreeDiskSpace(getDataDir(),bytes_free))
+ {
+ Uint64 bytes_to_download = stats.total_bytes_to_download;
+ Uint64 downloaded = 0;
+ try
+ {
+ downloaded = cman->diskUsage();
+ }
+ catch (bt::Error & err)
+ {
+ Out(SYS_GEN|LOG_DEBUG) << "Error : " << err.toString() << endl;
+ }
+ Uint64 remaining = 0;
+ if (downloaded <= bytes_to_download)
+ remaining = bytes_to_download - downloaded;
+
+ if (remaining > bytes_free)
+ {
+ bool toStop = bytes_free < (Uint64) Settings::minDiskSpace() * 1024 * 1024;
+
+ // if we don't need to stop the torrent, only emit the signal once
+ // so that we do bother the user continously
+ if (emit_sig && (toStop || !istats.diskspace_warning_emitted))
+ {
+ emit diskSpaceLow(this, toStop);
+ istats.diskspace_warning_emitted = true;
+ }
+
+ if (!stats.running)
+ {
+ stats.status = NO_SPACE_LEFT;
+ }
+
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ void TorrentControl::setTrafficLimits(Uint32 up,Uint32 down)
+ {
+ net::SocketMonitor & smon = net::SocketMonitor::instance();
+ if (up && !upload_gid)
+ {
+ // create upload group
+ upload_gid = smon.newGroup(net::SocketMonitor::UPLOAD_GROUP,up);
+ upload_limit = up;
+ }
+ else if (up && upload_gid)
+ {
+ // change existing group limit
+ smon.setGroupLimit(net::SocketMonitor::UPLOAD_GROUP,upload_gid,up);
+ upload_limit = up;
+ }
+ else if (!up && !upload_gid)
+ {
+ upload_limit = up;
+ }
+ else // !up && upload_gid
+ {
+ // remove existing group
+ smon.removeGroup(net::SocketMonitor::UPLOAD_GROUP,upload_gid);
+ upload_gid = upload_limit = 0;
+ }
+
+ if (down && !download_gid)
+ {
+ // create download grodown
+ download_gid = smon.newGroup(net::SocketMonitor::DOWNLOAD_GROUP,down);
+ download_limit = down;
+ }
+ else if (down && download_gid)
+ {
+ // change existing grodown limit
+ smon.setGroupLimit(net::SocketMonitor::DOWNLOAD_GROUP,download_gid,down);
+ download_limit = down;
+ }
+ else if (!down && !download_gid)
+ {
+ download_limit = down;
+ }
+ else // !down && download_gid
+ {
+ // remove existing grodown
+ smon.removeGroup(net::SocketMonitor::DOWNLOAD_GROUP,download_gid);
+ download_gid = download_limit = 0;
+ }
+
+ saveStats();
+ pman->setGroupIDs(upload_gid,download_gid);
+ }
+
+ void TorrentControl::getTrafficLimits(Uint32 & up,Uint32 & down)
+ {
+ up = upload_limit;
+ down = download_limit;
+ }
+
+ const PeerManager * TorrentControl::getPeerMgr() const
+ {
+ return pman;
+ }
+}
+
+#include "torrentcontrol.moc"
diff --git a/libktorrent/torrent/torrentcontrol.h b/libktorrent/torrent/torrentcontrol.h
new file mode 100644
index 0000000..33610de
--- /dev/null
+++ b/libktorrent/torrent/torrentcontrol.h
@@ -0,0 +1,394 @@
+/***************************************************************************
+ * Copyright (C) 2005 by *
+ * Joris Guisson <joris.guisson@gmail.com> *
+ * Ivan Vasic <ivasic@gmail.com> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef BTTORRENTCONTROL_H
+#define BTTORRENTCONTROL_H
+
+#include <qobject.h>
+#include <qcstring.h>
+#include <qtimer.h>
+#include <kurl.h>
+#include "globals.h"
+#include <util/timer.h>
+#include <interfaces/torrentinterface.h>
+#include <interfaces/monitorinterface.h>
+#include <interfaces/trackerslist.h>
+
+class QStringList;
+class QString;
+
+namespace KIO
+{
+ class Job;
+}
+
+
+namespace bt
+{
+ class Choker;
+ class Torrent;
+ class PeerSourceManager;
+ class ChunkManager;
+ class PeerManager;
+ class Downloader;
+ class Uploader;
+ class Peer;
+ class BitSet;
+ class QueueManager;
+ class PreallocationThread;
+ class TimeEstimator;
+ class DataCheckerThread;
+ class WaitJob;
+
+ /**
+ * @author Joris Guisson
+ * @brief Controls just about everything
+ *
+ * This is the interface which any user gets to deal with.
+ * This class controls the uploading, downloading, choking,
+ * updating the tracker and chunk management.
+ */
+ class TorrentControl : public kt::TorrentInterface
+ {
+ Q_OBJECT
+ public:
+ TorrentControl();
+ virtual ~TorrentControl();
+
+ /**
+ * Get a BitSet of the status of all Chunks
+ */
+ const BitSet & downloadedChunksBitSet() const;
+
+ /**
+ * Get a BitSet of the availability of all Chunks
+ */
+ const BitSet & availableChunksBitSet() const;
+
+ /**
+ * Get a BitSet of the excluded Chunks
+ */
+ const BitSet & excludedChunksBitSet() const;
+
+ /**
+ * Get a BitSet of the only seed chunks
+ */
+ const BitSet & onlySeedChunksBitSet() const;
+
+ /**
+ * Initialize the TorrentControl.
+ * @param qman The QueueManager
+ * @param torrent The filename of the torrent file
+ * @param tmpdir The directory to store temporary data
+ * @param datadir The directory to store the actual file(s)
+ * (only used the first time we load a torrent)
+ * @param default_save_dir Default save directory (null if not set)
+ * @throw Error when something goes wrong
+ */
+ void init(QueueManager* qman,
+ const QString & torrent,
+ const QString & tmpdir,
+ const QString & datadir,
+ const QString & default_save_dir);
+
+ /**
+ * Initialize the TorrentControl.
+ * @param qman The QueueManager
+ * @param data The data of the torrent
+ * @param tmpdir The directory to store temporary data
+ * @param datadir The directory to store the actual file(s)
+ * (only used the first time we load a torrent)
+ * @param default_save_dir Default save directory (null if not set)
+ * @throw Error when something goes wrong
+ */
+ void init(QueueManager* qman,
+ const QByteArray & data,
+ const QString & tmpdir,
+ const QString & datadir,
+ const QString & default_save_dir);
+
+ /**
+ * Change to a new data dir. If this fails
+ * we will fall back on the old directory.
+ * @param new_dir The new directory
+ * @return true upon succes
+ */
+ bool changeDataDir(const QString & new_dir);
+
+
+ /**
+ * Change torrents output directory. If this fails we will fall back on the old directory.
+ * @param new_dir The new directory
+ * @param moveFiles Wheather to actually move the files or just change the directory without moving them.
+ * @return true upon success.
+ */
+ bool changeOutputDir(const QString& new_dir, bool moveFiles = true);
+
+ /**
+ * Roll back the previous changeDataDir call.
+ * Does nothing if there was no previous changeDataDir call.
+ */
+ void rollback();
+
+ /// Gets the TrackersList interface
+ kt::TrackersList* getTrackersList();
+
+ /// Gets the TrackersList interface
+ const kt::TrackersList* getTrackersList() const;
+
+ /// Get the data directory of this torrent
+ QString getDataDir() const {return outputdir;}
+
+ /// Get the torX dir.
+ QString getTorDir() const {return datadir;}
+
+ /// Set the monitor
+ void setMonitor(kt::MonitorInterface* tmo);
+
+ /// Get the Torrent.
+ const Torrent & getTorrent() const {return *tor;}
+
+ /**
+ * Get the download running time of this torrent in seconds
+ * @return Uint32 - time in seconds
+ */
+ Uint32 getRunningTimeDL() const;
+
+ /**
+ * Get the upload running time of this torrent in seconds
+ * @return Uint32 - time in seconds
+ */
+ Uint32 getRunningTimeUL() const;
+
+ /**
+ * Checks if torrent is multimedial and chunks needed for preview are downloaded
+ * @param start_chunk The index of starting chunk to check
+ * @param end_chunk The index of the last chunk to check
+ * In case of single torrent file defaults can be used (0,1)
+ **/
+ bool readyForPreview(int start_chunk = 0, int end_chunk = 1);
+
+ /// Get the time to the next tracker update in seconds.
+ Uint32 getTimeToNextTrackerUpdate() const;
+
+ /// Get a short error message
+ QString getShortErrorMessage() const {return error_msg;}
+
+ virtual Uint32 getNumFiles() const;
+ virtual kt::TorrentFileInterface & getTorrentFile(Uint32 index);
+ virtual void recreateMissingFiles();
+ virtual void dndMissingFiles();
+ virtual void addPeerSource(kt::PeerSource* ps);
+ virtual void removePeerSource(kt::PeerSource* ps);
+
+ int getPriority() const { return istats.priority; }
+ void setPriority(int p);
+
+ virtual bool overMaxRatio();
+ virtual void setMaxShareRatio(float ratio);
+ virtual float getMaxShareRatio() const { return stats.max_share_ratio; }
+
+ virtual bool overMaxSeedTime();
+ virtual void setMaxSeedTime(float hours);
+ virtual float getMaxSeedTime() const {return stats.max_seed_time;}
+
+ /// Tell the TorrentControl obj to preallocate diskspace in the next update
+ void setPreallocateDiskSpace(bool pa) {prealloc = pa;}
+
+ /// Make a string out of the status message
+ virtual QString statusToString() const;
+
+ /// Checks if tracker announce is allowed (minimum interval 60 seconds)
+ bool announceAllowed();
+
+ void startDataCheck(bt::DataCheckerListener* lst,bool auto_import);
+
+ /// Test if the torrent has existing files, only works the first time a torrent is loaded
+ bool hasExistingFiles() const;
+
+ /**
+ * Test all files and see if they are not missing.
+ * If so put them in a list
+ */
+ bool hasMissingFiles(QStringList & sl);
+
+
+ virtual Uint32 getNumDHTNodes() const;
+ virtual const kt::DHTNode & getDHTNode(Uint32 i) const;
+ virtual void deleteDataFiles();
+ virtual const SHA1Hash & getInfoHash() const;
+ virtual const bt::PeerID & getOwnPeerID() const;
+
+ /**
+ * Called by the PeerSourceManager when it is going to start a new tracker.
+ */
+ void resetTrackerStats();
+
+ /**
+ * Returns estimated time left for finishing download. Returned value is in seconds.
+ * Uses TimeEstimator class to calculate this value.
+ */
+ Uint32 getETA();
+
+ /// Is a feature enabled
+ bool isFeatureEnabled(kt::TorrentFeature tf);
+
+ /// Disable or enable a feature
+ void setFeatureEnabled(kt::TorrentFeature tf,bool on);
+
+ /// Create all the necessary files
+ void createFiles();
+
+ ///Checks if diskspace is low
+ bool checkDiskSpace(bool emit_sig = true);
+
+ virtual void setTrafficLimits(Uint32 up,Uint32 down);
+ virtual void getTrafficLimits(Uint32 & up,Uint32 & down);
+
+ ///Get the PeerManager
+ const PeerManager * getPeerMgr() const;
+
+ /// Are we in the process of moving files
+ bool isMovingFiles() const {return moving_files;}
+
+ public slots:
+ /**
+ * Update the object, should be called periodically.
+ */
+ void update();
+
+ /**
+ * Start the download of the torrent.
+ */
+ void start();
+
+ /**
+ * Stop the download, closes all connections.
+ * @param user wether or not the user did this explicitly
+ * @param wjob WaitJob to wait at exit for the completion of stopped requests
+ */
+ void stop(bool user,WaitJob* wjob = 0);
+
+ /**
+ * Update the tracker, this should normally handled internally.
+ * We leave it public so that the user can do a manual announce.
+ */
+ void updateTracker();
+
+ /**
+ * The tracker status has changed.
+ * @param ns New status
+ */
+ void trackerStatusChanged(const QString & ns);
+
+ private slots:
+ void onNewPeer(Peer* p);
+ void onPeerRemoved(Peer* p);
+ void doChoking();
+ void onIOError(const QString & msg);
+ void onPortPacket(const QString & ip,Uint16 port);
+ /// Update the stats of the torrent.
+ void updateStats();
+ void corrupted(Uint32 chunk);
+ void moveDataFilesJobDone(KIO::Job* job);
+
+ private:
+ void updateTracker(const QString & ev,bool last_succes = true);
+ void updateStatusMsg();
+ void saveStats();
+ void loadStats();
+ void loadOutputDir();
+ void getSeederInfo(Uint32 & total,Uint32 & connected_to) const;
+ void getLeecherInfo(Uint32 & total,Uint32 & connected_to) const;
+ void migrateTorrent(const QString & default_save_dir);
+ void continueStart();
+ virtual void handleError(const QString & err);
+
+ void initInternal(QueueManager* qman,const QString & tmpdir,
+ const QString & ddir,const QString & default_save_dir,bool first_time);
+
+ void checkExisting(QueueManager* qman);
+ void setupDirs(const QString & tmpdir,const QString & ddir);
+ void setupStats();
+ void setupData(const QString & ddir);
+ virtual void afterDataCheck();
+ virtual bool isCheckingData(bool & finished) const;
+
+ private:
+ Torrent* tor;
+ PeerSourceManager* psman;
+ ChunkManager* cman;
+ PeerManager* pman;
+ Downloader* down;
+ Uploader* up;
+ Choker* choke;
+ TimeEstimator* m_eta;
+ kt::MonitorInterface* tmon;
+
+ Timer choker_update_timer;
+ Timer stats_save_timer;
+ Timer stalled_timer;
+
+ QString datadir;
+ QString old_datadir;
+ QString outputdir;
+ QString error_msg;
+
+ QString move_data_files_destination_path;
+ bool restart_torrent_after_move_data_files;
+
+ bool prealloc;
+ PreallocationThread* prealoc_thread;
+ DataCheckerThread* dcheck_thread;
+ TimeStamp last_diskspace_check;
+ bool moving_files;
+
+ struct InternalStats
+ {
+ QDateTime time_started_dl;
+ QDateTime time_started_ul;
+ Uint32 running_time_dl;
+ Uint32 running_time_ul;
+ Uint64 prev_bytes_dl;
+ Uint64 prev_bytes_ul;
+ Uint64 trk_prev_bytes_dl;
+ Uint64 trk_prev_bytes_ul;
+ Uint64 session_bytes_uploaded;
+ bool io_error;
+ bool custom_output_name;
+ Uint16 port;
+ int priority;
+ bool dht_on;
+ TimeStamp last_announce;
+ bool diskspace_warning_emitted;
+ };
+
+ Uint32 upload_gid; // group ID for upload
+ Uint32 upload_limit;
+ Uint32 download_gid; // group ID for download
+ Uint32 download_limit;
+
+ InternalStats istats;
+ };
+
+
+}
+
+#endif
diff --git a/libktorrent/torrent/torrentcreator.cpp b/libktorrent/torrent/torrentcreator.cpp
new file mode 100644
index 0000000..7b132b8
--- /dev/null
+++ b/libktorrent/torrent/torrentcreator.cpp
@@ -0,0 +1,388 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <qdir.h>
+#include <qfileinfo.h>
+#include <klocale.h>
+#include <time.h>
+#include <util/error.h>
+#include <ktversion.h>
+#include "torrentcontrol.h"
+#include "torrentcreator.h"
+#include "bencoder.h"
+#include <util/file.h>
+#include <util/sha1hash.h>
+#include <util/fileops.h>
+#include <util/log.h>
+#include <util/array.h>
+#include <util/functions.h>
+#include "globals.h"
+#include "chunkmanager.h"
+#include "statsfile.h"
+
+namespace bt
+{
+
+ TorrentCreator::TorrentCreator(const QString & tar,
+ const QStringList & track,
+ Uint32 cs,
+ const QString & name,
+ const QString & comments,bool priv, bool decentralized)
+ : target(tar),trackers(track),chunk_size(cs),
+ name(name),comments(comments),cur_chunk(0),priv(priv),tot_size(0), decentralized(decentralized)
+ {
+ this->chunk_size *= 1024;
+ QFileInfo fi(target);
+ if (fi.isDir())
+ {
+ if (!this->target.endsWith(bt::DirSeparator()))
+ this->target += bt::DirSeparator();
+
+ tot_size = 0;
+ buildFileList("");
+ num_chunks = tot_size / chunk_size;
+ if (tot_size % chunk_size > 0)
+ num_chunks++;
+ last_size = tot_size % chunk_size;
+ Out() << "Tot Size : " << tot_size << endl;
+ }
+ else
+ {
+ tot_size = bt::FileSize(target);
+ num_chunks = tot_size / chunk_size;
+ if (tot_size % chunk_size > 0)
+ num_chunks++;
+ last_size = tot_size % chunk_size;
+ Out() << "Tot Size : " << tot_size << endl;
+ }
+
+ if (last_size == 0)
+ last_size = chunk_size;
+
+ Out() << "Num Chunks : " << num_chunks << endl;
+ Out() << "Chunk Size : " << chunk_size << endl;
+ Out() << "Last Size : " << last_size << endl;
+ }
+
+
+ TorrentCreator::~TorrentCreator()
+ {}
+
+ void TorrentCreator::buildFileList(const QString & dir)
+ {
+ QDir d(target + dir);
+ // first get all files (we ignore symlinks)
+ QStringList dfiles = d.entryList(QDir::Files|QDir::NoSymLinks);
+ Uint32 cnt = 0; // counter to keep track of file index
+ for (QStringList::iterator i = dfiles.begin();i != dfiles.end();++i)
+ {
+ // add a TorrentFile to the list
+ Uint64 fs = bt::FileSize(target + dir + *i);
+ TorrentFile f(cnt,dir + *i,tot_size,fs,chunk_size);
+ files.append(f);
+ // update total size
+ tot_size += fs;
+ cnt++;
+ }
+
+ // now for each subdir do a buildFileList
+ QStringList subdirs = d.entryList(QDir::Dirs|QDir::NoSymLinks);
+ for (QStringList::iterator i = subdirs.begin();i != subdirs.end();++i)
+ {
+ if (*i == "." || *i == "..")
+ continue;
+
+ QString sd = dir + *i;
+ if (!sd.endsWith(bt::DirSeparator()))
+ sd += bt::DirSeparator();
+ buildFileList(sd);
+ }
+ }
+
+
+ void TorrentCreator::saveTorrent(const QString & url)
+ {
+ File fptr;
+ if (!fptr.open(url,"wb"))
+ throw Error(i18n("Cannot open file %1: %2").arg(url).arg(fptr.errorString()));
+
+ BEncoder enc(&fptr);
+ enc.beginDict(); // top dict
+
+ if(!decentralized)
+ {
+ enc.write("announce"); enc.write(trackers[0]);
+ if (trackers.count() > 1)
+ {
+ enc.write("announce-list");
+ enc.beginList();
+ enc.beginList();
+ for (Uint32 i = 0;i < trackers.count();i++)
+ enc.write(trackers[i]);
+ enc.end();
+ enc.end();
+
+ }
+ }
+
+
+ if (comments.length() > 0)
+ {
+ enc.write("comments");
+ enc.write(comments);
+ }
+ enc.write("created by");enc.write(QString("KTorrent %1").arg(kt::VERSION_STRING));
+ enc.write("creation date");enc.write((Uint64)time(0));
+ enc.write("info");
+ saveInfo(enc);
+ // save the nodes list after the info hash, keys must be sorted !
+ if (decentralized)
+ {
+ //DHT torrent
+ enc.write("nodes");
+ enc.beginList();
+
+ for(int i=0; i < trackers.count(); ++i)
+ {
+ QString t = trackers[i];
+ enc.beginList();
+ enc.write(t.section(',',0,0));
+ enc.write((Uint32)t.section(',',1,1).toInt());
+ enc.end();
+ }
+ enc.end();
+ }
+
+ enc.end();
+ }
+
+ void TorrentCreator::saveInfo(BEncoder & enc)
+ {
+ enc.beginDict();
+
+ QFileInfo fi(target);
+ if (fi.isDir())
+ {
+ enc.write("files");
+ enc.beginList();
+ QValueList<TorrentFile>::iterator i = files.begin();
+ while (i != files.end())
+ {
+ saveFile(enc,*i);
+ i++;
+ }
+ enc.end();
+ }
+ else
+ {
+ enc.write("length"); enc.write(bt::FileSize(target));
+ }
+ enc.write("name"); enc.write(name);
+ enc.write("piece length"); enc.write((Uint64)chunk_size);
+ enc.write("pieces"); savePieces(enc);
+ if (priv)
+ {
+ enc.write("private");
+ enc.write((Uint64)1);
+ }
+ enc.end();
+ }
+
+ void TorrentCreator::saveFile(BEncoder & enc,const TorrentFile & file)
+ {
+ enc.beginDict();
+ enc.write("length");enc.write(file.getSize());
+ enc.write("path");
+ enc.beginList();
+ QStringList sl = QStringList::split(bt::DirSeparator(),file.getPath());
+ for (QStringList::iterator i = sl.begin();i != sl.end();i++)
+ enc.write(*i);
+ enc.end();
+ enc.end();
+ }
+
+ void TorrentCreator::savePieces(BEncoder & enc)
+ {
+ if (hashes.empty())
+ while (!calculateHash())
+ ;
+
+ Array<Uint8> big_hash(num_chunks*20);
+ for (Uint32 i = 0;i < num_chunks;++i)
+ {
+ memcpy(big_hash+(20*i),hashes[i].getData(),20);
+ }
+ enc.write(big_hash,num_chunks*20);
+ }
+
+ bool TorrentCreator::calcHashSingle()
+ {
+ Array<Uint8> buf(chunk_size);
+ File fptr;
+ if (!fptr.open(target,"rb"))
+ throw Error(i18n("Cannot open file %1: %2")
+ .arg(target).arg(fptr.errorString()));
+
+ Uint32 s = cur_chunk != num_chunks - 1 ? chunk_size : last_size;
+ fptr.seek(File::BEGIN,(Int64)cur_chunk*chunk_size);
+
+ fptr.read(buf,s);
+ SHA1Hash h = SHA1Hash::generate(buf,s);
+ hashes.append(h);
+ cur_chunk++;
+ return cur_chunk >= num_chunks;
+ }
+
+ bool TorrentCreator::calcHashMulti()
+ {
+ Uint32 s = cur_chunk != num_chunks - 1 ? chunk_size : last_size;
+ // first find the file(s) the chunk lies in
+ Array<Uint8> buf(s);
+ QValueList<TorrentFile> file_list;
+ Uint32 i = 0;
+ while (i < files.size())
+ {
+ const TorrentFile & tf = files[i];
+ if (cur_chunk >= tf.getFirstChunk() && cur_chunk <= tf.getLastChunk())
+ {
+ file_list.append(tf);
+ }
+
+ i++;
+ }
+
+ Uint32 read = 0;
+ for (i = 0;i < file_list.count();i++)
+ {
+ const TorrentFile & f = file_list[i];
+ File fptr;
+ if (!fptr.open(target + f.getPath(),"rb"))
+ {
+ throw Error(i18n("Cannot open file %1: %2")
+ .arg(f.getPath()).arg(fptr.errorString()));
+ }
+
+ // first calculate offset into file
+ // only the first file can have an offset
+ // the following files will start at the beginning
+ Uint64 off = 0;
+ if (i == 0)
+ off = f.fileOffset(cur_chunk,chunk_size);
+
+ Uint32 to_read = 0;
+ // then the amount of data we can read from this file
+ if (file_list.count() == 1)
+ to_read = s;
+ else if (i == 0)
+ to_read = f.getLastChunkSize();
+ else if (i == file_list.count() - 1)
+ to_read = s - read;
+ else
+ to_read = f.getSize();
+
+ // read part of data
+ fptr.seek(File::BEGIN,(Int64)off);
+ fptr.read(buf + read,to_read);
+ read += to_read;
+ }
+
+ // generate hash
+ SHA1Hash h = SHA1Hash::generate(buf,s);
+ hashes.append(h);
+
+ cur_chunk++;
+ // Out() << "=============================================" << endl;
+ return cur_chunk >= num_chunks;
+ }
+
+ bool TorrentCreator::calculateHash()
+ {
+ if (cur_chunk >= num_chunks)
+ return true;
+ if (files.empty())
+ return calcHashSingle();
+ else
+ return calcHashMulti();
+ }
+
+ TorrentControl* TorrentCreator::makeTC(const QString & data_dir)
+ {
+ QString dd = data_dir;
+ if (!dd.endsWith(bt::DirSeparator()))
+ dd += bt::DirSeparator();
+
+ // make data dir if necessary
+ if (!bt::Exists(dd))
+ bt::MakeDir(dd);
+
+ // save the torrent
+ saveTorrent(dd + "torrent");
+ // write full index file
+ File fptr;
+ if (!fptr.open(dd + "index","wb"))
+ throw Error(i18n("Cannot create index file: %1").arg(fptr.errorString()));
+
+ for (Uint32 i = 0;i < num_chunks;i++)
+ {
+ NewChunkHeader hdr;
+ hdr.index = i;
+ fptr.write(&hdr,sizeof(NewChunkHeader));
+ }
+ fptr.close();
+
+ // now create the torrentcontrol object
+ TorrentControl* tc = new TorrentControl();
+ try
+ {
+ // get the parent dir of target
+ QFileInfo fi = QFileInfo(target);
+
+ QString odir;
+ StatsFile st(dd + "stats");
+ if (fi.fileName() == name)
+ {
+ st.write("OUTPUTDIR", fi.dirPath(true));
+ odir = fi.dirPath(true);
+ }
+ else
+ {
+ st.write("CUSTOM_OUTPUT_NAME","1");
+ st.write("OUTPUTDIR", target);
+ odir = target;
+ }
+ st.write("UPLOADED", "0");
+ st.write("RUNNING_TIME_DL","0");
+ st.write("RUNNING_TIME_UL","0");
+ st.write("PRIORITY", "0");
+ st.write("AUTOSTART", "1");
+ st.write("IMPORTED", QString::number(tot_size));
+ st.writeSync();
+
+ tc->init(0,dd + "torrent",dd,odir,QString::null);
+ tc->createFiles();
+ }
+ catch (...)
+ {
+ delete tc;
+ throw;
+ }
+
+ return tc;
+ }
+}
diff --git a/libktorrent/torrent/torrentcreator.h b/libktorrent/torrent/torrentcreator.h
new file mode 100644
index 0000000..c7057e2
--- /dev/null
+++ b/libktorrent/torrent/torrentcreator.h
@@ -0,0 +1,114 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef BTTORRENTCREATOR_H
+#define BTTORRENTCREATOR_H
+
+#include <qstringlist.h>
+#include "torrent.h"
+#include <util/sha1hash.h>
+
+namespace bt
+{
+ class BEncoder;
+ class TorrentControl;
+
+ /**
+ * @author Joris Guisson
+ * @brief Class to generate torrent files
+ *
+ * This class generates torrent files.
+ * It also allows to create a TorrentControl object, so
+ * that we immediately can start to share the torrent.
+ */
+ class TorrentCreator
+ {
+ // input values
+ QString target;
+ QStringList trackers;
+ int chunk_size;
+ QString name,comments;
+ // calculated values
+ Uint32 num_chunks;
+ Uint64 last_size;
+ QValueList<TorrentFile> files;
+ QValueList<SHA1Hash> hashes;
+ //
+ Uint32 cur_chunk;
+ bool priv;
+ Uint64 tot_size;
+ bool decentralized;
+ public:
+ /**
+ * Constructor.
+ * @param target The file or directory to make a torrent of
+ * @param trackers A list of tracker urls
+ * @param chunk_size The size of each chunk
+ * @param name The name suggestion
+ * @param comments The comments field of the torrent
+ * @param priv Private torrent or not
+ */
+ TorrentCreator(const QString & target,const QStringList & trackers,
+ Uint32 chunk_size,const QString & name,
+ const QString & comments,bool priv,bool decentralized);
+ virtual ~TorrentCreator();
+
+
+ /**
+ * Calculate the hash of a chunk, this function should be called
+ * until it returns true. We do it this way so that the calling
+ * function can display a progress dialog.
+ * @return true if all hashes are calculated, false otherwise
+ */
+ bool calculateHash();
+
+ /// Get the number of chunks
+ Uint32 getNumChunks() const {return num_chunks;}
+
+ /**
+ * Save the torrent file.
+ * @param url Filename
+ * @throw Error if something goes wrong
+ */
+ void saveTorrent(const QString & url);
+
+ /**
+ * Make a TorrentControl object for this torrent.
+ * This will also create the files :
+ * data_dir/index
+ * data_dir/torrent
+ * data_dir/cache (symlink to target)
+ * @param data_dir The data directory
+ * @throw Error if something goes wrong
+ * @return The newly created object
+ */
+ TorrentControl* makeTC(const QString & data_dir);
+
+ private:
+ void saveInfo(BEncoder & enc);
+ void saveFile(BEncoder & enc,const TorrentFile & file);
+ void savePieces(BEncoder & enc);
+ void buildFileList(const QString & dir);
+ bool calcHashSingle();
+ bool calcHashMulti();
+ };
+
+}
+
+#endif
diff --git a/libktorrent/torrent/torrentfile.cpp b/libktorrent/torrent/torrentfile.cpp
new file mode 100644
index 0000000..9c21a4a
--- /dev/null
+++ b/libktorrent/torrent/torrentfile.cpp
@@ -0,0 +1,200 @@
+/***************************************************************************
+ * Copyright (C) 2005 by *
+ * Joris Guisson <joris.guisson@gmail.com> *
+ * Ivan Vasic <ivasic@gmail.com> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <math.h>
+#include <util/log.h>
+#include <util/bitset.h>
+#include <util/functions.h>
+#include "globals.h"
+#include "torrentfile.h"
+
+namespace bt
+{
+
+ TorrentFile::TorrentFile() : TorrentFileInterface(QString::null,0),missing(false),filetype(UNKNOWN)
+ {}
+
+ TorrentFile::TorrentFile(Uint32 index,const QString & path,
+ Uint64 off,Uint64 size,Uint64 chunk_size)
+ : TorrentFileInterface(path,size),index(index),cache_offset(off),missing(false),filetype(UNKNOWN)
+ {
+ first_chunk = off / chunk_size;
+ first_chunk_off = off % chunk_size;
+ if (size > 0)
+ last_chunk = (off + size - 1) / chunk_size;
+ else
+ last_chunk = first_chunk;
+ last_chunk_size = (off + size) - last_chunk * chunk_size;
+ priority = old_priority = NORMAL_PRIORITY;
+ }
+
+ TorrentFile::TorrentFile(const TorrentFile & tf)
+ : TorrentFileInterface(QString::null,0)
+ {
+ index = tf.getIndex();
+ path = tf.getPath();
+ size = tf.getSize();
+ cache_offset = tf.getCacheOffset();
+ first_chunk = tf.getFirstChunk();
+ first_chunk_off = tf.getFirstChunkOffset();
+ last_chunk = tf.getLastChunk();
+ last_chunk_size = tf.getLastChunkSize();
+ old_priority = priority = tf.getPriority();
+ missing = tf.isMissing();
+ filetype = UNKNOWN;
+ }
+
+ TorrentFile::~TorrentFile()
+ {}
+
+ void TorrentFile::setDoNotDownload(bool dnd)
+ {
+ if (priority != EXCLUDED && dnd)
+ {
+ if(m_emitDlStatusChanged)
+ old_priority = priority;
+
+ priority = EXCLUDED;
+
+ if(m_emitDlStatusChanged)
+ emit downloadPriorityChanged(this,priority,old_priority);
+ }
+ if (priority == EXCLUDED && (!dnd))
+ {
+ if(m_emitDlStatusChanged)
+ old_priority = priority;
+
+ priority = NORMAL_PRIORITY;
+
+ if(m_emitDlStatusChanged)
+ emit downloadPriorityChanged(this,priority,old_priority);
+ }
+ }
+
+ void TorrentFile::emitDownloadStatusChanged()
+ {
+ // only emit when old_priority is not equal to the new priority
+ if (priority != old_priority)
+ emit downloadPriorityChanged(this,priority,old_priority);
+ }
+
+
+ bool TorrentFile::isMultimedia() const
+ {
+ if (filetype == UNKNOWN)
+ {
+ if (IsMultimediaFile(getPath()))
+ {
+ filetype = MULTIMEDIA;
+ return true;
+ }
+ else
+ {
+ filetype = NORMAL;
+ return false;
+ }
+ }
+ return filetype == MULTIMEDIA;
+ }
+
+ void TorrentFile::setPriority(Priority newpriority)
+ {
+ if(priority != newpriority)
+ {
+ if (priority == EXCLUDED)
+ {
+ setDoNotDownload(false);
+ }
+ if (newpriority == EXCLUDED)
+ {
+ setDoNotDownload(true);
+ }
+ else
+ {
+ old_priority = priority;
+ priority = newpriority;
+ emit downloadPriorityChanged(this,newpriority,old_priority);
+ }
+ }
+ }
+
+ TorrentFile & TorrentFile::operator = (const TorrentFile & tf)
+ {
+ index = tf.getIndex();
+ path = tf.getPath();
+ size = tf.getSize();
+ cache_offset = tf.getCacheOffset();
+ first_chunk = tf.getFirstChunk();
+ first_chunk_off = tf.getFirstChunkOffset();
+ last_chunk = tf.getLastChunk();
+ last_chunk_size = tf.getLastChunkSize();
+ priority = tf.getPriority();
+ missing = tf.isMissing();
+ return *this;
+ }
+
+ TorrentFile TorrentFile::null;
+
+
+ Uint64 TorrentFile::fileOffset(Uint32 cindex,Uint64 chunk_size) const
+ {
+ Uint64 off = 0;
+ if (getFirstChunkOffset() == 0)
+ {
+ off = (cindex - getFirstChunk()) * chunk_size;
+ }
+ else
+ {
+ if (cindex - this->getFirstChunk() > 0)
+ off = (cindex - this->getFirstChunk() - 1) * chunk_size;
+ if (cindex > 0)
+ off += (chunk_size - this->getFirstChunkOffset());
+ }
+ return off;
+ }
+
+ void TorrentFile::updateNumDownloadedChunks(const BitSet & bs)
+ {
+ float p = getDownloadPercentage();
+ num_chunks_downloaded = 0;
+ bool prev = preview;
+ preview = true;
+ for (Uint32 i = first_chunk;i <= last_chunk;i++)
+ {
+ if (bs.get(i))
+ {
+ num_chunks_downloaded++;
+ }
+ else if (i == first_chunk || i == first_chunk + 1)
+ {
+ preview = false;
+ }
+ }
+ preview = isMultimedia() && preview;
+
+ float np = getDownloadPercentage();
+ if (fabs(np - p) >= 0.01f)
+ downloadPercentageChanged(np);
+
+ if (prev != preview)
+ previewAvailable(preview);
+ }
+}
+#include "torrentfile.moc"
diff --git a/libktorrent/torrent/torrentfile.h b/libktorrent/torrent/torrentfile.h
new file mode 100644
index 0000000..9e0c397
--- /dev/null
+++ b/libktorrent/torrent/torrentfile.h
@@ -0,0 +1,158 @@
+/***************************************************************************
+ * Copyright (C) 2005 by *
+ * Joris Guisson <joris.guisson@gmail.com> *
+ * Ivan Vasic <ivasic@gmail.com> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef BTTORRENTFILE_H
+#define BTTORRENTFILE_H
+
+#include <qstring.h>
+#include <util/constants.h>
+#include <interfaces/torrentfileinterface.h>
+
+namespace bt
+{
+ class BitSet;
+
+ /**
+ * @author Joris Guisson
+ *
+ * File in a multi file torrent. Keeps track of the path of the file,
+ * it's size, offset into the cache and between which chunks it lies.
+ */
+ class TorrentFile : public kt::TorrentFileInterface
+ {
+ Q_OBJECT
+
+ Uint32 index;
+ Uint64 cache_offset;
+ Uint64 first_chunk_off;
+ Uint64 last_chunk_size;
+ Priority priority;
+ Priority old_priority;
+ bool missing;
+ enum FileType
+ {
+ UNKNOWN,
+ MULTIMEDIA,
+ NORMAL
+ };
+ mutable FileType filetype;
+ public:
+ /**
+ * Default constructor. Creates a null TorrentFile.
+ */
+ TorrentFile();
+
+ /**
+ * Constructor.
+ * @param index Index number of the file
+ * @param path Path of the file
+ * @param off Offset into the torrent
+ * (i.e. how many bytes were all the previous files in the torrent combined)
+ * @param size Size of the file
+ * @param chunk_size Size of each chunk
+ */
+ TorrentFile(Uint32 index,const QString & path,Uint64 off,Uint64 size,Uint64 chunk_size);
+
+ /**
+ * Copy constructor.
+ * @param tf The TorrentFile to copy
+ */
+ TorrentFile(const TorrentFile & tf);
+ virtual ~TorrentFile();
+
+ /// Get the index of the file
+ Uint32 getIndex() const {return index;}
+
+ /// Get the offset into the torrent
+ Uint64 getCacheOffset() const {return cache_offset;}
+
+ /// Get the offset at which the file starts in the first chunk
+ Uint64 getFirstChunkOffset() const {return first_chunk_off;}
+
+ /// Get how many bytes the files takes up of the last chunk
+ Uint64 getLastChunkSize() const {return last_chunk_size;}
+
+ /// Check if this file doesn't have to be downloaded
+ bool doNotDownload() const
+ {if(priority == EXCLUDED) return true; else return false;}
+
+ /// Set wether we have to not download this file
+ void setDoNotDownload(bool dnd);
+
+ /// Checks if this file is multimedial
+ bool isMultimedia() const;
+
+ /// Gets the priority of the file
+ Priority getPriority() const {return priority;}
+
+ /// Sets the priority of the file
+ void setPriority(Priority newpriority = NORMAL_PRIORITY);
+
+ /// Get the previous priority value
+ Priority getOldPriority() const {return old_priority;}
+
+
+ /// emits signal.
+ void emitDownloadStatusChanged();
+
+ void setEmitDownloadStatusChanged(bool show) { m_emitDlStatusChanged = show; }
+
+ /**
+ * Assignment operator
+ * @param tf The file to copy
+ * @return *this
+ */
+ TorrentFile & operator = (const TorrentFile & tf);
+
+ /// See if the file is missing
+ bool isMissing() const {return missing;}
+
+ /// Set the file to be missing or not
+ void setMissing(bool m) {missing = m;}
+
+ /**
+ * Calculate the offset of a chunk in the file
+ * @param cindex Index of chunk
+ * @param chunk_size Size of each chunk
+ */
+ Uint64 fileOffset(Uint32 cindex,Uint64 chunk_size) const;
+
+ static TorrentFile null;
+
+ /**
+ * Update the number of downloaded chunks for this file.
+ * @param bs The current bitset of all chunks
+ */
+ void updateNumDownloadedChunks(const BitSet & bs);
+
+ signals:
+ /**
+ * Signal emitted when the Priority variable changes.
+ * @param tf The TorrentFile which emitted the signal
+ * @param newpriority THe new priority of the file
+ * @param oldpriority Previous priority
+ */
+ void downloadPriorityChanged(TorrentFile* tf,Priority newpriority,Priority oldpriority);
+
+ };
+
+}
+
+#endif
diff --git a/libktorrent/torrent/torrentmonitor.cpp b/libktorrent/torrent/torrentmonitor.cpp
new file mode 100644
index 0000000..ff33acb
--- /dev/null
+++ b/libktorrent/torrent/torrentmonitor.cpp
@@ -0,0 +1,33 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include "torrentmonitor.h"
+
+namespace bt
+{
+
+ TorrentMonitor::TorrentMonitor()
+ {}
+
+
+ TorrentMonitor::~TorrentMonitor()
+ {}
+
+
+}
diff --git a/libktorrent/torrent/torrentmonitor.h b/libktorrent/torrent/torrentmonitor.h
new file mode 100644
index 0000000..52e3835
--- /dev/null
+++ b/libktorrent/torrent/torrentmonitor.h
@@ -0,0 +1,47 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef BTTORRENTMONITOR_H
+#define BTTORRENTMONITOR_H
+
+namespace bt
+{
+ class Peer;
+ class ChunkDownload;
+
+ /**
+ @author Joris Guisson
+ */
+ class TorrentMonitor
+ {
+ public:
+ TorrentMonitor();
+ virtual ~TorrentMonitor();
+
+ virtual void peerAdded(Peer* peer) = 0;
+ virtual void peerRemoved(Peer* peer) = 0;
+ virtual void downloadStarted(ChunkDownload* cd) = 0;
+ virtual void downloadRemoved(ChunkDownload* cd) = 0;
+ virtual void stopped() = 0;
+ virtual void destroyed() = 0;
+ };
+
+}
+
+#endif
diff --git a/libktorrent/torrent/tracker.cpp b/libktorrent/torrent/tracker.cpp
new file mode 100644
index 0000000..261169c
--- /dev/null
+++ b/libktorrent/torrent/tracker.cpp
@@ -0,0 +1,93 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <stdlib.h>
+#include <time.h>
+#include <kurl.h>
+#include <kresolver.h>
+#include <util/functions.h>
+#include <util/log.h>
+#include <torrent/globals.h>
+#include <interfaces/torrentinterface.h>
+#include <kademlia/dhtbase.h>
+#include <kademlia/dhttrackerbackend.h>
+#include "server.h"
+#include "tracker.h"
+#include "udptracker.h"
+#include "httptracker.h"
+
+using namespace KNetwork;
+
+namespace bt
+{
+ static QString custom_ip;
+ static QString custom_ip_resolved;
+
+ Tracker::Tracker(const KURL & url,kt::TorrentInterface* tor,const PeerID & id,int tier)
+ : url(url),tier(tier),peer_id(id),tor(tor)
+ {
+ // default 5 minute interval
+ interval = 5 * 60 * 1000;
+ seeders = leechers = 0;
+ srand(time(0));
+ key = rand();
+ started = false;
+ }
+
+ Tracker::~Tracker()
+ {
+ }
+
+ void Tracker::setCustomIP(const QString & ip)
+ {
+ if (custom_ip == ip)
+ return;
+
+ Out(SYS_TRK|LOG_NOTICE) << "Setting custom ip to " << ip << endl;
+ custom_ip = ip;
+ custom_ip_resolved = QString::null;
+ if (ip.isNull())
+ return;
+
+ KResolverResults res = KResolver::resolve(ip,QString::null);
+ if (res.error() || res.empty())
+ {
+ custom_ip = custom_ip_resolved = QString::null;
+ }
+ else
+ {
+ custom_ip_resolved = res.first().address().nodeName();
+ Out(SYS_TRK|LOG_NOTICE) << "custom_ip_resolved = " << custom_ip_resolved << endl;
+ }
+ }
+
+ QString Tracker::getCustomIP()
+ {
+ return custom_ip_resolved;
+ }
+
+ void Tracker::timedDelete(int ms)
+ {
+ QTimer::singleShot(ms,this,SLOT(deleteLater()));
+ connect(this,SIGNAL(stopDone()),this,SLOT(deleteLater()));
+ }
+
+}
+
+#include "tracker.moc"
diff --git a/libktorrent/torrent/tracker.h b/libktorrent/torrent/tracker.h
new file mode 100644
index 0000000..d254b63
--- /dev/null
+++ b/libktorrent/torrent/tracker.h
@@ -0,0 +1,136 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef BTTRACKER_H
+#define BTTRACKER_H
+
+#include <kurl.h>
+#include <util/sha1hash.h>
+#include <interfaces/peersource.h>
+#include "globals.h"
+#include "peerid.h"
+
+class KURL;
+
+namespace kt
+{
+ class TorrentInterface;
+}
+
+
+namespace bt
+{
+ class Tracker;
+
+ /**
+ * Base class for all tracker classes.
+ */
+ class Tracker : public kt::PeerSource
+ {
+ Q_OBJECT
+ public:
+ Tracker(const KURL & url,kt::TorrentInterface* tor,const PeerID & id,int tier);
+ virtual ~Tracker();
+
+ /// See if a start request succeeded
+ bool isStarted() const {return started;}
+
+ /**
+ * Set the custom IP
+ * @param str
+ */
+ static void setCustomIP(const QString & str);
+
+ /// get the tracker url
+ KURL trackerURL() const {return url;}
+
+ /**
+ * Delete the tracker in ms milliseconds, or when the stopDone signal is emitted.
+ * @param ms Number of ms to wait
+ */
+ void timedDelete(int ms);
+
+ /**
+ * Get the number of failed attempts to reach a tracker.
+ * @return The number of failed attempts
+ */
+ virtual Uint32 failureCount() const = 0;
+
+ /**
+ * Do a tracker scrape to get more accurate stats about a torrent.
+ * Does nothing if the tracker does not support this.
+ */
+ virtual void scrape() = 0;
+
+ /// Get the trackers tier
+ int getTier() const {return tier;}
+
+ /**
+ * Get the update interval in ms
+ * @return interval
+ */
+ Uint32 getInterval() const {return interval;}
+
+ /// Set the interval
+ void setInterval(Uint32 i) {interval = i;}
+
+ /// Get the number of seeders
+ Uint32 getNumSeeders() const {return seeders;}
+
+ /// Get the number of leechers
+ Uint32 getNumLeechers() const {return leechers;}
+
+ /// Get the custom ip to use, null if none is set
+ static QString getCustomIP();
+ signals:
+ /**
+ * Emitted when an error happens.
+ * @param failure_reason The reason why we couldn't reach the tracker
+ */
+ void requestFailed(const QString & failure_reason);
+
+ /**
+ * Emitted when a stop is done.
+ */
+ void stopDone();
+
+ /**
+ * Emitted when a request to the tracker succeeded
+ */
+ void requestOK();
+
+ /**
+ * A request to the tracker has been started.
+ */
+ void requestPending();
+
+ protected:
+ KURL url;
+ int tier;
+ PeerID peer_id;
+ kt::TorrentInterface* tor;
+ Uint32 interval,seeders,leechers,key;
+ bool started;
+ private:
+ //static QString custom_ip,custom_ip_resolved;
+ };
+
+}
+
+#endif
diff --git a/libktorrent/torrent/udptracker.cpp b/libktorrent/torrent/udptracker.cpp
new file mode 100644
index 0000000..2dd4a01
--- /dev/null
+++ b/libktorrent/torrent/udptracker.cpp
@@ -0,0 +1,291 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <stdlib.h>
+#include <kresolver.h>
+#include <util/functions.h>
+#include <util/log.h>
+#include <ksocketaddress.h>
+#include "peermanager.h"
+#include "udptracker.h"
+#include "torrentcontrol.h"
+#include "globals.h"
+#include "server.h"
+#include "udptrackersocket.h"
+
+
+using namespace kt;
+using namespace KNetwork;
+
+namespace bt
+{
+
+ UDPTrackerSocket* UDPTracker::socket = 0;
+ Uint32 UDPTracker::num_instances = 0;
+
+
+ UDPTracker::UDPTracker(const KURL & url,kt::TorrentInterface* tor,const PeerID & id,int tier)
+ : Tracker(url,tor,id,tier)
+ {
+ num_instances++;
+ if (!socket)
+ socket = new UDPTrackerSocket();
+
+ connection_id = 0;
+ transaction_id = 0;
+ interval = 0;
+
+ connect(&conn_timer,SIGNAL(timeout()),this,SLOT(onConnTimeout()));
+ connect(socket,SIGNAL(announceRecieved(Int32, const QByteArray &)),
+ this,SLOT(announceRecieved(Int32, const QByteArray& )));
+ connect(socket,SIGNAL(connectRecieved(Int32, Int64 )),
+ this,SLOT(connectRecieved(Int32, Int64 )));
+ connect(socket,SIGNAL(error(Int32, const QString& )),
+ this,SLOT(onError(Int32, const QString& )));
+
+ KResolver::resolveAsync(this,SLOT(onResolverResults(KResolverResults )),
+ url.host(),QString::number(url.port()));
+ }
+
+
+ UDPTracker::~UDPTracker()
+ {
+ num_instances--;
+ if (num_instances == 0)
+ {
+ delete socket;
+ socket = 0;
+ }
+ }
+
+ void UDPTracker::start()
+ {
+ event = STARTED;
+ conn_timer.stop();
+ doRequest();
+ }
+
+ void UDPTracker::stop(WaitJob* )
+ {
+ if (!started)
+ return;
+
+ event = STOPPED;
+ conn_timer.stop();
+ doRequest();
+ started = false;
+ }
+
+ void UDPTracker::completed()
+ {
+ event = COMPLETED;
+ conn_timer.stop();
+ doRequest();
+ }
+
+ void UDPTracker::manualUpdate()
+ {
+ conn_timer.stop();
+ if (!started)
+ event = STARTED;
+ doRequest();
+ }
+
+ void UDPTracker::connectRecieved(Int32 tid,Int64 cid)
+ {
+ if (tid != transaction_id)
+ return;
+
+ connection_id = cid;
+ n = 0;
+ sendAnnounce();
+ }
+
+ void UDPTracker::announceRecieved(Int32 tid,const QByteArray & data)
+ {
+ if (tid != transaction_id)
+ return;
+
+ const Uint8* buf = (const Uint8*)data.data();
+
+ /*
+ 0 32-bit integer action 1
+ 4 32-bit integer transaction_id
+ 8 32-bit integer interval
+ 12 32-bit integer leechers
+ 16 32-bit integer seeders
+ 20 + 6 * n 32-bit integer IP address
+ 24 + 6 * n 16-bit integer TCP port
+ 20 + 6 * N
+ */
+ interval = ReadInt32(buf,8);
+ leechers = ReadInt32(buf,12);
+ seeders = ReadInt32(buf,16);
+
+ Uint32 nip = leechers + seeders;
+ Uint32 j = 0;
+ for (Uint32 i = 20;i < data.size() && j < nip;i+=6,j++)
+ {
+ Uint32 ip = ReadUint32(buf,i);
+ addPeer(QString("%1.%2.%3.%4")
+ .arg((ip & (0xFF000000)) >> 24)
+ .arg((ip & (0x00FF0000)) >> 16)
+ .arg((ip & (0x0000FF00)) >> 8)
+ .arg(ip & 0x000000FF),
+ ReadUint16(buf,i+4));
+ }
+
+ peersReady(this);
+ connection_id = 0;
+ conn_timer.stop();
+ if (event != STOPPED)
+ {
+ if (event == STARTED)
+ started = true;
+ event = NONE;
+ requestOK();
+ }
+ else
+ {
+ stopDone();
+ requestOK();
+ }
+ }
+
+ void UDPTracker::onError(Int32 tid,const QString & error_string)
+ {
+ if (tid != transaction_id)
+ return;
+
+ Out(SYS_TRK|LOG_IMPORTANT) << "UDPTracker::error : " << error_string << endl;
+ requestFailed(error_string);
+ }
+
+
+ bool UDPTracker::doRequest()
+ {
+ Out(SYS_TRK|LOG_NOTICE) << "Doing tracker request to url : " << url << endl;
+ if (connection_id == 0)
+ {
+ n = 0;
+ sendConnect();
+ }
+ else
+ sendAnnounce();
+
+ requestPending();
+ return true;
+ }
+
+ void UDPTracker::scrape()
+ {
+ }
+
+ void UDPTracker::sendConnect()
+ {
+ transaction_id = socket->newTransactionID();
+ socket->sendConnect(transaction_id,address);
+ int tn = 1;
+ for (int i = 0;i < n;i++)
+ tn *= 2;
+ conn_timer.start(60000 * tn,true);
+ }
+
+ void UDPTracker::sendAnnounce()
+ {
+ // Out(SYS_TRK|LOG_NOTICE) << "UDPTracker::sendAnnounce()" << endl;
+ transaction_id = socket->newTransactionID();
+ /*
+ 0 64-bit integer connection_id
+ 8 32-bit integer action 1
+ 12 32-bit integer transaction_id
+ 16 20-byte string info_hash
+ 36 20-byte string peer_id
+ 56 64-bit integer downloaded
+ 64 64-bit integer left
+ 72 64-bit integer uploaded
+ 80 32-bit integer event
+ 84 32-bit integer IP address 0
+ 88 32-bit integer key
+ 92 32-bit integer num_want -1
+ 96 16-bit integer port
+ 98
+ */
+
+ Uint32 ev = event;
+ const TorrentStats & s = tor->getStats();
+ Uint16 port = Globals::instance().getServer().getPortInUse();
+ Uint8 buf[98];
+ WriteInt64(buf,0,connection_id);
+ WriteInt32(buf,8,ANNOUNCE);
+ WriteInt32(buf,12,transaction_id);
+ const SHA1Hash & info_hash = tor->getInfoHash();
+ memcpy(buf+16,info_hash.getData(),20);
+ memcpy(buf+36,peer_id.data(),20);
+ WriteInt64(buf,56,s.trk_bytes_downloaded);
+ if (ev == COMPLETED)
+ WriteInt64(buf,64,0);
+ else
+ WriteInt64(buf,64,s.bytes_left);
+ WriteInt64(buf,72,s.trk_bytes_uploaded);
+ WriteInt32(buf,80,ev);
+ QString cip = Tracker::getCustomIP();
+ if (cip.isNull())
+ {
+ WriteUint32(buf,84,0);
+ }
+ else
+ {
+ KNetwork::KIpAddress addr(cip);
+ WriteUint32(buf,84,addr.IPv4Addr(true));
+ }
+ WriteUint32(buf,88,key);
+ if (ev != STOPPED)
+ WriteInt32(buf,92,100);
+ else
+ WriteInt32(buf,92,0);
+ WriteUint16(buf,96,port);
+
+ socket->sendAnnounce(transaction_id,buf,address);
+ }
+
+ void UDPTracker::onConnTimeout()
+ {
+ if (connection_id)
+ {
+ connection_id = 0;
+ n++;
+ if (event != STOPPED)
+ sendConnect();
+ else
+ stopDone();
+ }
+ else
+ {
+ doRequest();
+ }
+ }
+
+ void UDPTracker::onResolverResults(KResolverResults res)
+ {
+ address = res.front().address();
+ }
+
+}
+#include "udptracker.moc"
diff --git a/libktorrent/torrent/udptracker.h b/libktorrent/torrent/udptracker.h
new file mode 100644
index 0000000..5107fb9
--- /dev/null
+++ b/libktorrent/torrent/udptracker.h
@@ -0,0 +1,105 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef BTUDPTRACKER_H
+#define BTUDPTRACKER_H
+
+#include <kurl.h>
+#include <qvaluelist.h>
+#include <qcstring.h>
+#include <qtimer.h>
+#include <ksocketaddress.h>
+#include "tracker.h"
+#include "globals.h"
+#include "peermanager.h"
+
+
+
+namespace KNetwork
+{
+ class KResolverResults;
+}
+
+
+namespace bt
+{
+ using KNetwork::KResolverResults;
+
+ enum Event
+ {
+ NONE = 0,
+ COMPLETED = 1,
+ STARTED = 2,
+ STOPPED = 3
+ };
+
+ class UDPTrackerSocket;
+
+ /**
+ * @author Joris Guisson
+ * @brief Communicates with an UDP tracker
+ *
+ * This class is able to communicate with an UDP tracker.
+ * This is an implementation of the protocol described in
+ * http://xbtt.sourceforge.net/udp_tracker_protocol.html
+ */
+ class UDPTracker : public Tracker
+ {
+ Q_OBJECT
+ public:
+ UDPTracker(const KURL & url,kt::TorrentInterface* tor,const PeerID & id,int tier);
+ virtual ~UDPTracker();
+
+ virtual void start();
+ virtual void stop(WaitJob* wjob = 0);
+ virtual void completed();
+ virtual void manualUpdate();
+ virtual Uint32 failureCount() const {return n;}
+ virtual void scrape();
+
+ private slots:
+ void onConnTimeout();
+ void connectRecieved(Int32 tid,Int64 connection_id);
+ void announceRecieved(Int32 tid,const QByteArray & buf);
+ void onError(Int32 tid,const QString & error_string);
+ void onResolverResults(KResolverResults res);
+
+ private:
+ void sendConnect();
+ void sendAnnounce();
+ bool doRequest();
+
+ private:
+ KNetwork::KSocketAddress address;
+
+ Int32 transaction_id;
+ Int64 connection_id;
+
+ Uint32 data_read;
+ int n;
+ QTimer conn_timer;
+ Event event;
+
+ static UDPTrackerSocket* socket;
+ static Uint32 num_instances;
+ };
+
+}
+
+#endif
diff --git a/libktorrent/torrent/udptrackersocket.cpp b/libktorrent/torrent/udptrackersocket.cpp
new file mode 100644
index 0000000..43ef2b6
--- /dev/null
+++ b/libktorrent/torrent/udptrackersocket.cpp
@@ -0,0 +1,222 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <time.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <util/array.h>
+#include <ksocketaddress.h>
+#include <kdatagramsocket.h>
+#include <ksocketdevice.h>
+#include <net/portlist.h>
+#include <util/log.h>
+#include <util/functions.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include "globals.h"
+#include "udptrackersocket.h"
+
+using namespace KNetwork;
+
+namespace bt
+{
+ Uint16 UDPTrackerSocket::port = 4444;
+
+ UDPTrackerSocket::UDPTrackerSocket()
+ {
+ sock = new KNetwork::KDatagramSocket(this);
+ sock->setAddressReuseable(true);
+ connect(sock,SIGNAL(readyRead()),this,SLOT(dataReceived()));
+ int i = 0;
+ if (port == 0)
+ port = 4444;
+
+ bool bound = false;
+
+ while (!(bound = sock->bind(QString::null,QString::number(port + i))) && i < 10)
+ {
+ Out() << "Failed to bind socket to port " << (port+i) << endl;
+ i++;
+ }
+
+
+ if (!bound)
+ {
+ KMessageBox::error(0,
+ i18n("Cannot bind to udp port %1 or the 10 following ports.").arg(port));
+ }
+ else
+ {
+ port = port + i;
+ Globals::instance().getPortList().addNewPort(port,net::UDP,true);
+ }
+ }
+
+
+ UDPTrackerSocket::~UDPTrackerSocket()
+ {
+ Globals::instance().getPortList().removePort(port,net::UDP);
+ delete sock;
+ }
+
+ void UDPTrackerSocket::sendConnect(Int32 tid,const KNetwork::KSocketAddress & addr)
+ {
+ Int64 cid = 0x41727101980LL;
+ Uint8 buf[16];
+
+ WriteInt64(buf,0,cid);
+ WriteInt32(buf,8,CONNECT);
+ WriteInt32(buf,12,tid);
+
+ sock->send(KDatagramPacket((char*)buf,16,addr));
+ transactions.insert(tid,CONNECT);
+ }
+
+ void UDPTrackerSocket::sendAnnounce(Int32 tid,const Uint8* data,const KNetwork::KSocketAddress & addr)
+ {
+ transactions.insert(tid,ANNOUNCE);
+ sock->send(KDatagramPacket((char*)data,98,addr));
+ }
+
+ void UDPTrackerSocket::cancelTransaction(Int32 tid)
+ {
+ transactions.remove(tid);
+ }
+
+ void UDPTrackerSocket::handleConnect(const QByteArray & data)
+ {
+ const Uint8* buf = (const Uint8*)data.data();
+
+ // Read the transaction_id and check it
+ Int32 tid = ReadInt32(buf,4);
+ QMap<Int32,Action>::iterator i = transactions.find(tid);
+ // if we can't find the transaction, just return
+ if (i == transactions.end())
+ {
+ return;
+ }
+
+ // check wether the transaction is a CONNECT
+ if (i.data() != CONNECT)
+ {
+ transactions.erase(i);
+ error(tid,QString::null);
+ return;
+ }
+
+ // everything ok, emit signal
+ transactions.erase(i);
+ connectRecieved(tid,ReadInt64(buf,8));
+ }
+
+ void UDPTrackerSocket::handleAnnounce(const QByteArray & data)
+ {
+ const Uint8* buf = (const Uint8*)data.data();
+
+ // Read the transaction_id and check it
+ Int32 tid = ReadInt32(buf,4);
+ QMap<Int32,Action>::iterator i = transactions.find(tid);
+ // if we can't find the transaction, just return
+ if (i == transactions.end())
+ return;
+
+ // check wether the transaction is a ANNOUNCE
+ if (i.data() != ANNOUNCE)
+ {
+ transactions.erase(i);
+ error(tid,QString::null);
+ return;
+ }
+
+ // everything ok, emit signal
+ transactions.erase(i);
+ announceRecieved(tid,data);
+ }
+
+ void UDPTrackerSocket::handleError(const QByteArray & data)
+ {
+ const Uint8* buf = (const Uint8*)data.data();
+ // Read the transaction_id and check it
+ Int32 tid = ReadInt32(buf,4);
+ QMap<Int32,Action>::iterator it = transactions.find(tid);
+ // if we can't find the transaction, just return
+ if (it == transactions.end())
+ return;
+
+ // extract error message
+ transactions.erase(it);
+ QString msg;
+ for (Uint32 i = 8;i < data.size();i++)
+ msg += (char)buf[i];
+
+ // emit signal
+ error(tid,msg);
+ }
+
+ void UDPTrackerSocket::dataReceived()
+ {
+ if (sock->bytesAvailable() == 0)
+ {
+ Out(SYS_TRK|LOG_NOTICE) << "0 byte UDP packet " << endl;
+ // KDatagramSocket wrongly handles UDP packets with no payload
+ // so we need to deal with it oursleves
+ int fd = sock->socketDevice()->socket();
+ char tmp;
+ read(fd,&tmp,1);
+ return;
+ }
+
+ KDatagramPacket pck = sock->receive();
+ const QByteArray & data = pck.data();
+ const Uint8* buf = (const Uint8*)data.data();
+ Uint32 type = ReadUint32(buf,0);
+ switch (type)
+ {
+ case CONNECT:
+ handleConnect(data);
+ break;
+ case ANNOUNCE:
+ handleAnnounce(data);
+ break;
+ case ERROR:
+ handleError(data);
+ break;
+ }
+ }
+
+ Int32 UDPTrackerSocket::newTransactionID()
+ {
+ Int32 transaction_id = rand() * time(0);
+ while (transactions.contains(transaction_id))
+ transaction_id++;
+ return transaction_id;
+ }
+
+ void UDPTrackerSocket::setPort(Uint16 p)
+ {
+ port = p;
+ }
+
+ Uint16 UDPTrackerSocket::getPort()
+ {
+ return port;
+ }
+}
+
+#include "udptrackersocket.moc"
diff --git a/libktorrent/torrent/udptrackersocket.h b/libktorrent/torrent/udptrackersocket.h
new file mode 100644
index 0000000..1537598
--- /dev/null
+++ b/libktorrent/torrent/udptrackersocket.h
@@ -0,0 +1,139 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef BTUDPTRACKERSOCKET_H
+#define BTUDPTRACKERSOCKET_H
+
+#include <qobject.h>
+#include <qmap.h>
+#include <qcstring.h>
+#include <util/constants.h>
+
+
+namespace KNetwork
+{
+ class KDatagramSocket;
+ class KSocketAddress;
+}
+
+namespace bt
+{
+
+
+ enum Action
+ {
+ CONNECT = 0,
+ ANNOUNCE = 1,
+ SCRAPE = 2,
+ ERROR = 3
+ };
+
+
+
+ /**
+ * @author Joris Guisson
+ *
+ * Class which handles communication with one or more UDP trackers.
+ */
+ class UDPTrackerSocket : public QObject
+ {
+ Q_OBJECT
+ public:
+ UDPTrackerSocket();
+ virtual ~UDPTrackerSocket();
+
+ /**
+ * Send a connect message. As a response to this, the connectRecieved
+ * signal will be emitted, classes recieving this signal should check if
+ * the transaction_id is the same.
+ * @param tid The transaction_id
+ * @param addr The address to send to
+ */
+ void sendConnect(Int32 tid,const KNetwork::KSocketAddress & addr);
+
+ /**
+ * Send an announce message. As a response to this, the announceRecieved
+ * signal will be emitted, classes recieving this signal should check if
+ * the transaction_id is the same.
+ * @param tid The transaction_id
+ * @param data The data to send (connect input structure, in UDP Tracker specifaction)
+ * @param addr The address to send to
+ */
+ void sendAnnounce(Int32 tid,const Uint8* data,const KNetwork::KSocketAddress & addr);
+
+ /**
+ * If a transaction times out, this should be used to cancel it.
+ * @param tid
+ */
+ void cancelTransaction(Int32 tid);
+
+
+ /**
+ * Compute a free transaction_id.
+ * @return A free transaction_id
+ */
+ Int32 newTransactionID();
+
+ /**
+ * Set the port ot use.
+ * @param p The port
+ */
+ static void setPort(Uint16 p);
+
+ /// Get the port in use.
+ static Uint16 getPort();
+ private slots:
+ void dataReceived();
+
+ signals:
+ /**
+ * Emitted when a connect message is received.
+ * @param tid The transaction_id
+ * @param connection_id The connection_id returned
+ */
+ void connectRecieved(Int32 tid,Int64 connection_id);
+
+ /**
+ * Emitted when an announce message is received.
+ * @param tid The transaction_id
+ * @param buf The data
+ */
+ void announceRecieved(Int32 tid,const QByteArray & buf);
+
+ /**
+ * Signal emitted, when an error occurs during a transaction.
+ * @param tid The transaction_id
+ * @param error_string Potential error string
+ */
+ void error(Int32 tid,const QString & error_string);
+
+ private:
+ void handleConnect(const QByteArray & buf);
+ void handleAnnounce(const QByteArray & buf);
+ void handleError(const QByteArray & buf);
+
+ private:
+ Uint16 udp_port;
+ KNetwork::KDatagramSocket* sock;
+ QMap<Int32,Action> transactions;
+ static Uint16 port;
+ };
+}
+
+#endif
diff --git a/libktorrent/torrent/uploadcap.cpp b/libktorrent/torrent/uploadcap.cpp
new file mode 100644
index 0000000..701d854
--- /dev/null
+++ b/libktorrent/torrent/uploadcap.cpp
@@ -0,0 +1,46 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#if 0
+#include <math.h>
+#include <util/log.h>
+#include <torrent/globals.h>
+#include "uploadcap.h"
+#include "peer.h"
+#include "packetwriter.h"
+
+namespace bt
+{
+
+ UploadCap UploadCap::self;
+
+ UploadCap::UploadCap() : Cap(false)
+ {
+ }
+
+ UploadCap::~UploadCap()
+ {
+ }
+
+
+
+
+}
+#endif
+
diff --git a/libktorrent/torrent/uploadcap.h b/libktorrent/torrent/uploadcap.h
new file mode 100644
index 0000000..f766f59
--- /dev/null
+++ b/libktorrent/torrent/uploadcap.h
@@ -0,0 +1,56 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef BTUPLOADCAP_H
+#define BTUPLOADCAP_H
+
+// DEPRECATED
+#if 0
+#include "cap.h"
+
+namespace bt
+{
+ class PacketWriter;
+
+
+
+ /**
+ * @author Joris Guisson
+ * @brief Keeps the upload rate under control
+ *
+ * Before a PeerUploader can send a piece, it must first ask
+ * permission to a UploadCap object. This object will make sure
+ * that the upload rate remains under a specified threshold. When the
+ * threshold is set to 0, no upload capping will be done.
+ */
+ class UploadCap : public Cap
+ {
+ static UploadCap self;
+
+ UploadCap();
+ public:
+ virtual ~UploadCap();
+
+
+ static UploadCap & instance() {return self;}
+ };
+
+}
+#endif
+#endif
diff --git a/libktorrent/torrent/uploader.cpp b/libktorrent/torrent/uploader.cpp
new file mode 100644
index 0000000..0cf3677
--- /dev/null
+++ b/libktorrent/torrent/uploader.cpp
@@ -0,0 +1,67 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <util/log.h>
+#include "uploader.h"
+#include "peer.h"
+#include "chunkmanager.h"
+#include "request.h"
+#include "uploader.h"
+#include "peeruploader.h"
+#include "peermanager.h"
+
+
+namespace bt
+{
+
+ Uploader::Uploader(ChunkManager & cman,PeerManager & pman)
+ : cman(cman),pman(pman),uploaded(0)
+ {}
+
+
+ Uploader::~Uploader()
+ {
+ }
+
+
+
+ void Uploader::update(Uint32 opt_unchoked)
+ {
+ for (Uint32 i = 0;i < pman.getNumConnectedPeers();++i)
+ {
+ PeerUploader* p = pman.getPeer(i)->getPeerUploader();
+ uploaded += p->update(cman,opt_unchoked);
+ }
+ }
+
+
+ Uint32 Uploader::uploadRate() const
+ {
+ Uint32 rate = 0;
+ for (Uint32 i = 0;i < pman.getNumConnectedPeers();++i)
+ {
+ const Peer* p = pman.getPeer(i);
+ rate += p->getUploadRate();
+ }
+ return rate;
+ }
+
+
+}
+#include "uploader.moc"
diff --git a/libktorrent/torrent/uploader.h b/libktorrent/torrent/uploader.h
new file mode 100644
index 0000000..4370d69
--- /dev/null
+++ b/libktorrent/torrent/uploader.h
@@ -0,0 +1,75 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef BTUPLOADER_H
+#define BTUPLOADER_H
+
+#include <qobject.h>
+#include "globals.h"
+
+namespace bt
+{
+ class Peer;
+ class PeerID;
+ class ChunkManager;
+ class Request;
+ class PeerManager;
+
+
+ /**
+ * @author Joris Guisson
+ *
+ * Class which manages the uploading of data. It has a PeerUploader for
+ * each Peer.
+ */
+ class Uploader : public QObject
+ {
+ Q_OBJECT
+ public:
+ /**
+ * Constructor, sets the ChunkManager.
+ * @param cman The ChunkManager
+ */
+ Uploader(ChunkManager & cman,PeerManager & pman);
+ virtual ~Uploader();
+
+ /// Get the number of bytes uploaded.
+ Uint64 bytesUploaded() const {return uploaded;}
+
+ /// Get the upload rate of all Peers combined.
+ Uint32 uploadRate() const;
+
+ /// Set the number of bytes which have been uploaded.
+ void setBytesUploaded(Uint64 b) {uploaded = b;}
+ public slots:
+ /**
+ * Update every PeerUploader.
+ * @param opt_unchoked ID of optimisticly unchoked peer
+ */
+ void update(Uint32 opt_unchoked);
+
+ private:
+ ChunkManager & cman;
+ PeerManager & pman;
+ Uint64 uploaded;
+ };
+
+}
+
+#endif
diff --git a/libktorrent/torrent/upspeedestimater.cpp b/libktorrent/torrent/upspeedestimater.cpp
new file mode 100644
index 0000000..0d6c544
--- /dev/null
+++ b/libktorrent/torrent/upspeedestimater.cpp
@@ -0,0 +1,148 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <math.h>
+#include <util/functions.h>
+#include <util/log.h>
+#include <torrent/globals.h>
+#include "upspeedestimater.h"
+
+namespace bt
+{
+
+ UpSpeedEstimater::UpSpeedEstimater()
+ {
+ accumulated_bytes = 0;
+ upload_rate = 0.0;
+ proto_upload_rate = 0.0;
+ }
+
+
+ UpSpeedEstimater::~UpSpeedEstimater()
+ {}
+
+
+ void UpSpeedEstimater::writeBytes(Uint32 bytes,bool proto)
+ {
+ // add entry to outstanding_bytes
+ Entry e;
+ e.bytes = bytes;
+ e.data = !proto;
+ e.start_time = GetCurrentTime();
+ outstanding_bytes.append(e);
+ }
+
+ void UpSpeedEstimater::bytesWritten(Uint32 bytes)
+ {
+ QValueList<Entry>::iterator i = outstanding_bytes.begin();
+ TimeStamp now = GetCurrentTime();
+ while (bytes > 0 && i != outstanding_bytes.end())
+ {
+ Entry e = *i;
+ if (e.bytes <= bytes + accumulated_bytes)
+ {
+ // first remove outstanding bytes
+ i = outstanding_bytes.erase(i);
+ bytes -= e.bytes;
+ accumulated_bytes = 0;
+ if (e.data)
+ {
+ // if it's data move it to the written_bytes list
+ // but first store time it takes to send in e.t
+ e.duration = now - e.start_time;
+ written_bytes.append(e);
+ }
+ else
+ {
+ e.duration = now - e.start_time;
+#ifdef MEASURE_PROTO_OVERHEAD
+ proto_bytes.append(e);
+#endif
+ }
+ }
+ else
+ {
+ accumulated_bytes += bytes;
+ bytes = 0;
+ }
+ }
+ }
+
+ double UpSpeedEstimater::rate(QValueList<Entry> & el)
+ {
+ TimeStamp now = GetCurrentTime();
+ const Uint32 INTERVAL = 3000;
+
+ Uint32 tot_bytes = 0;
+ Uint32 oldest_time = now;
+
+ QValueList<Entry>::iterator i = el.begin();
+ while (i != el.end())
+ {
+ Entry & e = *i;
+ Uint32 end_time = e.start_time + e.duration;
+
+ if (now - end_time > INTERVAL)
+ {
+ // get rid of old entries
+ i = el.erase(i);
+ }
+ else if (now - e.start_time <= INTERVAL)
+ {
+ // entry was fully sent in the last 3 seconds
+ // so fully add it
+ tot_bytes += e.bytes;
+ if (e.start_time < oldest_time)
+ oldest_time = e.start_time;
+ i++;
+ }
+ else
+ {
+ // entry was partially sent in the last 3 seconds
+ // so we need to take into account a part of the bytes;
+ Uint32 part_dur = end_time - (now - INTERVAL);
+ double dur_perc = (double)part_dur / e.duration;
+ tot_bytes += (Uint32)ceil(dur_perc * e.bytes);
+ oldest_time = (now - INTERVAL);
+ i++;
+ }
+ }
+
+ return (double)tot_bytes / (INTERVAL * 0.001);
+ }
+
+ void UpSpeedEstimater::update()
+ {
+ if (!written_bytes.empty())
+ {
+ upload_rate = 0;
+ upload_rate = rate(written_bytes);
+ }
+
+
+#ifdef MEASURE_PROTO_OVERHEAD
+ if (!proto_bytes.empty())
+ {
+ proto_upload_rate = 0;
+ proto_upload_rate = rate(proto_bytes);
+ }
+#endif
+ }
+
+}
diff --git a/libktorrent/torrent/upspeedestimater.h b/libktorrent/torrent/upspeedestimater.h
new file mode 100644
index 0000000..6503499
--- /dev/null
+++ b/libktorrent/torrent/upspeedestimater.h
@@ -0,0 +1,86 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef BTUPSPEEDESTIMATER_H
+#define BTUPSPEEDESTIMATER_H
+
+#include <qvaluelist.h>
+#include <util/constants.h>
+
+namespace bt
+{
+
+ /**
+ * @author Joris Guisson
+ *
+ * Measures upload speed.
+ */
+ class UpSpeedEstimater
+ {
+ struct Entry
+ {
+ Uint32 bytes;
+ TimeStamp start_time;
+ Uint32 duration;
+ bool data;
+ };
+ public:
+ UpSpeedEstimater();
+ virtual ~UpSpeedEstimater();
+
+ /**
+ * Start sending bytes.
+ * @param bytes The number of bytes
+ * @param rec Wether to record or not (i.e. is this data)
+ */
+ void writeBytes(Uint32 bytes,bool rec);
+
+ /**
+ * The socket has finished sending bytes.
+ * @param bytes The number of bytes.
+ */
+ void bytesWritten(Uint32 bytes);
+
+ /**
+ * Update the upload speed estimater.
+ */
+ void update();
+
+ /// Get the upload rate
+ double uploadRate() const {return upload_rate;}
+
+ /// Get the protocol overhead
+ double protocollOverhead() const {return proto_upload_rate;}
+ private:
+ double rate(QValueList<Entry> & el);
+
+ private:
+ double upload_rate;
+ double proto_upload_rate;
+ Uint32 accumulated_bytes;
+ QValueList<Entry> outstanding_bytes;
+ QValueList<Entry> written_bytes;
+#ifdef MEASURE_PROTO_OVERHEAD
+ QValueList<Entry> proto_bytes;
+#endif
+ };
+
+}
+
+#endif
diff --git a/libktorrent/torrent/utpex.cpp b/libktorrent/torrent/utpex.cpp
new file mode 100644
index 0000000..4933218
--- /dev/null
+++ b/libktorrent/torrent/utpex.cpp
@@ -0,0 +1,155 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <net/address.h>
+#include <util/functions.h>
+#include <util/log.h>
+#include "utpex.h"
+#include "peer.h"
+#include "packetwriter.h"
+#include "bdecoder.h"
+#include "bencoder.h"
+#include "bnode.h"
+#include "peermanager.h"
+
+
+namespace bt
+{
+
+ UTPex::UTPex(Peer* peer,Uint32 id) : peer(peer),id(id),last_updated(0)
+ {}
+
+
+ UTPex::~UTPex()
+ {}
+
+
+
+ void UTPex::handlePexPacket(const Uint8* packet,Uint32 size)
+ {
+ if (size <= 2 || packet[1] != 1)
+ return;
+
+ QByteArray tmp;
+ tmp.setRawData((const char*)packet,size);
+ BNode* node = 0;
+ try
+ {
+ BDecoder dec(tmp,false,2);
+ node = dec.decode();
+ if (node && node->getType() == BNode::DICT)
+ {
+ BDictNode* dict = (BDictNode*)node;
+
+ // ut_pex packet, emit signal to notify PeerManager
+ BValueNode* val = dict->getValue("added");
+ if (val)
+ {
+ QByteArray data = val->data().toByteArray();
+ peer->emitPex(data);
+ }
+ }
+ }
+ catch (...)
+ {
+ // just ignore invalid packets
+ Out(SYS_CON|LOG_DEBUG) << "Invalid extended packet" << endl;
+ }
+ delete node;
+ tmp.resetRawData((const char*)packet,size);
+ }
+
+ bool UTPex::needsUpdate() const
+ {
+ return bt::GetCurrentTime() - last_updated >= 60*1000;
+ }
+
+ void UTPex::update(PeerManager* pman)
+ {
+ last_updated = bt::GetCurrentTime();
+
+ std::map<Uint32,net::Address> added;
+ std::map<Uint32,net::Address> npeers;
+
+ PeerManager::CItr itr = pman->beginPeerList();
+ while (itr != pman->endPeerList())
+ {
+ const Peer* p = *itr;
+ if (p != peer)
+ {
+ npeers.insert(std::make_pair(p->getID(),p->getAddress()));
+ if (peers.count(p->getID()) == 0)
+ {
+ // new one, add to added
+ added.insert(std::make_pair(p->getID(),p->getAddress()));
+ }
+ else
+ {
+ // erase from old list, so only the dropped ones are left
+ peers.erase(p->getID());
+ }
+ }
+ itr++;
+ }
+
+ if (!(peers.size() == 0 && added.size() == 0))
+ {
+ // encode the whole lot
+ QByteArray data;
+ BEncoder enc(new BEncoderBufferOutput(data));
+ enc.beginDict();
+ enc.write("added");
+ encode(enc,added);
+ enc.write("added.f"); // no idea what this added.f thing means
+ enc.write("");
+ enc.write("dropped");
+ encode(enc,peers);
+ enc.end();
+
+ peer->getPacketWriter().sendExtProtMsg(id,data);
+ }
+
+ peers = npeers;
+ }
+
+ void UTPex::encode(BEncoder & enc,const std::map<Uint32,net::Address> & ps)
+ {
+ if (ps.size() == 0)
+ {
+ enc.write("");
+ return;
+ }
+
+ Uint8* buf = new Uint8[ps.size() * 6];
+ Uint32 size = 0;
+
+ std::map<Uint32,net::Address>::const_iterator i = ps.begin();
+ while (i != ps.end())
+ {
+ const net::Address & addr = i->second;
+ WriteUint32(buf,size,addr.ip());
+ WriteUint16(buf,size + 4,addr.port());
+ size += 6;
+ i++;
+ }
+
+ enc.write(buf,size);
+ delete [] buf;
+ }
+}
diff --git a/libktorrent/torrent/utpex.h b/libktorrent/torrent/utpex.h
new file mode 100644
index 0000000..cab2f39
--- /dev/null
+++ b/libktorrent/torrent/utpex.h
@@ -0,0 +1,71 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef BTUTPEX_H
+#define BTUTPEX_H
+
+#include <map>
+#include <net/address.h>
+#include <util/constants.h>
+
+namespace bt
+{
+ class Peer;
+ class PeerManager;
+ class BEncoder;
+
+ /**
+ * @author Joris Guisson <joris.guisson@gmail.com>
+ *
+ * Class which handles µTorrent's peer exchange
+ */
+ class UTPex
+ {
+ public:
+ UTPex(Peer* peer,Uint32 id);
+ virtual ~UTPex();
+
+ /**
+ * Handle a PEX packet
+ * @param packet The packet
+ * @param size The size of the packet
+ */
+ void handlePexPacket(const Uint8* packet,Uint32 size);
+
+ /// Do we need to update PEX (should happen every minute)
+ bool needsUpdate() const;
+
+ /// Send a new PEX packet to the Peer
+ void update(PeerManager* pman);
+
+ /// Change the ID used in the extended packets
+ void changeID(Uint32 nid) {id = nid;}
+ private:
+ void encode(BEncoder & enc,const std::map<Uint32,net::Address> & ps);
+
+ private:
+ Peer* peer;
+ Uint32 id;
+ std::map<Uint32,net::Address> peers;
+ TimeStamp last_updated;
+ };
+
+}
+
+#endif
diff --git a/libktorrent/torrent/value.cpp b/libktorrent/torrent/value.cpp
new file mode 100644
index 0000000..df063ab
--- /dev/null
+++ b/libktorrent/torrent/value.cpp
@@ -0,0 +1,91 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <qtextcodec.h>
+#include "value.h"
+
+namespace bt
+{
+
+ Value::Value() : type(INT),ival(0),big_ival(0)
+ {}
+
+ Value::Value(int val) : type(INT),ival(val),big_ival(val)
+ {}
+
+ Value::Value(Int64 val) : type(INT64),big_ival(val)
+ {}
+
+ Value::Value(const QByteArray & val) : type(STRING),ival(0),strval(val),big_ival(0)
+ {}
+
+ Value::Value(const Value & val)
+ : type(val.type),ival(val.ival),strval(val.strval),big_ival(val.big_ival)
+ {}
+
+ Value::~Value()
+ {}
+
+
+ QString Value::toString(const QString & encoding) const
+ {
+ if (encoding.isNull() || encoding.isEmpty())
+ return toString();
+
+ QTextCodec* tc = QTextCodec::codecForName(encoding.ascii());
+ if (!tc)
+ return toString();
+
+ return tc->toUnicode(strval);
+ }
+
+
+ Value & Value::operator = (const Value & val)
+ {
+ type = val.type;
+ ival = val.ival;
+ strval = val.strval;
+ big_ival = val.big_ival;
+ return *this;
+ }
+
+ Value & Value::operator = (Int32 val)
+ {
+ type = INT;
+ ival = val;
+ big_ival = val;
+ return *this;
+ }
+
+ Value & Value::operator = (Int64 val)
+ {
+ type = INT64;
+ big_ival = val;
+ return *this;
+ }
+
+ Value & Value::operator = (const QByteArray & val)
+ {
+ type = STRING;
+ strval = val;
+ big_ival = 0;
+ return *this;
+ }
+
+}
diff --git a/libktorrent/torrent/value.h b/libktorrent/torrent/value.h
new file mode 100644
index 0000000..cd7c879
--- /dev/null
+++ b/libktorrent/torrent/value.h
@@ -0,0 +1,67 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef BTVALUE_H
+#define BTVALUE_H
+
+#include <qstring.h>
+#include <util/constants.h>
+
+namespace bt
+{
+
+ /**
+ @author Joris Guisson
+ */
+ class Value
+ {
+ public:
+ enum Type
+ {
+ STRING,INT,INT64
+ };
+
+
+ Value();
+ Value(int val);
+ Value(Int64 val);
+ Value(const QByteArray & val);
+ Value(const Value & val);
+ ~Value();
+
+ Value & operator = (const Value & val);
+ Value & operator = (Int32 val);
+ Value & operator = (Int64 val);
+ Value & operator = (const QByteArray & val);
+
+ Type getType() const {return type;}
+ Int32 toInt() const {return ival;}
+ Int64 toInt64() const {return big_ival;}
+ QString toString() const {return QString(strval);}
+ QString toString(const QString & encoding) const;
+ QByteArray toByteArray() const {return strval;}
+ private:
+ Type type;
+ Int32 ival;
+ QByteArray strval;
+ Int64 big_ival;
+ };
+}
+
+#endif
diff --git a/libktorrent/util/Makefile.am b/libktorrent/util/Makefile.am
new file mode 100644
index 0000000..c7f46ae
--- /dev/null
+++ b/libktorrent/util/Makefile.am
@@ -0,0 +1,18 @@
+INCLUDES = -I$(srcdir)/../../libktorrent $(all_includes)
+
+METASOURCES = AUTO
+
+noinst_LTLIBRARIES = libutil.la
+libutil_la_LDFLAGS = $(all_libraries)
+
+
+libutil_la_SOURCES = array.cpp autorotatelogjob.cpp bitset.cpp error.cpp \
+ file.cpp fileops.cpp functions.cpp httprequest.cpp log.cpp mmapfile.cpp \
+ profiler.cpp ptrmap.cpp sha1hash.cpp sha1hashgen.cpp timer.cpp urlencoder.cpp \
+ waitjob.cpp
+
+
+
+
+KDE_CXXFLAGS = $(USE_EXCEPTIONS) $(USE_RTTI)
+noinst_HEADERS = autorotatelogjob.h profiler.h
diff --git a/libktorrent/util/array.cpp b/libktorrent/util/array.cpp
new file mode 100644
index 0000000..e57091d
--- /dev/null
+++ b/libktorrent/util/array.cpp
@@ -0,0 +1,27 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include "array.h"
+
+namespace bt
+{
+
+
+
+}
diff --git a/libktorrent/util/array.h b/libktorrent/util/array.h
new file mode 100644
index 0000000..1694e2a
--- /dev/null
+++ b/libktorrent/util/array.h
@@ -0,0 +1,73 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef BTARRAY_H
+#define BTARRAY_H
+
+#include "constants.h"
+
+namespace bt
+{
+
+ /**
+ * @author Joris Guisson
+ *
+ * Template array classes, makes creating dynamic buffers easier
+ * and safer.
+ */
+ template<class T>
+ class Array
+ {
+ Uint32 num;
+ T* data;
+ public:
+ Array(Uint32 num = 0) : num(num),data(0)
+ {
+ if (num > 0)
+ data = new T[num];
+ }
+
+ ~Array()
+ {
+ delete [] data;
+ }
+
+ T & operator [] (Uint32 i) {return data[i];}
+ const T & operator [] (Uint32 i) const {return data[i];}
+
+ operator const T* () const {return data;}
+ operator T* () {return data;}
+
+ /// Get the number of elements in the array
+ Uint32 size() const {return num;}
+
+ /**
+ * Fill the array with a value
+ * @param val The value
+ */
+ void fill(T val)
+ {
+ for (Uint32 i = 0;i < num;i++)
+ data[i] = val;
+ }
+ };
+
+}
+
+#endif
diff --git a/libktorrent/util/autorotatelogjob.cpp b/libktorrent/util/autorotatelogjob.cpp
new file mode 100644
index 0000000..c43e304
--- /dev/null
+++ b/libktorrent/util/autorotatelogjob.cpp
@@ -0,0 +1,88 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <kurl.h>
+#include <kprocess.h>
+#include <util/fileops.h>
+#include "autorotatelogjob.h"
+#include "log.h"
+
+namespace bt
+{
+
+ AutoRotateLogJob::AutoRotateLogJob(const QString & file,Log* lg)
+ : KIO::Job(false),file(file),cnt(10),lg(lg)
+ {
+ update();
+ }
+
+
+ AutoRotateLogJob::~AutoRotateLogJob()
+ {}
+
+ void AutoRotateLogJob::kill(bool)
+ {
+ m_error = 0;
+ emitResult();
+ }
+
+ void AutoRotateLogJob::update()
+ {
+ while (cnt > 1)
+ {
+ QString prev = QString("%1-%2.gz").arg(file).arg(cnt - 1);
+ QString curr = QString("%1-%2.gz").arg(file).arg(cnt);
+ if (bt::Exists(prev)) // if file exists start the move job
+ {
+ KIO::Job* sj = KIO::file_move(KURL::fromPathOrURL(prev),KURL::fromPathOrURL(curr),-1,true,false,false);
+ connect(sj,SIGNAL(result(KIO::Job*)),this,SLOT(moveJobDone(KIO::Job* )));
+ return;
+ }
+ else
+ {
+ cnt--;
+ }
+ }
+
+ if (cnt == 1)
+ {
+ // move current log to 1 and zip it
+ bt::Move(file,file + "-1",true);
+ KIO::Job* sj = KIO::file_move(KURL::fromPathOrURL(file),KURL::fromPathOrURL(file + "-1"),-1,true,false,false);
+ connect(sj,SIGNAL(result(KIO::Job*)),this,SLOT(moveJobDone(KIO::Job* )));
+ }
+ else
+ {
+ // final log file is moved, now zip it and end the job
+ std::system(QString("gzip " + KProcess::quote(file + "-1")).local8Bit());
+ m_error = 0;
+ lg->logRotateDone();
+ emitResult();
+ }
+ }
+
+
+ void AutoRotateLogJob::moveJobDone(KIO::Job*)
+ {
+ cnt--; // decrease counter so the newt file will be moved in update
+ update(); // don't care about result of job
+ }
+
+}
+#include "autorotatelogjob.moc"
diff --git a/libktorrent/util/autorotatelogjob.h b/libktorrent/util/autorotatelogjob.h
new file mode 100644
index 0000000..11cf06a
--- /dev/null
+++ b/libktorrent/util/autorotatelogjob.h
@@ -0,0 +1,59 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef BTAUTOROTATELOGJOB_H
+#define BTAUTOROTATELOGJOB_H
+
+#include <kio/job.h>
+#include <cstdlib>
+
+namespace bt
+{
+ class Log;
+
+ /**
+ @author Joris Guisson <joris.guisson@gmail.com>
+
+ Job which handles the rotation of the log file.
+ This Job must do several move jobs which must be done sequentially.
+ */
+ class AutoRotateLogJob : public KIO::Job
+ {
+ Q_OBJECT
+ public:
+ AutoRotateLogJob(const QString & file,Log* lg);
+ virtual ~AutoRotateLogJob();
+
+ virtual void kill(bool quietly=true);
+
+ private slots:
+ void moveJobDone(KIO::Job*);
+
+ private:
+ void update();
+
+ private:
+ QString file;
+ int cnt;
+ Log* lg;
+ };
+
+}
+
+#endif
diff --git a/libktorrent/util/bitset.cpp b/libktorrent/util/bitset.cpp
new file mode 100644
index 0000000..6139e01
--- /dev/null
+++ b/libktorrent/util/bitset.cpp
@@ -0,0 +1,111 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <algorithm>
+#include "bitset.h"
+#include <string.h>
+
+namespace bt
+{
+ BitSet BitSet::null;
+
+ BitSet::BitSet(Uint32 num_bits) : num_bits(num_bits),data(0)
+ {
+ num_bytes = (num_bits / 8) + ((num_bits % 8 > 0) ? 1 : 0);
+ data = new Uint8[num_bytes];
+ std::fill(data,data+num_bytes,0x00);
+ num_on = 0;
+ }
+
+ BitSet::BitSet(const Uint8* d,Uint32 num_bits) : num_bits(num_bits),data(0)
+ {
+ num_bytes = (num_bits / 8) + ((num_bits % 8 > 0) ? 1 : 0);
+ data = new Uint8[num_bytes];
+ memcpy(data,d,num_bytes);
+ num_on = 0;
+ Uint32 i = 0;
+ while (i < num_bits)
+ {
+ if (get(i))
+ num_on++;
+ i++;
+ }
+ }
+
+ BitSet::BitSet(const BitSet & bs) : num_bits(bs.num_bits),num_bytes(bs.num_bytes),data(0),num_on(bs.num_on)
+ {
+ data = new Uint8[num_bytes];
+ std::copy(bs.data,bs.data+num_bytes,data);
+ }
+
+ BitSet::~BitSet()
+ {
+ delete [] data;
+ }
+
+
+
+ BitSet & BitSet::operator = (const BitSet & bs)
+ {
+ if (data)
+ delete [] data;
+ num_bytes = bs.num_bytes;
+ num_bits = bs.num_bits;
+ data = new Uint8[num_bytes];
+ std::copy(bs.data,bs.data+num_bytes,data);
+ num_on = bs.num_on;
+ return *this;
+ }
+
+ void BitSet::setAll(bool on)
+ {
+ std::fill(data,data+num_bytes,on ? 0xFF : 0x00);
+ num_on = on ? num_bits : 0;
+ }
+
+ void BitSet::clear()
+ {
+ setAll(false);
+ }
+
+ void BitSet::orBitSet(const BitSet & other)
+ {
+ Uint32 i = 0;
+ while (i < num_bits)
+ {
+ bool val = get(i) || other.get(i);
+ set(i,val);
+ i++;
+ }
+ }
+
+ bool BitSet::allOn() const
+ {
+ return num_on == num_bits;
+ }
+
+ bool BitSet::operator == (const BitSet & bs)
+ {
+ if (this->getNumBits() != bs.getNumBits())
+ return false;
+
+ return memcmp(data,bs.data,num_bytes) == 0;
+ }
+}
+
diff --git a/libktorrent/util/bitset.h b/libktorrent/util/bitset.h
new file mode 100644
index 0000000..32e7e48
--- /dev/null
+++ b/libktorrent/util/bitset.h
@@ -0,0 +1,157 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef BTBITSET_H
+#define BTBITSET_H
+
+#include "constants.h"
+
+namespace bt
+{
+
+ /**
+ * @author Joris Guisson
+ * @brief Simple implementation of a BitSet
+ *
+ * Simple implementation of a BitSet, can only turn on and off bits.
+ * BitSet's are used to indicate which chunks we have or not.
+ */
+ class BitSet
+ {
+ Uint32 num_bits,num_bytes;
+ Uint8* data;
+ Uint32 num_on;
+ public:
+ /**
+ * Constructor.
+ * @param num_bits The number of bits
+ */
+ BitSet(Uint32 num_bits = 8);
+
+ /**
+ * Manually set data.
+ * @param data The data
+ * @param num_bits The number of bits
+ */
+ BitSet(const Uint8* data,Uint32 num_bits);
+
+ /**
+ * Copy constructor.
+ * @param bs BitSet to copy
+ * @return
+ */
+ BitSet(const BitSet & bs);
+ virtual ~BitSet();
+
+ /// See if the BitSet is null
+ bool isNull() const {return num_bits == 0;}
+
+ /**
+ * Get the value of a bit, false means 0, true 1.
+ * @param i Index of Bit
+ */
+ bool get(Uint32 i) const;
+
+ /**
+ * Set the value of a bit, false means 0, true 1.
+ * @param i Index of Bit
+ * @param on False means 0, true 1
+ */
+ void set(Uint32 i,bool on);
+
+ /// Set all bits on or off
+ void setAll(bool on);
+
+ Uint32 getNumBytes() const {return num_bytes;}
+ Uint32 getNumBits() const {return num_bits;}
+ const Uint8* getData() const {return data;}
+ Uint8* getData() {return data;}
+
+ /// Get the number of on bits
+ Uint32 numOnBits() const {return num_on;}
+
+ /**
+ * Set all bits to 0
+ */
+ void clear();
+
+ /**
+ * or this BitSet with another.
+ * @param other The other BitSet
+ */
+ void orBitSet(const BitSet & other);
+
+ /**
+ * Assignment operator.
+ * @param bs BitSet to copy
+ * @return *this
+ */
+ BitSet & operator = (const BitSet & bs);
+
+ /// Check if all bit are set to 1
+ bool allOn() const;
+
+ /**
+ * Check for equality of bitsets
+ * @param bs BitSet to compare
+ * @return true if equal
+ */
+ bool operator == (const BitSet & bs);
+
+ /**
+ * Opposite of operator ==
+ */
+ bool operator != (const BitSet & bs) {return ! operator == (bs);}
+
+ static BitSet null;
+ };
+
+ inline bool BitSet::get(Uint32 i) const
+ {
+ if (i >= num_bits)
+ return false;
+
+ Uint32 byte = i / 8;
+ Uint32 bit = i % 8;
+ Uint8 b = data[byte] & (0x01 << (7 - bit));
+ return b != 0x00;
+ }
+
+ inline void BitSet::set(Uint32 i,bool on)
+ {
+ if (i >= num_bits)
+ return;
+
+ Uint32 byte = i / 8;
+ Uint32 bit = i % 8;
+ if (on && !get(i))
+ {
+ num_on++;
+ data[byte] |= (0x01 << (7 - bit));
+ }
+ else if (!on && get(i))
+ {
+ num_on--;
+ Uint8 b = (0x01 << (7 - bit));
+ data[byte] &= (~b);
+ }
+ }
+}
+
+#endif
diff --git a/libktorrent/util/constants.h b/libktorrent/util/constants.h
new file mode 100644
index 0000000..e663978
--- /dev/null
+++ b/libktorrent/util/constants.h
@@ -0,0 +1,96 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef BTCONSTANTS_H
+#define BTCONSTANTS_H
+
+#include <qglobal.h>
+
+namespace bt
+{
+ typedef Q_UINT64 Uint64;
+ typedef Q_UINT32 Uint32;
+ typedef Q_UINT16 Uint16;
+ typedef Q_UINT8 Uint8;
+
+ typedef Q_INT64 Int64;
+ typedef Q_INT32 Int32;
+ typedef Q_INT16 Int16;
+ typedef Q_INT8 Int8;
+
+ typedef Uint64 TimeStamp;
+
+ typedef enum
+ {
+ /* These are the old values, for compatability reasons with old chunk_info files we leave them here :
+ PREVIEW_PRIORITY = 4,
+ FIRST_PRIORITY = 3,
+ NORMAL_PRIORITY = 2,
+ LAST_PRIORITY = 1,
+ EXCLUDED = 0,
+ ONLY_SEED_PRIORITY = -1
+ */
+ // make sure new values are different from old values
+ // also leave some room if we want to add new priorities in the future
+ PREVIEW_PRIORITY = 60,
+ FIRST_PRIORITY = 50,
+ NORMAL_PRIORITY = 40,
+ LAST_PRIORITY = 30,
+ ONLY_SEED_PRIORITY = 20,
+ EXCLUDED = 10
+ }Priority;
+
+ enum ConfirmationResult
+ {
+ KEEP_DATA,
+ THROW_AWAY_DATA,
+ CANCELED
+ };
+
+ const Uint32 MAX_MSGLEN = 9 + 131072;
+ const Uint16 MIN_PORT = 6881;
+ const Uint16 MAX_PORT = 6889;
+ const Uint32 MAX_PIECE_LEN = 16384;
+
+ const Uint8 CHOKE = 0;
+ const Uint8 UNCHOKE = 1;
+ const Uint8 INTERESTED = 2;
+ const Uint8 NOT_INTERESTED = 3;
+ const Uint8 HAVE = 4;
+ const Uint8 BITFIELD = 5;
+ const Uint8 REQUEST = 6;
+ const Uint8 PIECE = 7;
+ const Uint8 CANCEL = 8;
+ const Uint8 PORT = 9;
+ const Uint8 SUGGEST_PIECE = 13;
+ const Uint8 HAVE_ALL = 14;
+ const Uint8 HAVE_NONE = 15;
+ const Uint8 REJECT_REQUEST = 16;
+ const Uint8 ALLOWED_FAST = 17;
+ const Uint8 EXTENDED = 20; // extension protocol message
+
+
+ // flags for things which a peer supports
+ const Uint32 DHT_SUPPORT = 0x01;
+ const Uint32 EXT_PROT_SUPPORT = 0x10;
+ const Uint32 FAST_EXT_SUPPORT = 0x04;
+}
+
+
+#endif
diff --git a/libktorrent/util/error.cpp b/libktorrent/util/error.cpp
new file mode 100644
index 0000000..bb981db
--- /dev/null
+++ b/libktorrent/util/error.cpp
@@ -0,0 +1,33 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include "error.h"
+
+namespace bt
+{
+
+ Error::Error(const QString & msg) : msg(msg)
+ {}
+
+
+ Error::~Error()
+ {}
+
+
+}
diff --git a/libktorrent/util/error.h b/libktorrent/util/error.h
new file mode 100644
index 0000000..8b089e4
--- /dev/null
+++ b/libktorrent/util/error.h
@@ -0,0 +1,44 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef BTERROR_H
+#define BTERROR_H
+
+#include <qstring.h>
+
+namespace bt
+{
+
+ /**
+ @author Joris Guisson
+ */
+ class Error
+ {
+ QString msg;
+ public:
+ Error(const QString & msg);
+ virtual ~Error();
+
+ QString toString() const {return msg;}
+
+ };
+
+}
+
+#endif
diff --git a/libktorrent/util/file.cpp b/libktorrent/util/file.cpp
new file mode 100644
index 0000000..b898e07
--- /dev/null
+++ b/libktorrent/util/file.cpp
@@ -0,0 +1,150 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <qfile.h>
+#include "config.h"
+#include <klocale.h>
+#include <string.h>
+#include <errno.h>
+#include <stdio.h>
+#include <torrent/globals.h>
+#include "file.h"
+#include "error.h"
+#include "log.h"
+
+namespace bt
+{
+
+ File::File() : fptr(0)
+ {}
+
+
+ File::~File()
+ {
+ close();
+ }
+
+ bool File::open(const QString & file,const QString & mode)
+ {
+ this->file = file;
+ if (fptr)
+ close();
+#if HAVE_FOPEN64
+ fptr = fopen64(QFile::encodeName(file),mode.ascii());
+#else
+ fptr = fopen(QFile::encodeName(file),mode.ascii());
+#endif
+ return fptr != 0;
+ }
+
+ void File::close()
+ {
+ if (fptr)
+ {
+ fclose(fptr);
+ fptr = 0;
+ }
+ }
+
+ void File::flush()
+ {
+ if (fptr)
+ fflush(fptr);
+ }
+
+ Uint32 File::write(const void* buf,Uint32 size)
+ {
+ if (!fptr)
+ return 0;
+
+ Uint32 ret = fwrite(buf,1,size,fptr);
+ if (ret != size)
+ {
+ if (errno == ENOSPC)
+ Out() << "Disk full !" << endl;
+
+ throw Error(i18n("Cannot write to %1 : %2").arg(file).arg(strerror(errno)));
+ }
+ return ret;
+ }
+
+ Uint32 File::read(void* buf,Uint32 size)
+ {
+ if (!fptr)
+ return 0;
+
+ Uint32 ret = fread(buf,1,size,fptr);
+ if (ferror(fptr))
+ {
+ clearerr(fptr);
+ throw Error(i18n("Cannot read from %1").arg(file));
+ }
+ return ret;
+ }
+
+ Uint64 File::seek(SeekPos from,Int64 num)
+ {
+ // printf("sizeof(off_t) = %i\n",sizeof(__off64_t));
+ if (!fptr)
+ return 0;
+
+ int p = SEEK_CUR; // use a default to prevent compiler warning
+ switch (from)
+ {
+ case BEGIN : p = SEEK_SET; break;
+ case END : p = SEEK_END; break;
+ case CURRENT : p = SEEK_CUR; break;
+ default:
+ break;
+ }
+#if HAVE_FSEEKO64
+ fseeko64(fptr,num,p);
+ return ftello64(fptr);
+#else
+ fseeko(fptr,num,p);
+ return ftello(fptr);
+#endif
+ }
+
+ bool File::eof() const
+ {
+ if (!fptr)
+ return true;
+
+ return feof(fptr) != 0;
+ }
+
+ Uint64 File::tell() const
+ {
+ if (!fptr)
+ return 0;
+
+ return ftello(fptr);
+ }
+
+ QString File::errorString() const
+ {
+ return QString(strerror(errno));
+ }
+}
diff --git a/libktorrent/util/file.h b/libktorrent/util/file.h
new file mode 100644
index 0000000..323a3a7
--- /dev/null
+++ b/libktorrent/util/file.h
@@ -0,0 +1,114 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef BTFILE_H
+#define BTFILE_H
+
+#include <stdio.h>
+#include <qstring.h>
+#include "constants.h"
+
+namespace bt
+{
+
+ /**
+ * @author Joris Guisson
+ * @brief Wrapper class for stdio's FILE
+ *
+ * Wrapper class for stdio's FILE.
+ */
+ class File
+ {
+ FILE* fptr;
+ QString file;
+ public:
+ /**
+ * Constructor.
+ */
+ File();
+
+ /**
+ * Destructor, closes the file.
+ */
+ virtual ~File();
+
+ /**
+ * Open the file similar to fopen
+ * @param file Filename
+ * @param mode Mode
+ * @return true upon succes
+ */
+ bool open(const QString & file,const QString & mode);
+
+ /**
+ * Close the file.
+ */
+ void close();
+
+ /**
+ * Flush the file.
+ */
+ void flush();
+
+ /**
+ * Write a bunch of data. If anything goes wrong
+ * an Error will be thrown.
+ * @param buf The data
+ * @param size Size of the data
+ * @return The number of bytes written
+ */
+ Uint32 write(const void* buf,Uint32 size);
+
+ /**
+ * Read a bunch of data. If anything goes wrong
+ * an Error will be thrown.
+ * @param buf The buffer to store the data
+ * @param size Size of the buffer
+ * @return The number of bytes read
+ */
+ Uint32 read(void* buf,Uint32 size);
+
+ enum SeekPos
+ {
+ BEGIN,
+ END,
+ CURRENT
+ };
+
+ /**
+ * Seek in the file.
+ * @param from Position to seek from
+ * @param num Number of bytes to move
+ * @return New position
+ */
+ Uint64 seek(SeekPos from,Int64 num);
+
+ /// Check to see if we are at the end of the file.
+ bool eof() const;
+
+ /// Get the current position in the file.
+ Uint64 tell() const;
+
+ /// Get the error string.
+ QString errorString() const;
+ };
+
+}
+
+#endif
diff --git a/libktorrent/util/fileops.cpp b/libktorrent/util/fileops.cpp
new file mode 100644
index 0000000..3fcf03d
--- /dev/null
+++ b/libktorrent/util/fileops.cpp
@@ -0,0 +1,466 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <klocale.h>
+#include <kio/netaccess.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <qdir.h>
+#include <qfile.h>
+#include <qstringlist.h>
+#include "fileops.h"
+#include "error.h"
+#include "log.h"
+#include <torrent/globals.h>
+#include "file.h"
+#include "array.h"
+
+#ifdef HAVE_XFS_XFS_H
+
+#if !defined(HAVE___S64) || !defined(HAVE___U64)
+#include <stdint.h>
+#endif
+
+#ifndef HAVE___U64
+typedef uint64_t __u64;
+#endif
+
+#ifndef HAVE___S64
+typedef int64_t __s64;
+#endif
+
+#include <xfs/xfs.h>
+#endif
+
+#ifndef O_LARGEFILE
+#define O_LARGEFILE 0
+#endif
+
+#if HAVE_STATVFS
+#include <sys/statvfs.h>
+#else
+#include <sys/param.h>
+#include <sys/mount.h>
+#endif
+
+namespace bt
+{
+ void MakeDir(const QString & dir,bool nothrow)
+ {
+ if (mkdir(QFile::encodeName(dir),0777) < -1)
+ {
+ if (!nothrow)
+ throw Error(i18n("Cannot create directory %1: %2")
+ .arg(dir).arg(strerror(errno)));
+ else
+ {
+ Out() << QString("Error : Cannot create directory %1 : %2").arg(dir).arg(strerror(errno))<< endl;
+ }
+ }
+ }
+
+ void SymLink(const QString & link_to,const QString & link_url,bool nothrow)
+ {
+ if (symlink(QFile::encodeName(link_to),QFile::encodeName(link_url)) != 0)
+ {
+ if (!nothrow)
+ throw Error(i18n("Cannot symlink %1 to %2: %3")
+ .arg(link_url.utf8()).arg(link_to.utf8())
+ .arg(strerror(errno)));
+ else
+ Out() << QString("Error : Cannot symlink %1 to %2: %3")
+ .arg(link_url.utf8()).arg(link_to.utf8())
+ .arg(strerror(errno)) << endl;
+ }
+ }
+
+ void Move(const QString & src,const QString & dst,bool nothrow)
+ {
+ // Out() << "Moving " << src << " -> " << dst << endl;
+ if (!KIO::NetAccess::move(KURL::fromPathOrURL(src),KURL::fromPathOrURL(dst),0))
+ {
+ if (!nothrow)
+ throw Error(i18n("Cannot move %1 to %2: %3")
+ .arg(src).arg(dst)
+ .arg(KIO::NetAccess::lastErrorString()));
+ else
+ Out() << QString("Error : Cannot move %1 to %2: %3")
+ .arg(src).arg(dst)
+ .arg(KIO::NetAccess::lastErrorString()) << endl;
+
+ }
+ }
+
+ void CopyFile(const QString & src,const QString & dst,bool nothrow)
+ {
+ if (!KIO::NetAccess::file_copy(KURL::fromPathOrURL(src),KURL::fromPathOrURL(dst)))
+ {
+ if (!nothrow)
+ throw Error(i18n("Cannot copy %1 to %2: %3")
+ .arg(src).arg(dst)
+ .arg(KIO::NetAccess::lastErrorString()));
+ else
+ Out() << QString("Error : Cannot copy %1 to %2: %3")
+ .arg(src).arg(dst)
+ .arg(KIO::NetAccess::lastErrorString()) << endl;
+
+ }
+ }
+
+ void CopyDir(const QString & src,const QString & dst,bool nothrow)
+ {
+ if (!KIO::NetAccess::dircopy(KURL::fromPathOrURL(src),KURL::fromPathOrURL(dst),0))
+ {
+ if (!nothrow)
+ throw Error(i18n("Cannot copy %1 to %2: %3")
+ .arg(src).arg(dst)
+ .arg(KIO::NetAccess::lastErrorString()));
+ else
+ Out() << QString("Error : Cannot copy %1 to %2: %3")
+ .arg(src).arg(dst)
+ .arg(KIO::NetAccess::lastErrorString()) << endl;
+
+ }
+ }
+
+ bool Exists(const QString & url)
+ {
+ // Out() << "Testing if " << url << " exists " << endl;
+ if (access(QFile::encodeName(url),F_OK) < 0)
+ {
+ // Out() << "No " << endl;
+ return false;
+ }
+ else
+ {
+ // Out() << "Yes " << endl;
+ return true;
+ }
+ }
+
+ static bool DelDir(const QString & fn)
+ {
+ QDir d(fn);
+ QStringList subdirs = d.entryList(QDir::Dirs);
+
+ for (QStringList::iterator i = subdirs.begin(); i != subdirs.end();i++)
+ {
+ QString entry = *i;
+
+ if (entry == ".." || entry == ".")
+ continue;
+
+ if (!DelDir(d.absFilePath(entry)))
+ {
+ Out(SYS_GEN|LOG_DEBUG) << "Delete of " << fn << "/" << entry << " failed !" << endl;
+ return false;
+ }
+ }
+
+ QStringList files = d.entryList(QDir::Files | QDir::System | QDir::Hidden);
+ for (QStringList::iterator i = files.begin(); i != files.end();i++)
+ {
+ QString entry = *i;
+
+ if (remove(QFile::encodeName(d.absFilePath(entry))) < 0)
+ {
+ Out(SYS_GEN|LOG_DEBUG) << "Delete of " << fn << "/" << entry << " failed !" << endl;
+ return false;
+ }
+ }
+
+ if (!d.rmdir(d.absPath()))
+ {
+ Out(SYS_GEN|LOG_DEBUG) << "Failed to remove " << d.absPath() << endl;
+ return false;
+ }
+
+ return true;
+ }
+
+ void Delete(const QString & url,bool nothrow)
+ {
+ QCString fn = QFile::encodeName(url);
+#if HAVE_STAT64
+ struct stat64 statbuf;
+ if (lstat64(fn, &statbuf) < 0)
+ return;
+#else
+ struct stat statbuf;
+ if (lstat(fn, &statbuf) < 0)
+ return;
+#endif
+
+ bool ok = true;
+ // first see if it is a directory
+ if (S_ISDIR(statbuf.st_mode))
+ {
+ ok = DelDir(url);
+ }
+ else
+ {
+ ok = remove(fn) >= 0;
+ }
+
+ if (!ok)
+ {
+ QString err = i18n("Cannot delete %1: %2")
+ .arg(url)
+ .arg(strerror(errno));
+ if (!nothrow)
+ throw Error(err);
+ else
+ Out() << "Error : " << err << endl;
+ }
+ }
+
+ void Touch(const QString & url,bool nothrow)
+ {
+ if (Exists(url))
+ return;
+
+ File fptr;
+ if (!fptr.open(url,"wb"))
+ {
+ if (!nothrow)
+ throw Error(i18n("Cannot create %1: %2")
+ .arg(url)
+ .arg(fptr.errorString()));
+ else
+ Out() << "Error : Cannot create " << url << " : "
+ << fptr.errorString() << endl;
+
+ }
+ }
+
+ Uint64 FileSize(const QString & url)
+ {
+ int ret = 0;
+#if HAVE_STAT64
+ struct stat64 sb;
+ ret = stat64(QFile::encodeName(url),&sb);
+#else
+ struct stat sb;
+ ret = stat(QFile::encodeName(url),&sb);
+#endif
+ if (ret < 0)
+ throw Error(i18n("Cannot calculate the filesize of %1: %2")
+ .arg(url).arg(strerror(errno)));
+
+ return (Uint64)sb.st_size;
+ }
+
+ Uint64 FileSize(int fd)
+ {
+ int ret = 0;
+#if HAVE_STAT64
+ struct stat64 sb;
+ ret = fstat64(fd,&sb);
+#else
+ struct stat sb;
+ ret = fstat(fd,&sb);
+#endif
+ if (ret < 0)
+ throw Error(i18n("Cannot calculate the filesize : %2").arg(strerror(errno)));
+
+ return (Uint64)sb.st_size;
+ }
+
+ bool FatPreallocate(int fd,Uint64 size)
+ {
+ try
+ {
+ SeekFile(fd, size - 1, SEEK_SET);
+ char zero = 0;
+ if (write(fd, &zero, 1) == -1)
+ return false;
+
+ TruncateFile(fd,size,true);
+ }
+ catch (bt::Error & e)
+ {
+ Out() << e.toString() << endl;
+ return false;
+ }
+ return true;
+ }
+
+ bool FatPreallocate(const QString & path,Uint64 size)
+ {
+ int fd = ::open(QFile::encodeName(path),O_RDWR | O_LARGEFILE);
+ if (fd < 0)
+ throw Error(i18n("Cannot open %1 : %2").arg(path).arg(strerror(errno)));
+
+ bool ret = FatPreallocate(fd,size);
+ close(fd);
+ return ret;
+ }
+
+#ifdef HAVE_XFS_XFS_H
+
+ bool XfsPreallocate(int fd, Uint64 size)
+ {
+ if( ! platform_test_xfs_fd(fd) )
+ {
+ return false;
+ }
+
+ xfs_flock64_t allocopt;
+ allocopt.l_whence = 0;
+ allocopt.l_start = 0;
+ allocopt.l_len = size;
+
+ return (! static_cast<bool>(xfsctl(0, fd, XFS_IOC_RESVSP64, &allocopt)) );
+
+ }
+
+ bool XfsPreallocate(const QString & path, Uint64 size)
+ {
+ int fd = ::open(QFile::encodeName(path), O_RDWR | O_LARGEFILE);
+ if (fd < 0)
+ throw Error(i18n("Cannot open %1 : %2").arg(path).arg(strerror(errno)));
+
+ bool ret = XfsPreallocate(fd,size);
+ close(fd);
+ return ret;
+ }
+
+#endif
+
+ void TruncateFile(int fd,Uint64 size,bool quick)
+ {
+ if (FileSize(fd) == size)
+ return;
+
+ if (quick)
+ {
+#if HAVE_FTRUNCATE64
+ if (ftruncate64(fd,size) == -1)
+#else
+ if (ftruncate(fd,size) == -1)
+#endif
+ throw Error(i18n("Cannot expand file : %1").arg(strerror(errno)));
+ }
+ else
+ {
+#if HAVE_POSIX_FALLOCATE64
+ if (posix_fallocate64(fd,0,size) != 0)
+ throw Error(i18n("Cannot expand file : %1").arg(strerror(errno)));
+#elif HAVE_POSIX_FALLOCATE
+ if (posix_fallocate(fd,0,size) != 0)
+ throw Error(i18n("Cannot expand file : %1").arg(strerror(errno)));
+#else
+ SeekFile(fd,0,SEEK_SET);
+ bt::Array<Uint8> buf(4096);
+ buf.fill(0);
+
+ Uint64 written = 0;
+ while (written < size)
+ {
+ int to_write = size - written;
+ if (to_write > 4096)
+ to_write = 4096;
+
+ int ret = write(fd,buf,to_write);
+ if (ret < 0)
+ throw Error(i18n("Cannot expand file : %1").arg(strerror(errno)));
+ else if (ret == 0 || ret != (int)to_write)
+ throw Error(i18n("Cannot expand file").arg(strerror(errno)));
+ else
+ written += to_write;
+ }
+#endif
+ }
+ }
+
+ void TruncateFile(const QString & path,Uint64 size)
+ {
+ int fd = ::open(QFile::encodeName(path),O_RDWR | O_LARGEFILE);
+ if (fd < 0)
+ throw Error(i18n("Cannot open %1 : %2").arg(path).arg(strerror(errno)));
+
+ try
+ {
+ TruncateFile(fd,size,true);
+ close(fd);
+ }
+ catch (...)
+ {
+ close(fd);
+ throw;
+ }
+ }
+
+ void SeekFile(int fd,Int64 off,int whence)
+ {
+#if HAVE_LSEEK64
+ if (lseek64(fd,off,whence) == -1)
+#else
+ if (lseek(fd,off,whence) == -1)
+#endif
+ throw Error(i18n("Cannot seek in file : %1").arg(strerror(errno)));
+ }
+
+ bool FreeDiskSpace(const QString & path,Uint64 & bytes_free)
+ {
+#if HAVE_STATVFS
+#if HAVE_STATVFS64
+ struct statvfs64 stfs;
+ if (statvfs64(path.local8Bit(), &stfs) == 0)
+#else
+ struct statvfs stfs;
+ if (statvfs(path.local8Bit(), &stfs) == 0)
+#endif
+ {
+ bytes_free = ((Uint64)stfs.f_bavail) * ((Uint64)stfs.f_frsize);
+ return true;
+ }
+ else
+ {
+ Out(SYS_GEN|LOG_DEBUG) << "Error : statvfs for " << path << " failed : "
+ << QString(strerror(errno)) << endl;
+
+ return false;
+ }
+#else
+ struct statfs stfs;
+ if (statfs(path.local8Bit(), &stfs) == 0)
+ {
+ bytes_free = ((Uint64)stfs.f_bavail) * ((Uint64)stfs.f_bsize);
+ return true;
+ }
+ else
+ {
+ Out(SYS_GEN|LOG_DEBUG) << "Error : statfs for " << path << " failed : "
+ << QString(strerror(errno)) << endl;
+
+ return false;
+ }
+#endif
+ }
+}
diff --git a/libktorrent/util/fileops.h b/libktorrent/util/fileops.h
new file mode 100644
index 0000000..253ee96
--- /dev/null
+++ b/libktorrent/util/fileops.h
@@ -0,0 +1,165 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef BTFILEOPS_H
+#define BTFILEOPS_H
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <util/constants.h>
+class QString;
+
+namespace bt
+{
+
+ /**
+ * Creates a directory. Convenience function around
+ * KIO::NetAccess::mkdir .
+ * @param dir The url of the dir
+ * @param nothrow wether or not we shouldn't throw an Error upon failure
+ * @throw Error upon error
+ */
+ void MakeDir(const QString & dir,bool nothrow = false);
+
+ /**
+ * Create a symbolic link @a link_url which links to @a link_to
+ * @param link_to The file to link to
+ * @param link_url The link url
+ * @param nothrow wether or not we shouldn't throw an Error upon failure
+ */
+ void SymLink(const QString & link_to,const QString & link_url,bool nothrow = false);
+
+ /**
+ * Move a file/dir from one location to another
+ * @param src The source file
+ * @param dst The destination file / directory
+ * @param nothrow wether or not we shouldn't throw an Error upon failure
+ */
+ void Move(const QString & src,const QString & dst,bool nothrow = false);
+
+ /**
+ * Copy a file.
+ * @param src The source file
+ * @param dst The destination dir/file
+ * @param nothrow wether or not we shouldn't throw an Error upon failure
+ */
+ void CopyFile(const QString & src,const QString & dst,bool nothrow = false);
+
+ /**
+ * Copy a file or directory
+ * @param src The source file
+ * @param dst The destination dir/file
+ * @param nothrow wether or not we shouldn't throw an Error upon failure
+ */
+ void CopyDir(const QString & src,const QString & dst,bool nothrow = false);
+
+ /**
+ * Check wether a file/dir exists
+ * @param url The file/dir
+ * @return true if it exits
+ */
+ bool Exists(const QString & url);
+
+ /**
+ * Delete a file or directory.
+ * @param url The url of the file/dir
+ * @param nothrow wether or not we shouldn't throw an Error upon failure
+ */
+ void Delete(const QString & url,bool nothrow = false);
+
+ /**
+ * Try to create a file. Doesn't do anything if the file
+ * already exists.
+ * @param url The url of the file
+ * @param nothrow wether or not we shouldn't throw an Error upon failure
+ */
+ void Touch(const QString & url,bool nothrow = false);
+
+ /**
+ * Calculates the size of a file
+ * @param url Name of the file
+ * @return The size of the file
+ * @throw Error if the file doesn't exist, or something else goes wrong
+ */
+ Uint64 FileSize(const QString & url);
+
+ /**
+ * Get the size of a file.
+ * @param fd The file descriptor of the file
+ * @return The size
+ * @throw Error if the file doesn't exist, or something else goes wrong
+ */
+ Uint64 FileSize(int fd);
+
+ /**
+ * Truncate a file (wrapper around ftruncate)
+ * @param fd The file descriptor of the file
+ * @param size The size to truncate to
+ * @throw Error if the file doesn't exist, or something else goes wrong
+ */
+ void TruncateFile(int fd,Uint64 size,bool quick);
+
+ /**
+ * Truncate a file (wrapper around ftruncate)
+ * @param fd Path of the file
+ * @param size The size to truncate to
+ * @param quick Use the quick way (doesn't prevent fragmentationt)
+ * @throw Error if the file doesn't exist, or something else goes wrong
+ */
+ void TruncateFile(const QString & path,Uint64 size);
+
+ /**
+ * Special truncate for FAT file systems.
+ */
+ bool FatPreallocate(int fd,Uint64 size);
+
+ /**
+ * Special truncate for FAT file systems.
+ */
+ bool FatPreallocate(const QString & path,Uint64 size);
+
+#ifdef HAVE_XFS_XFS_H
+ /**
+ * Special truncate for XFS file systems.
+ */
+ bool XfsPreallocate(int fd,Uint64 size);
+
+ /**
+ * Special truncate for XFS file systems.
+ */
+ bool XfsPreallocate(const QString & path,Uint64 size);
+
+#endif
+
+ /**
+ * Seek in a file, wrapper around lseek
+ * @param fd The file descriptor
+ * @param off Offset
+ * @param whence Position to seek from
+ * @throw Error if something else goes wrong
+ */
+ void SeekFile(int fd,Int64 off,int whence);
+
+ /// Calculate the number of bytes free on the filesystem path is located
+ bool FreeDiskSpace(const QString & path,Uint64 & bytes_free);
+}
+
+#endif
diff --git a/libktorrent/util/functions.cpp b/libktorrent/util/functions.cpp
new file mode 100644
index 0000000..744bf43
--- /dev/null
+++ b/libktorrent/util/functions.cpp
@@ -0,0 +1,239 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <qdir.h>
+#include <qhostaddress.h>
+#include <errno.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <kio/netaccess.h>
+#include <klocale.h>
+#include <kmimetype.h>
+#include <kglobal.h>
+#include "functions.h"
+#include "error.h"
+#include "log.h"
+
+namespace bt
+{
+
+ bool IsMultimediaFile(const QString & filename)
+ {
+ KMimeType::Ptr ptr = KMimeType::findByPath(filename);
+ QString name = ptr->name();
+ return name.startsWith("audio") || name.startsWith("video") || name == "application/ogg";
+ }
+
+ QHostAddress LookUpHost(const QString & host)
+ {
+ struct hostent * he = gethostbyname(host.ascii());
+ QHostAddress addr;
+ if (he)
+ {
+ addr.setAddress(inet_ntoa(*((struct in_addr *)he->h_addr)));
+ }
+ return addr;
+ }
+
+ QString DirSeparator()
+ {
+ QString tmp;
+ tmp.append(QDir::separator());
+ return tmp;
+ }
+
+ void WriteUint64(Uint8* buf,Uint32 off,Uint64 val)
+ {
+ buf[off + 0] = (Uint8) ((val & 0xFF00000000000000ULL) >> 56);
+ buf[off + 1] = (Uint8) ((val & 0x00FF000000000000ULL) >> 48);
+ buf[off + 2] = (Uint8) ((val & 0x0000FF0000000000ULL) >> 40);
+ buf[off + 3] = (Uint8) ((val & 0x000000FF00000000ULL) >> 32);
+ buf[off + 4] = (Uint8) ((val & 0x00000000FF000000ULL) >> 24);
+ buf[off + 5] = (Uint8) ((val & 0x0000000000FF0000ULL) >> 16);
+ buf[off + 6] = (Uint8) ((val & 0x000000000000FF00ULL) >> 8);
+ buf[off + 7] = (Uint8) ((val & 0x00000000000000FFULL) >> 0);
+ }
+
+ Uint64 ReadUint64(const Uint8* buf,Uint64 off)
+ {
+ Uint64 tmp =
+ ((Uint64)buf[off] << 56) |
+ ((Uint64)buf[off+1] << 48) |
+ ((Uint64)buf[off+2] << 40) |
+ ((Uint64)buf[off+3] << 32) |
+ ((Uint64)buf[off+4] << 24) |
+ ((Uint64)buf[off+5] << 16) |
+ ((Uint64)buf[off+6] << 8) |
+ ((Uint64)buf[off+7] << 0);
+
+ return tmp;
+ }
+
+ void WriteUint32(Uint8* buf,Uint32 off,Uint32 val)
+ {
+ buf[off + 0] = (Uint8) ((val & 0xFF000000) >> 24);
+ buf[off + 1] = (Uint8) ((val & 0x00FF0000) >> 16);
+ buf[off + 2] = (Uint8) ((val & 0x0000FF00) >> 8);
+ buf[off + 3] = (Uint8) (val & 0x000000FF);
+ }
+
+ Uint32 ReadUint32(const Uint8* buf,Uint32 off)
+ {
+ return (buf[off] << 24) | (buf[off+1] << 16) | (buf[off+2] << 8) | buf[off + 3];
+ }
+
+ void WriteUint16(Uint8* buf,Uint32 off,Uint16 val)
+ {
+ buf[off + 0] = (Uint8) ((val & 0xFF00) >> 8);
+ buf[off + 1] = (Uint8) (val & 0x000FF);
+ }
+
+ Uint16 ReadUint16(const Uint8* buf,Uint32 off)
+ {
+ return (buf[off] << 8) | buf[off + 1];
+ }
+
+
+ void WriteInt64(Uint8* buf,Uint32 off,Int64 val)
+ {
+ buf[off + 0] = (Uint8) ((val & 0xFF00000000000000ULL) >> 56);
+ buf[off + 1] = (Uint8) ((val & 0x00FF000000000000ULL) >> 48);
+ buf[off + 2] = (Uint8) ((val & 0x0000FF0000000000ULL) >> 40);
+ buf[off + 3] = (Uint8) ((val & 0x000000FF00000000ULL) >> 32);
+ buf[off + 4] = (Uint8) ((val & 0x00000000FF000000ULL) >> 24);
+ buf[off + 5] = (Uint8) ((val & 0x0000000000FF0000ULL) >> 16);
+ buf[off + 6] = (Uint8) ((val & 0x000000000000FF00ULL) >> 8);
+ buf[off + 7] = (Uint8) ((val & 0x00000000000000FFULL) >> 0);
+ }
+
+ Int64 ReadInt64(const Uint8* buf,Uint32 off)
+ {
+ Int64 tmp =
+ ((Int64)buf[off] << 56) |
+ ((Int64)buf[off+1] << 48) |
+ ((Int64)buf[off+2] << 40) |
+ ((Int64)buf[off+3] << 32) |
+ ((Int64)buf[off+4] << 24) |
+ ((Int64)buf[off+5] << 16) |
+ ((Int64)buf[off+6] << 8) |
+ ((Int64)buf[off+7] << 0);
+
+ return tmp;
+ }
+
+ void WriteInt32(Uint8* buf,Uint32 off,Int32 val)
+ {
+ buf[off + 0] = (Uint8) ((val & 0xFF000000) >> 24);
+ buf[off + 1] = (Uint8) ((val & 0x00FF0000) >> 16);
+ buf[off + 2] = (Uint8) ((val & 0x0000FF00) >> 8);
+ buf[off + 3] = (Uint8) (val & 0x000000FF);
+ }
+
+ Int32 ReadInt32(const Uint8* buf,Uint32 off)
+ {
+ return (Int32)(buf[off] << 24) | (buf[off+1] << 16) | (buf[off+2] << 8) | buf[off + 3];
+ }
+
+ void WriteInt16(Uint8* buf,Uint32 off,Int16 val)
+ {
+ buf[off + 0] = (Uint8) ((val & 0xFF00) >> 8);
+ buf[off + 1] = (Uint8) (val & 0x000FF);
+ }
+
+ Int16 ReadInt16(const Uint8* buf,Uint32 off)
+ {
+ return (Int16)(buf[off] << 8) | buf[off + 1];
+ }
+
+ void UpdateCurrentTime()
+ {
+ global_time_stamp = Now();
+ }
+
+ TimeStamp global_time_stamp = 0;
+
+ Uint64 Now()
+ {
+ struct timeval tv;
+ gettimeofday(&tv,0);
+ global_time_stamp = (Uint64)tv.tv_sec * 1000 + (Uint64)tv.tv_usec * 0.001;
+ return global_time_stamp;
+ }
+
+ Uint32 MaxOpenFiles()
+ {
+ struct rlimit lim;
+ getrlimit(RLIMIT_NOFILE,&lim);
+ return lim.rlim_cur;
+ }
+
+ bool MaximizeLimits()
+ {
+ // first get the current limits
+ struct rlimit lim;
+ getrlimit(RLIMIT_NOFILE,&lim);
+
+ if (lim.rlim_cur != lim.rlim_max)
+ {
+ Out(SYS_GEN|LOG_DEBUG) << "Current limit for number of files : " << lim.rlim_cur
+ << " (" << lim.rlim_max << " max)" << endl;
+ lim.rlim_cur = lim.rlim_max;
+ if (setrlimit(RLIMIT_NOFILE,&lim) < 0)
+ {
+ Out(SYS_GEN|LOG_DEBUG) << "Failed to maximize file limit : "
+ << QString(strerror(errno)) << endl;
+ return false;
+ }
+ }
+ else
+ {
+ Out(SYS_GEN|LOG_DEBUG) << "File limit allready at maximum " << endl;
+ }
+
+ getrlimit(RLIMIT_DATA,&lim);
+ if (lim.rlim_cur != lim.rlim_max)
+ {
+ Out(SYS_GEN|LOG_DEBUG) << "Current limit for data size : " << lim.rlim_cur
+ << " (" << lim.rlim_max << " max)" << endl;
+ lim.rlim_cur = lim.rlim_max;
+ if (setrlimit(RLIMIT_DATA,&lim) < 0)
+ {
+ Out(SYS_GEN|LOG_DEBUG) << "Failed to maximize data limit : "
+ << QString(strerror(errno)) << endl;
+ return false;
+ }
+ }
+ else
+ {
+ Out(SYS_GEN|LOG_DEBUG) << "Data limit allready at maximum " << endl;
+ }
+
+ return true;
+ }
+
+
+
+
+}
diff --git a/libktorrent/util/functions.h b/libktorrent/util/functions.h
new file mode 100644
index 0000000..4ace51b
--- /dev/null
+++ b/libktorrent/util/functions.h
@@ -0,0 +1,72 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef BTFUNCTIONS_H
+#define BTFUNCTIONS_H
+
+#include "constants.h"
+
+class QString;
+class QHostAddress;
+class KURL;
+
+namespace bt
+{
+
+ void WriteUint64(Uint8* buf,Uint32 off,Uint64 val);
+ Uint64 ReadUint64(const Uint8* buf,Uint64 off);
+
+ void WriteUint32(Uint8* buf,Uint32 off,Uint32 val);
+ Uint32 ReadUint32(const Uint8* buf,Uint32 off);
+
+ void WriteUint16(Uint8* buf,Uint32 off,Uint16 val);
+ Uint16 ReadUint16(const Uint8* buf,Uint32 off);
+
+
+ void WriteInt64(Uint8* buf,Uint32 off,Int64 val);
+ Int64 ReadInt64(const Uint8* buf,Uint32 off);
+
+ void WriteInt32(Uint8* buf,Uint32 off,Int32 val);
+ Int32 ReadInt32(const Uint8* buf,Uint32 off);
+
+ void WriteInt16(Uint8* buf,Uint32 off,Int16 val);
+ Int16 ReadInt16(const Uint8* buf,Uint32 off);
+
+ void UpdateCurrentTime();
+
+ extern TimeStamp global_time_stamp;
+
+ inline TimeStamp GetCurrentTime() {return global_time_stamp;}
+
+ TimeStamp Now();
+
+ QHostAddress LookUpHost(const QString & host);
+ QString DirSeparator();
+ bool IsMultimediaFile(const QString & filename);
+
+ /**
+ * Maximize the file and memory limits using setrlimit.
+ */
+ bool MaximizeLimits();
+
+ /// Get the maximum number of open files
+ Uint32 MaxOpenFiles();
+}
+
+#endif
diff --git a/libktorrent/util/httprequest.cpp b/libktorrent/util/httprequest.cpp
new file mode 100644
index 0000000..d0652bc
--- /dev/null
+++ b/libktorrent/util/httprequest.cpp
@@ -0,0 +1,122 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <qstringlist.h>
+#include <torrent/globals.h>
+#include "httprequest.h"
+#include "array.h"
+#include "log.h"
+
+
+namespace bt
+{
+
+ HTTPRequest::HTTPRequest(const QString & hdr,const QString & payload,const QString & host,Uint16 port,bool verbose) : hdr(hdr),payload(payload),verbose(verbose)
+ {
+ sock = new KNetwork::KStreamSocket(host,QString::number(port),this,0);
+ sock->enableRead(true);
+ sock->enableWrite(true);
+ sock->setTimeout(30000);
+ sock->setBlocking(false);
+ connect(sock,SIGNAL(readyRead()),this,SLOT(onReadyRead()));
+ connect(sock,SIGNAL(gotError(int)),this,SLOT(onError(int )));
+ connect(sock,SIGNAL(timedOut()),this,SLOT(onTimeout()));
+ connect(sock,SIGNAL(connected(const KResolverEntry&)),
+ this, SLOT(onConnect( const KResolverEntry& )));
+ }
+
+
+ HTTPRequest::~HTTPRequest()
+ {
+ sock->close();
+ delete sock;
+ }
+
+ void HTTPRequest::start()
+ {
+ sock->connect();
+ }
+
+ void HTTPRequest::onConnect(const KResolverEntry&)
+ {
+ payload = payload.replace("$LOCAL_IP",sock->localAddress().nodeName());
+ hdr = hdr.replace("$CONTENT_LENGTH",QString::number(payload.length()));
+
+ QString req = hdr + payload;
+ if (verbose)
+ {
+ Out(SYS_PNP|LOG_DEBUG) << "Sending " << endl;
+ Out(SYS_PNP|LOG_DEBUG) << hdr << payload << endl;
+ }
+ sock->writeBlock(req.ascii(),req.length());
+ }
+
+ void HTTPRequest::onReadyRead()
+ {
+ Uint32 ba = sock->bytesAvailable();
+ if (ba == 0)
+ {
+ error(this,false);
+ sock->close();
+ return;
+ }
+
+ Array<char> data(ba);
+ ba = sock->readBlock(data,ba);
+ QString strdata((const char*)data);
+ QStringList sl = QStringList::split("\r\n",strdata,false);
+
+ if (verbose)
+ {
+ Out(SYS_PNP|LOG_DEBUG) << "Got reply : " << endl;
+ Out(SYS_PNP|LOG_DEBUG) << strdata << endl;
+ }
+
+ if (sl.first().contains("HTTP") && sl.first().contains("200"))
+ {
+ // emit reply OK
+ replyOK(this,sl.last());
+ }
+ else
+ {
+ // emit reply error
+ replyError(this,sl.last());
+ }
+ operationFinished(this);
+ }
+
+ void HTTPRequest::onError(int)
+ {
+ Out() << "HTTPRequest error : " << sock->errorString() << endl;
+ error(this,false);
+ sock->close();
+ operationFinished(this);
+ }
+
+ void HTTPRequest::onTimeout()
+ {
+ Out() << "HTTPRequest timeout" << endl;
+ error(this,true);
+ sock->close();
+ operationFinished(this);
+ }
+
+
+}
+#include "httprequest.moc"
diff --git a/libktorrent/util/httprequest.h b/libktorrent/util/httprequest.h
new file mode 100644
index 0000000..348a84e
--- /dev/null
+++ b/libktorrent/util/httprequest.h
@@ -0,0 +1,98 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef BTHTTPREQUEST_H
+#define BTHTTPREQUEST_H
+
+#include <qobject.h>
+#include <kurl.h>
+#include <kstreamsocket.h>
+#include <interfaces/exitoperation.h>
+#include "constants.h"
+
+using KNetwork::KResolverEntry;
+
+namespace bt
+{
+
+ /**
+ * @author Joris Guisson
+ *
+ * Just create one, fill in the fields,
+ * connect to the right signals and forget about it. After the reply has been received or
+ * an error occurred, the appropriate signal will be emitted.
+ */
+ class HTTPRequest : public kt::ExitOperation
+ {
+ Q_OBJECT
+ public:
+ /**
+ * Constructor, set the url and the request header.
+ * @param hdr The http request header
+ * @param payload The payload
+ * @param host The host
+ * @param port THe port
+ * @param verbose Print traffic to the log
+ */
+ HTTPRequest(const QString & hdr,const QString & payload,const QString & host,
+ Uint16 port,bool verbose);
+ virtual ~HTTPRequest();
+
+ /**
+ * Open a connetion and send the request.
+ */
+ void start();
+
+ signals:
+ /**
+ * An OK reply was sent.
+ * @param r The sender of the request
+ * @param data The data of the reply
+ */
+ void replyOK(bt::HTTPRequest* r,const QString & data);
+
+ /**
+ * Anything else but an 200 OK was sent.
+ * @param r The sender of the request
+ * @param data The data of the reply
+ */
+ void replyError(bt::HTTPRequest* r,const QString & data);
+
+ /**
+ * No reply was sent and an error or timeout occurred.
+ * @param r The sender of the request
+ * @param timeout Wether or not a timeout occurred
+ */
+ void error(bt::HTTPRequest* r,bool timeout);
+
+ private slots:
+ void onReadyRead();
+ void onError(int);
+ void onTimeout();
+ void onConnect(const KResolverEntry&);
+
+ private:
+ KNetwork::KStreamSocket* sock;
+ QString hdr,payload;
+ bool verbose;
+ };
+
+}
+
+#endif
diff --git a/libktorrent/util/log.cpp b/libktorrent/util/log.cpp
new file mode 100644
index 0000000..05682a8
--- /dev/null
+++ b/libktorrent/util/log.cpp
@@ -0,0 +1,249 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+
+#include <kurl.h>
+#include <kprocess.h>
+#include <klocale.h>
+#include <qdatetime.h>
+#include <qtextstream.h>
+#include <qfile.h>
+#include <qptrlist.h>
+#include <iostream>
+#include <stdlib.h>
+#include <torrent/globals.h>
+#include <interfaces/logmonitorinterface.h>
+#include <qmutex.h>
+#include <util/fileops.h>
+#include <stdlib.h>
+#include "log.h"
+#include "error.h"
+#include "autorotatelogjob.h"
+
+using namespace kt;
+
+namespace bt
+{
+ const Uint32 MAX_LOG_FILE_SIZE = 10 * 1024 * 1024; // 10 MB
+
+ class Log::Private
+ {
+ public:
+ Log* parent;
+ QTextStream* out;
+ QFile fptr;
+ bool to_cout;
+ QPtrList<LogMonitorInterface> monitors;
+ QString tmp;
+ QMutex mutex;
+ unsigned int m_filter;
+ AutoRotateLogJob* rotate_job;
+ public:
+ Private(Log* parent) : parent(parent),out(0),to_cout(false),rotate_job(0)
+ {
+ out = new QTextStream();
+ }
+
+ ~Private()
+ {
+ delete out;
+ }
+
+
+ void setFilter(unsigned int filter)
+ {
+ m_filter = filter;
+ }
+
+ void rotateLogs(const QString & file)
+ {
+ if (bt::Exists(file + "-10.gz"))
+ bt::Delete(file + "-10.gz",true);
+
+ // move all log files one up
+ for (Uint32 i = 10;i > 1;i--)
+ {
+ QString prev = QString("%1-%2.gz").arg(file).arg(i - 1);
+ QString curr = QString("%1-%2.gz").arg(file).arg(i);
+ if (bt::Exists(prev))
+ bt::Move(prev,curr,true);
+ }
+
+ // move current log to 1 and zip it
+ bt::Move(file,file + "-1",true);
+ system(QString("gzip " + KProcess::quote(file + "-1")).local8Bit());
+ }
+
+ void setOutputFile(const QString & file)
+ {
+ if (fptr.isOpen())
+ fptr.close();
+
+ if (bt::Exists(file))
+ rotateLogs(file);
+
+ fptr.setName(file);
+ if (!fptr.open(IO_WriteOnly))
+ throw Error(i18n("Cannot open log file %1 : %2").arg(file).arg(fptr.errorString()));
+
+ out->setDevice(&fptr);
+ }
+
+ void write(const QString & line)
+ {
+ tmp += line;
+ }
+
+ void finishLine()
+ {
+ // only add stuff when we are not rotating the logs
+ // this could result in the loss of some messages
+ if (!rotate_job)
+ {
+ *out << QDateTime::currentDateTime().toString() << ": " << tmp << ::endl;
+ fptr.flush();
+ if (to_cout)
+ std::cout << tmp.local8Bit() << std::endl;
+
+ if (monitors.count() > 0)
+ {
+ QPtrList<LogMonitorInterface>::iterator i = monitors.begin();
+ while (i != monitors.end())
+ {
+ kt::LogMonitorInterface* lmi = *i;
+ lmi->message(tmp,m_filter);
+ i++;
+ }
+ }
+ }
+ tmp = "";
+ }
+
+ void endline()
+ {
+ finishLine();
+ if (fptr.size() > MAX_LOG_FILE_SIZE && !rotate_job)
+ {
+ tmp = "Log larger then 10 MB, rotating";
+ finishLine();
+ QString file = fptr.name();
+ fptr.close(); // close the log file
+ out->setDevice(0);
+ // start the rotate job
+ rotate_job = new AutoRotateLogJob(file,parent);
+ }
+ }
+
+ void logRotateDone()
+ {
+ fptr.open(IO_WriteOnly);
+ out->setDevice(&fptr);
+ rotate_job = 0;
+ }
+ };
+
+ Log::Log()
+ {
+ priv = new Private(this);
+ }
+
+
+ Log::~Log()
+ {
+ delete priv;
+ }
+
+
+ void Log::setOutputFile(const QString & file)
+ {
+ priv->setOutputFile(file);
+ }
+
+ void Log::addMonitor(kt::LogMonitorInterface* m)
+ {
+ priv->monitors.append(m);
+ }
+
+ void Log::removeMonitor(kt::LogMonitorInterface* m)
+ {
+ priv->monitors.remove(m);
+ }
+
+ void Log::setOutputToConsole(bool on)
+ {
+ priv->to_cout = on;
+ }
+
+ Log & endl(Log & lg)
+ {
+ lg.priv->endline();
+ lg.priv->mutex.unlock(); // unlock after end of line
+ return lg;
+ }
+
+ Log & Log::operator << (const KURL & url)
+ {
+ priv->write(url.prettyURL());
+ return *this;
+ }
+
+ Log & Log::operator << (const QString & s)
+ {
+ priv->write(s);
+ return *this;
+ }
+
+ Log & Log::operator << (const char* s)
+ {
+ priv->write(s);
+ return *this;
+ }
+
+ Log & Log::operator << (Uint64 v)
+ {
+ return operator << (QString::number(v));
+ }
+
+ Log & Log::operator << (Int64 v)
+ {
+ return operator << (QString::number(v));
+ }
+
+ void Log::setFilter(unsigned int filter)
+ {
+ priv->setFilter(filter);
+ }
+
+ void Log::lock()
+ {
+ priv->mutex.lock();
+ }
+
+ void Log::logRotateDone()
+ {
+ priv->logRotateDone();
+ }
+
+ Log & Out(unsigned int arg)
+ {
+ Log & lg = Globals::instance().getLog(arg);
+ lg.lock();
+ return lg;
+ }
+}
diff --git a/libktorrent/util/log.h b/libktorrent/util/log.h
new file mode 100644
index 0000000..2fe0ba6
--- /dev/null
+++ b/libktorrent/util/log.h
@@ -0,0 +1,209 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+
+#ifndef JORISLOG_H
+#define JORISLOG_H
+
+
+#include "constants.h"
+#include <qstring.h>
+
+// LOG MESSAGES CONSTANTS
+#define LOG_NONE 0x00
+#define LOG_IMPORTANT 0x01
+#define LOG_NOTICE 0x03
+#define LOG_DEBUG 0x07
+#define LOG_ALL 0x0F
+
+#define SYS_GEN 0x0010 // Genereral info messages
+#define SYS_CON 0x0020 // Connections
+#define SYS_TRK 0x0040 // Tracker
+#define SYS_DHT 0x0080 // DHT
+#define SYS_DIO 0x0100 // Disk IO related stuff, saving and loading of chunks ...
+
+//plugins
+#define SYS_IPF 0x1000 // IPFilter
+#define SYS_SRC 0x2000 // Search plugin
+#define SYS_PNP 0x4000 // UPnP plugin
+#define SYS_INW 0x8000 // InfoWidget
+#define SYS_SNF 0x10000 // ScanFolder plugin
+#define SYS_PFI 0x20000 // Part file import
+#define SYS_SCD 0x40000 // Scheduler plugin
+#define SYS_RSS 0x80000 // RSS plugin
+#define SYS_WEB 0x100000 // WebInterface plugin
+#define SYS_ZCO 0x200000 // ZeroConf plugin
+
+class KURL;
+
+
+namespace kt
+{
+ class LogMonitorInterface;
+}
+
+namespace bt
+{
+
+
+ /**
+ * @author Joris Guisson
+ * @brief Class which writes messages to a logfile
+ *
+ * This class writes messages to a logfile. To use it, create an instance,
+ * set the output file and write stuff with the << operator.
+ *
+ * By default all messages will also be printed on the standard output. This
+ * can be turned down using the @a setOutputToConsole function.
+ *
+ * There is also the possibility to monitor what is written to the log using
+ * the LogMonitorInterface class.
+ */
+ class Log
+ {
+ class Private;
+
+ Private* priv;
+ public:
+ /**
+ * Constructor.
+ */
+ Log();
+
+ /**
+ * Destructor, closes the file.
+ */
+ virtual ~Log();
+
+ /**
+ * Enable or disable the printing of log messages to the standard
+ * output.
+ * @param on Enable or disable
+ */
+ void setOutputToConsole(bool on);
+
+ /**
+ * Add a log monitor.
+ * @param m The log monitor
+ */
+ void addMonitor(kt::LogMonitorInterface* m);
+
+ /**
+ * Remove a log monitor.
+ * @param m The log monitor
+ */
+ void removeMonitor(kt::LogMonitorInterface* m);
+
+ /**
+ * Set the output logfile.
+ * @param file The name of the file
+ * @throw Exception if the file can't be opened
+ */
+ void setOutputFile(const QString & file);
+
+ /**
+ * Write a number to the log file.
+ * Anything which can be passed to QString::number will do.
+ * @param val The value
+ * @return This Log
+ */
+ template <class T>
+ Log & operator << (T val)
+ {
+ return operator << (QString::number(val));
+ }
+
+ /**
+ * Apply a function to the Log.
+ * @param func The function
+ * @return This Log
+ */
+ Log & operator << (Log & (*func)(Log & ))
+ {
+ return func(*this);
+ }
+
+
+ /**
+ * Output a QString to the log.
+ * @param s The QString
+ * @return This Log
+ */
+ Log & operator << (const char* s);
+
+ /**
+ * Output a QString to the log.
+ * @param s The QString
+ * @return This Log
+ */
+ Log & operator << (const QString & s);
+
+ /**
+ * Output a 64 bit integer to the log.
+ * @param v The integer
+ * @return This Log
+ */
+ Log & operator << (Uint64 v);
+
+ /**
+ * Output a 64 bit integer to the log.
+ * @param v The integer
+ * @return This Log
+ */
+ Log & operator << (Int64 v);
+
+ /**
+ * Prints and endline character to the Log and flushes it.
+ * @param lg The Log
+ * @return @a lg
+ */
+ friend Log & endl(Log & lg);
+
+ /**
+ * Write an URL to the file.
+ * @param text The KURL
+ * @return This Log
+ */
+ Log & operator << (const KURL & url);
+
+ /**
+ * Sets a filter for log messages. Applies only to listeners via LogMonitorInterface!
+ * @param filter SYS & LOG flags combined with bitwise OR.
+ */
+ void setFilter(unsigned int filter);
+
+ /// Lock the mutex of the log, should be called in Out()
+ void lock();
+
+ /// Called by the auto log rotate job when it has finished
+ void logRotateDone();
+ };
+
+ Log & endl(Log & lg);
+
+
+ Log & Out(unsigned int arg = 0x00);
+ inline Log & GenOut(unsigned int arg) {return Out(SYS_GEN|arg);}
+ inline Log & DHTOut(unsigned int arg) {return Out(SYS_DHT|arg);}
+ inline Log & ConOut(unsigned int arg) {return Out(SYS_CON|arg);}
+ inline Log & TrkOut(unsigned int arg) {return Out(SYS_TRK|arg);}
+
+}
+
+#endif
diff --git a/libktorrent/util/mmapfile.cpp b/libktorrent/util/mmapfile.cpp
new file mode 100644
index 0000000..579c67a
--- /dev/null
+++ b/libktorrent/util/mmapfile.cpp
@@ -0,0 +1,294 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <qfile.h>
+#include <kfileitem.h>
+#include <kio/netaccess.h>
+#include <klocale.h>
+#include <util/error.h>
+#include <util/log.h>
+#include <torrent/globals.h>
+#include "mmapfile.h"
+
+namespace bt
+{
+
+ MMapFile::MMapFile() : fd(-1),data(0),size(0),file_size(0),ptr(0),mode(READ)
+ {}
+
+
+ MMapFile::~MMapFile()
+ {
+ if (fd > 0)
+ close();
+ }
+
+ bool MMapFile::open(const QString & file,Mode mode)
+ {
+#if HAVE_STAT64
+ struct stat64 sb;
+ stat64(QFile::encodeName(file),&sb);
+#else
+ struct stat sb;
+ stat(QFile::encodeName(file),&sb);
+#endif
+
+ return open(file,mode,(Uint64)sb.st_size);
+ }
+
+ bool MMapFile::open(const QString & file,Mode mode,Uint64 size)
+ {
+ // close already open file
+ if (fd > 0)
+ close();
+
+ // setup flags
+ int flag = 0,mmap_flag = 0;
+ switch (mode)
+ {
+ case READ:
+ flag = O_RDONLY;
+ mmap_flag = PROT_READ;
+ break;
+ case WRITE:
+ flag = O_WRONLY | O_CREAT;
+ mmap_flag = PROT_WRITE;
+ break;
+ case RW:
+ flag = O_RDWR | O_CREAT;
+ mmap_flag = PROT_READ|PROT_WRITE;
+ break;
+ }
+
+ // Not all systems have O_LARGEFILE as an explicit flag
+ // (for instance, FreeBSD. Solaris does, but only if
+ // _LARGEFILE_SOURCE is defined in the compile).
+ // So OR it in if it is defined.
+#ifdef O_LARGEFILE
+ flag |= O_LARGEFILE;
+#endif
+
+ // open the file
+ fd = ::open(QFile::encodeName(file) , flag);//(int)flag);
+ if (fd == -1)
+ return false;
+
+ // read the file size
+ this->size = size;
+ this->mode = mode;
+
+#if HAVE_STAT64
+ struct stat64 sb;
+ stat64(QFile::encodeName(file),&sb);
+#else
+ struct stat sb;
+ stat(QFile::encodeName(file),&sb);
+#endif
+ file_size = (Uint64)sb.st_size;
+ filename = file;
+
+ // mmap the file
+#if HAVE_MMAP64
+ data = (Uint8*)mmap64(0, size, mmap_flag, MAP_SHARED, fd, 0);
+#else
+ data = (Uint8*)mmap(0, size, mmap_flag, MAP_SHARED, fd, 0);
+#endif
+ if (data == MAP_FAILED)
+ {
+ ::close(fd);
+ data = 0;
+ fd = -1;
+ ptr = 0;
+ return false;
+ }
+ ptr = 0;
+ return true;
+ }
+
+ void MMapFile::close()
+ {
+ if (fd > 0)
+ {
+#if HAVE_MUNMAP64
+ munmap64(data,size);
+#else
+ munmap(data,size);
+#endif
+ ::close(fd);
+ ptr = size = 0;
+ data = 0;
+ fd = -1;
+ filename = QString::null;
+ }
+ }
+
+ void MMapFile::flush()
+ {
+ if (fd > 0)
+ msync(data,size,MS_SYNC);
+ }
+
+ Uint32 MMapFile::write(const void* buf,Uint32 buf_size)
+ {
+ if (fd == -1 || mode == READ)
+ return 0;
+
+ // check if data fits in memory mapping
+ if (ptr + buf_size > size)
+ throw Error(i18n("Cannot write beyond end of the mmap buffer!"));
+
+ Out() << "MMapFile::write : " << (ptr + buf_size) << " " << file_size << endl;
+ // enlarge the file if necessary
+ if (ptr + buf_size > file_size)
+ {
+ growFile(ptr + buf_size);
+ }
+
+ // memcpy data
+ memcpy(&data[ptr],buf,buf_size);
+ // update ptr
+ ptr += buf_size;
+ // update file size if necessary
+ if (ptr >= size)
+ size = ptr;
+
+ return buf_size;
+ }
+
+ void MMapFile::growFile(Uint64 new_size)
+ {
+ Out() << "Growing file to " << new_size << " bytes " << endl;
+ Uint64 to_write = new_size - file_size;
+ ssize_t written;
+ // jump to the end of the file
+ lseek(fd,0,SEEK_END);
+
+ Uint8 buf[1024];
+ memset(buf,0,1024);
+ // write data until to_write is 0
+ while (to_write > 0)
+ {
+ ssize_t w = ::write(fd,buf, to_write > 1024 ? 1024 : to_write);
+ if (w > 0)
+ to_write -= w;
+ else if (w < 0)
+ break;
+ }
+ file_size = new_size;
+ }
+
+ Uint32 MMapFile::read(void* buf,Uint32 buf_size)
+ {
+ if (fd == -1 || mode == WRITE)
+ return 0;
+
+ // check if we aren't going to read past the end of the file
+ Uint32 to_read = ptr + buf_size >= size ? size - ptr : buf_size;
+ // read data
+ memcpy(buf,data+ptr,to_read);
+ ptr += to_read;
+ return to_read;
+ }
+
+ Uint64 MMapFile::seek(SeekPos from,Int64 num)
+ {
+ switch (from)
+ {
+ case BEGIN:
+ if (num > 0)
+ ptr = num;
+ if (ptr >= size)
+ ptr = size - 1;
+ break;
+ case END:
+ {
+ Int64 np = (size - 1) + num;
+ if (np < 0)
+ {
+ ptr = 0;
+ break;
+ }
+ if (np >= (Int64) size)
+ {
+ ptr = size - 1;
+ break;
+ }
+ ptr = np;
+ }
+ break;
+ case CURRENT:
+ {
+ Int64 np = ptr + num;
+ if (np < 0)
+ {
+ ptr = 0;
+ break;
+ }
+ if (np >= (Int64) size)
+ {
+ ptr = size - 1;
+ break;
+ }
+ ptr = np;
+ }
+ break;
+ }
+ return ptr;
+ }
+
+ bool MMapFile::eof() const
+ {
+ return ptr >= size;
+ }
+
+ Uint64 MMapFile::tell() const
+ {
+ return ptr;
+ }
+
+ QString MMapFile::errorString() const
+ {
+ return strerror(errno);
+ }
+
+ Uint64 MMapFile::getSize() const
+ {
+ return size;
+ }
+
+ Uint8* MMapFile::getData(Uint64 off)
+ {
+ if (off >= size)
+ return 0;
+ return &data[off];
+ }
+}
+
diff --git a/libktorrent/util/mmapfile.h b/libktorrent/util/mmapfile.h
new file mode 100644
index 0000000..ca0d782
--- /dev/null
+++ b/libktorrent/util/mmapfile.h
@@ -0,0 +1,146 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef BTMMAPFILE_H
+#define BTMMAPFILE_H
+
+
+#include <qstring.h>
+#include <util/constants.h>
+
+namespace bt
+{
+
+ /**
+ * @author Joris Guisson
+ * @brief Memory mapped file
+ *
+ * This class allows to access memory mapped files. It's pretty similar to
+ * File.
+ * TODO: make sure large files work (not really needed for the blocklist)
+ */
+ class MMapFile
+ {
+ public:
+ MMapFile();
+ virtual ~MMapFile();
+
+ enum Mode
+ {
+ READ,WRITE, RW
+ };
+ /**
+ * Open the file. If mode is write and the file doesn't exist, it will
+ * be created.
+ * @param file Filename
+ * @param mode Mode (READ, WRITE or RW)
+ * @return true upon succes
+ */
+ bool open(const QString & file,Mode mode);
+
+ /**
+ * Open the file. If mode is write and the file doesn't exist, it will
+ * be created.
+ * @param file Filename
+ * @param mode Mode (READ, WRITE or RW)
+ * @param size Size of the memory mapping (the file will be enlarged to this value)
+ * @return true upon succes
+ */
+ bool open(const QString & file,Mode mode,Uint64 size);
+
+ /**
+ * Close the file. Undoes the memory mapping.
+ */
+ void close();
+
+ /**
+ * Flush the file.
+ */
+ void flush();
+
+ /**
+ * Write a bunch of data.
+ * @param buf The data
+ * @param size Size of the data
+ * @return The number of bytes written
+ */
+ Uint32 write(const void* buf,Uint32 size);
+
+ /**
+ * Read a bunch of data
+ * @param buf The buffer to store the data
+ * @param size Size of the buffer
+ * @return The number of bytes read
+ */
+ Uint32 read(void* buf,Uint32 size);
+
+ enum SeekPos
+ {
+ BEGIN,
+ END,
+ CURRENT
+ };
+
+ /**
+ * Seek in the file.
+ * @param from Position to seek from
+ * @param num Number of bytes to move
+ * @return New position
+ */
+ Uint64 seek(SeekPos from,Int64 num);
+
+ /// Check to see if we are at the end of the file.
+ bool eof() const;
+
+ /// Get the current position in the file.
+ Uint64 tell() const;
+
+ /// Get the error string.
+ QString errorString() const;
+
+ /// Get the file size
+ Uint64 getSize() const;
+
+
+ /**
+ * Get a pointer to the mmapped region of data.
+ * @param off Offset into buffer, if invalid 0 will be returned
+ * @return Pointer to a location in the mmapped region
+ */
+ Uint8* getData(Uint64 off);
+
+ /// Gets the data pointer
+ void* getDataPointer() { return data; }
+ private:
+ void growFile(Uint64 new_size);
+
+ private:
+ int fd;
+ Uint8* data;
+ Uint64 size; // size of mmapping
+ Uint64 file_size; // size of file
+ Uint64 ptr;
+ QString filename;
+ Mode mode;
+ };
+
+}
+
+
+#endif
diff --git a/libktorrent/util/profiler.cpp b/libktorrent/util/profiler.cpp
new file mode 100644
index 0000000..05c53bd
--- /dev/null
+++ b/libktorrent/util/profiler.cpp
@@ -0,0 +1,138 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifdef KT_PROFILE
+#include <qfile.h>
+#include <qtextstream.h>
+#include <sys/time.h>
+#include "profiler.h"
+
+namespace bt
+{
+ Profile::Profile(Profile* parent,const QString & name) : parent(parent),name(name)
+ {
+ min = max = avg = 0.0;
+ count = 0;
+ start_time = 0.0;
+ children.setAutoDelete(true);
+ }
+
+ Profile::~Profile()
+ {
+ }
+
+ void Profile::start()
+ {
+ struct timeval tv;
+ gettimeofday(&tv,0);
+ start_time = tv.tv_sec * 1000.0 + tv.tv_usec * 0.001;
+ }
+
+ void Profile::end()
+ {
+ struct timeval tv;
+ gettimeofday(&tv,0);
+ double end_time = tv.tv_sec * 1000.0 + tv.tv_usec * 0.001;
+ double d = end_time - start_time;
+ // update stuff
+
+ if (d < min || count == 0)
+ min = d;
+ if (d > max || count == 0)
+ max = d;
+
+ avg = (avg * count + d) / (count + 1);
+ count++;
+ }
+
+ Profile* Profile::child(const QString & name)
+ {
+ QPtrList<Profile>::iterator i = children.begin();
+ while (i != children.end())
+ {
+ Profile* p = *i;
+ if (p->name == name)
+ return p;
+ i++;
+ }
+
+ Profile* p = new Profile(this,name);
+ children.append(p);
+ return p;
+ }
+
+ void Profile::save(QTextStream & out,const QString & base)
+ {
+ QString nb = base + "/" + name;
+
+ out.precision(5);
+ out << qSetW(60) << nb << qSetW(10) << min << qSetW(10) << max << qSetW(10) << avg << qSetW(10) << count << endl;
+
+ QPtrList<Profile>::iterator i = children.begin();
+ while (i != children.end())
+ {
+ Profile* p = *i;
+ p->save(out,nb);
+ i++;
+ }
+ }
+
+ /////////////////////
+
+ Profiler Profiler::inst;
+
+ Profiler::Profiler() : curr(0),root(0)
+ {
+ root = new Profile(0,"root");
+ curr = root;
+ }
+
+
+ Profiler::~Profiler()
+ {
+ delete root;
+ }
+
+ void Profiler::start(const QString & s)
+ {
+ curr = curr->child(s);
+ curr->start();
+ }
+
+ void Profiler::end()
+ {
+ curr->end();
+ curr = curr->getParent();
+ }
+
+ void Profiler::saveToFile(const QString & fn)
+ {
+ QFile fptr(fn);
+ if (!fptr.open(IO_WriteOnly))
+ return;
+
+ QTextStream out(&fptr);
+
+ out << qSetW(60) << "code" << qSetW(10) << "min" << qSetW(10) << "max" << qSetW(10) << "avg" << qSetW(10) << "count" << endl;
+ out << endl;
+
+ root->save(out,QString::null);
+ }
+}
+#endif
diff --git a/libktorrent/util/profiler.h b/libktorrent/util/profiler.h
new file mode 100644
index 0000000..6ab06e7
--- /dev/null
+++ b/libktorrent/util/profiler.h
@@ -0,0 +1,108 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef BTPROFILER_H
+#define BTPROFILER_H
+
+#ifdef KT_PROFILE
+#include <qptrlist.h>
+#include <util/constants.h>
+
+class QTextStream;
+
+
+namespace bt
+{
+ /**
+ * Profile of one function or section of code.
+ */
+ class Profile
+ {
+ Profile* parent;
+ QPtrList<Profile> children;
+
+ QString name;
+ double min,max,avg;
+ Uint32 count;
+ double start_time;
+ public:
+ Profile(Profile* parent,const QString & name);
+ virtual ~Profile();
+
+ /**
+ * We just entered the function and will profile it.
+ */
+ void start();
+
+ /**
+ * We just left the function, internal variables will now be updated
+ */
+ void end();
+
+ /**
+ * Get a child, if it doesn't exist it will be created.
+ * @param name The name of the child
+ * @return The child
+ */
+ Profile* child(const QString & name);
+
+ /**
+ * Get the parent of the current profile.
+ */
+ Profile* getParent() const {return parent;}
+
+ /**
+ * Save profile information to a file.
+ * @param out Text stream to write to
+ * @param base Base path of the profiles
+ */
+ void save(QTextStream & out,const QString & base);
+ };
+
+ /**
+ * @author Joris Guisson <joris.guisson@gmail.com>
+ *
+ * Class used to profile ktorrent
+ */
+ class Profiler
+ {
+ Profile* curr;
+ Profile* root;
+
+ static Profiler inst;
+
+ Profiler();
+ public:
+ virtual ~Profiler();
+
+ void start(const QString & s);
+ void end();
+ void saveToFile(const QString & fn);
+
+ static Profiler & instance() {return inst;}
+ };
+}
+#define KT_PROF_START(S) bt::Profiler::instance().start(S)
+#define KT_PROF_END() bt::Profiler::instance().end()
+#else
+#define KT_PROF_START(S)
+#define KT_PROF_END()
+#endif
+
+#endif
diff --git a/libktorrent/util/ptrmap.cpp b/libktorrent/util/ptrmap.cpp
new file mode 100644
index 0000000..28a4340
--- /dev/null
+++ b/libktorrent/util/ptrmap.cpp
@@ -0,0 +1,24 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include "ptrmap.h"
+
+
+
+
diff --git a/libktorrent/util/ptrmap.h b/libktorrent/util/ptrmap.h
new file mode 100644
index 0000000..36e1c20
--- /dev/null
+++ b/libktorrent/util/ptrmap.h
@@ -0,0 +1,181 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef BTPTRMAP_H
+#define BTPTRMAP_H
+
+#include <map>
+
+namespace bt
+{
+ /**
+ * @author Joris Guisson
+ * @brief Map of pointers
+ *
+ * A Map where the data is a pointer. The PtrMap has an autodeletion feature.
+ * When autodelete is on, every time we remove something from the map, the data
+ * will be deleted.
+ */
+ template <class Key,class Data>
+ class PtrMap
+ {
+ bool autodel;
+ std::map<Key,Data*> pmap;
+ public:
+ /**
+ * Constructor.
+ * @param auto_del Wether or not to enable auto deletion
+ */
+ PtrMap(bool autodel = false) : autodel(autodel)
+ {}
+
+ /**
+ * Destructor. Will delete all objects, if auto deletion is on.
+ */
+ virtual ~PtrMap()
+ {
+ clear();
+ }
+
+
+ /**
+ * Return the number of key data pairs in the map.
+ */
+ unsigned int count() const {return pmap.size();}
+
+ /**
+ * Enable or disable auto deletion.
+ * @param yes Enable if true, disable if false
+ */
+ void setAutoDelete(bool yes)
+ {
+ autodel = yes;
+ }
+
+ typedef typename std::map<Key,Data*>::iterator iterator;
+ typedef typename std::map<Key,Data*>::const_iterator const_iterator;
+
+ iterator begin() {return pmap.begin();}
+ iterator end() {return pmap.end();}
+
+ const_iterator begin() const {return pmap.begin();}
+ const_iterator end() const {return pmap.end();}
+
+ /**
+ * Remove all objects, will delete them if autodelete is on.
+ */
+ void clear()
+ {
+ if (autodel)
+ {
+ for (iterator i = pmap.begin();i != pmap.end();i++)
+ {
+ delete i->second;
+ i->second = 0;
+ }
+ }
+ pmap.clear();
+ }
+
+ /**
+ * Insert a key data pair.
+ * @param k The key
+ * @param d The data
+ * @param overwrite Wether or not to overwrite
+ * @return true if the insertion took place
+ */
+ bool insert(const Key & k,Data* d,bool overwrite = true)
+ {
+ iterator itr = pmap.find(k);
+ if (itr != pmap.end())
+ {
+ if (overwrite)
+ {
+ if (autodel)
+ delete itr->second;
+ itr->second = d;
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+ else
+ {
+ pmap[k] = d;
+ return true;
+ }
+ }
+
+ /**
+ * Find a key in the map and returns it's data.
+ * @param k The key
+ * @return The data of the key, 0 if the key isn't in the map
+ */
+ Data* find(const Key & k)
+ {
+ iterator i = pmap.find(k);
+ return (i == pmap.end()) ? 0 : i->second;
+ }
+
+ /**
+ * Find a key in the map and returns it's data.
+ * @param k The key
+ * @return The data of the key, 0 if the key isn't in the map
+ */
+ const Data* find(const Key & k) const
+ {
+ const_iterator i = pmap.find(k);
+ return (i == pmap.end()) ? 0 : i->second;
+ }
+
+ /**
+ * Check to see if a key is in the map.
+ * @param k The key
+ * @return true if it is part of the map
+ */
+ bool contains(const Key & k) const
+ {
+ const_iterator i = pmap.find(k);
+ return i != pmap.end();
+ }
+
+ /**
+ * Erase a key from the map. Will delete
+ * the data if autodelete is on.
+ * @param key The key
+ * @return true if an erase took place
+ */
+ bool erase(const Key & key)
+ {
+ iterator i = pmap.find(key);
+ if (i == pmap.end())
+ return false;
+
+ if (autodel)
+ delete i->second;
+ pmap.erase(i);
+ return true;
+ }
+ };
+
+}
+
+#endif
diff --git a/libktorrent/util/sha1hash.cpp b/libktorrent/util/sha1hash.cpp
new file mode 100644
index 0000000..c7b151c
--- /dev/null
+++ b/libktorrent/util/sha1hash.cpp
@@ -0,0 +1,131 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <qurl.h>
+#include <string.h>
+#include <algorithm>
+#include "log.h"
+#include "sha1hash.h"
+#include "sha1hashgen.h"
+#include "urlencoder.h"
+
+#include <stdio.h>
+
+namespace bt
+{
+ SHA1Hash::SHA1Hash()
+ {
+ std::fill(hash,hash+20,'\0');
+ }
+
+ SHA1Hash::SHA1Hash(const SHA1Hash & other)
+ {
+ for (int i = 0;i < 20;i++)
+ hash[i] = other.hash[i];
+ }
+
+ SHA1Hash::SHA1Hash(const Uint8* h)
+ {
+ memcpy(hash,h,20);
+ }
+
+
+ SHA1Hash::~SHA1Hash()
+ {}
+
+ SHA1Hash & SHA1Hash::operator = (const SHA1Hash & other)
+ {
+ for (int i = 0;i < 20;i++)
+ hash[i] = other.hash[i];
+ return *this;
+ }
+
+ bool SHA1Hash::operator == (const SHA1Hash & other) const
+ {
+ for (int i = 0;i < 20;i++)
+ if (hash[i] != other.hash[i])
+ return false;
+
+ return true;
+ }
+
+ SHA1Hash SHA1Hash::generate(const Uint8* data,Uint32 len)
+ {
+ SHA1HashGen hg;
+
+ return hg.generate(data,len);
+ }
+
+ QString SHA1Hash::toString() const
+ {
+ char tmp[41];
+ QString fmt;
+ for (int i = 0;i < 20;i++)
+ fmt += "%02x";
+ tmp[40] = '\0';
+ snprintf(tmp,41,fmt.ascii(),
+ hash[0],hash[1],hash[2],hash[3],hash[4],
+ hash[5],hash[6],hash[7],hash[8],hash[9],
+ hash[10],hash[11],hash[12],hash[13],hash[14],
+ hash[15],hash[16],hash[17],hash[18],hash[19]);
+ return QString(tmp);
+ }
+
+ QByteArray SHA1Hash::toByteArray() const
+ {
+ QByteArray arr(20);
+ arr.duplicate((const char*)hash,20);
+ return arr;
+ }
+
+ QString SHA1Hash::toURLString() const
+ {
+ return URLEncoder::encode((const char*)hash,20);
+ }
+
+ Log & operator << (Log & out,const SHA1Hash & h)
+ {
+ out << h.toString();
+ return out;
+ }
+
+ SHA1Hash operator ^ (const SHA1Hash & a,const SHA1Hash & b)
+ {
+ SHA1Hash k;
+ for (int i = 0;i < 20;i++)
+ {
+ k.hash[i] = a.hash[i] ^ b.hash[i];
+ }
+ return k;
+ }
+
+ bool operator < (const SHA1Hash & a,const SHA1Hash & b)
+ {
+ for (int i = 0;i < 20;i++)
+ {
+ if (a.hash[i] < b.hash[i])
+ return true;
+ else if (a.hash[i] > b.hash[i])
+ return false;
+ }
+
+ return false;
+ }
+}
+
diff --git a/libktorrent/util/sha1hash.h b/libktorrent/util/sha1hash.h
new file mode 100644
index 0000000..a831d2d
--- /dev/null
+++ b/libktorrent/util/sha1hash.h
@@ -0,0 +1,148 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef BTSHA1HASH_H
+#define BTSHA1HASH_H
+
+#include <qcstring.h>
+#include "constants.h"
+
+class QString;
+
+namespace bt
+{
+ class Log;
+
+ /**
+ * @author Joris Guisson
+ * @brief Stores a SHA1 hash
+ *
+ * This class keeps track of a SHA1 hash. A SHA1 hash is a 20 byte
+ * array of bytes.
+ */
+ class SHA1Hash
+ {
+ protected:
+ Uint8 hash[20];
+ public:
+ /**
+ * Constructor, sets every byte in the hash to 0.
+ */
+ SHA1Hash();
+
+ /**
+ * Copy constructor.
+ * @param other Hash to copy
+ */
+ SHA1Hash(const SHA1Hash & other);
+
+ /**
+ * Directly set the hash data.
+ * @param h The hash data must be 20 bytes large
+ */
+ SHA1Hash(const Uint8* h);
+
+ /**
+ * Destructor.
+ */
+ virtual ~SHA1Hash();
+
+ /// Get the idx'th byte of the hash.
+ Uint8 operator [] (const Uint32 idx) const {return idx < 20 ? hash[idx] : 0;}
+
+ /**
+ * Assignment operator.
+ * @param other Hash to copy
+ */
+ SHA1Hash & operator = (const SHA1Hash & other);
+
+ /**
+ * Test wether another hash is equal to this one.
+ * @param other The other hash
+ * @return true if equal, false otherwise
+ */
+ bool operator == (const SHA1Hash & other) const;
+
+ /**
+ * Test wether another hash is not equal to this one.
+ * @param other The other hash
+ * @return true if not equal, false otherwise
+ */
+ bool operator != (const SHA1Hash & other) const {return !operator ==(other);}
+
+ /**
+ * Generate an SHA1 hash from a bunch of data.
+ * @param data The data
+ * @param len Size in bytes of data
+ * @return The generated SHA1 hash
+ */
+ static SHA1Hash generate(const Uint8* data,Uint32 len);
+
+ /**
+ * Convert the hash to a printable string.
+ * @return The string
+ */
+ QString toString() const;
+
+ /**
+ * Convert the hash to a string, usable in http get requests.
+ * @return The string
+ */
+ QString toURLString() const;
+
+ /**
+ * Directly get pointer to the data.
+ * @return The data
+ */
+ const Uint8* getData() const {return hash;}
+
+ /**
+ * Function to print a SHA1Hash to the Log.
+ * @param out The Log
+ * @param h The hash
+ * @return out
+ */
+ friend Log & operator << (Log & out,const SHA1Hash & h);
+
+
+ /**
+ * XOR two SHA1Hashes
+ * @param a The first hash
+ * @param b The second
+ * @return a xor b
+ */
+ friend SHA1Hash operator ^ (const SHA1Hash & a,const SHA1Hash & b);
+
+ /**
+ * Function to compare 2 hashes
+ * @param a The first hash
+ * @param h The second hash
+ * @return wether a is smaller then b
+ */
+ friend bool operator < (const SHA1Hash & a,const SHA1Hash & b);
+
+ /**
+ * Convert the hash to a byte array.
+ */
+ QByteArray toByteArray() const;
+ };
+
+}
+
+#endif
diff --git a/libktorrent/util/sha1hashgen.cpp b/libktorrent/util/sha1hashgen.cpp
new file mode 100644
index 0000000..5c0d9f5
--- /dev/null
+++ b/libktorrent/util/sha1hashgen.cpp
@@ -0,0 +1,340 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <string.h>
+#include <arpa/inet.h>
+#include "sha1hashgen.h"
+#include "functions.h"
+
+
+
+namespace bt
+{
+ static inline Uint32 LeftRotate(Uint32 x,Uint32 n)
+ {
+ return ((x << n) | ((x & 0xFFFFFFFF) >> (32 - n)));
+ }
+
+
+
+ SHA1HashGen::SHA1HashGen() : tmp_len(0),total_len(0)
+ {
+
+ }
+
+
+ SHA1HashGen::~SHA1HashGen()
+ {}
+
+ SHA1Hash SHA1HashGen::generate(const Uint8* data,Uint32 len)
+ {
+ h0 = 0x67452301;
+ h1 = 0xEFCDAB89;
+ h2 = 0x98BADCFE;
+ h3 = 0x10325476;
+ h4 = 0xC3D2E1F0;
+
+ Uint32 num_64_byte_chunks = len / 64;
+ Uint32 left_over = len % 64;
+ // proces regular data
+ for (Uint32 i = 0;i < num_64_byte_chunks;i++)
+ {
+ processChunk(data + (64*i));
+ }
+
+ // calculate the low and high byte of the data length
+ Uint32 total[2] = {0,0};
+ total[0] += len;
+ total[0] &= 0xFFFFFFFF;
+
+ if (total[0] < len)
+ total[1]++;
+
+ Uint32 high = ( total[0] >> 29 ) | ( total[1] << 3 );
+ Uint32 low = ( total[0] << 3 );
+
+ if (left_over == 0)
+ {
+ tmp[0] = 0x80;
+ for (Uint32 i = 1;i < 56;i++)
+ tmp[i] = 0;
+
+ // put in the length as 64-bit integer (BIG-ENDIAN)
+ WriteUint32(tmp,56,high);
+ WriteUint32(tmp,60,low);
+ // process the padding
+ processChunk(tmp);
+ }
+ else if (left_over < 56)
+ {
+ Uint32 off = num_64_byte_chunks * 64;
+ // copy left over bytes in tmp
+ memcpy(tmp,data + off, left_over);
+ tmp[left_over] = 0x80;
+ for (Uint32 i = left_over + 1;i < 56;i++)
+ tmp[i] = 0;
+
+ // put in the length as 64-bit integer (BIG-ENDIAN)
+ WriteUint32(tmp,56,high);
+ WriteUint32(tmp,60,low);
+ // process the padding
+ processChunk(tmp);
+ }
+ else
+ {
+ // now we need to process 2 chunks
+ Uint32 off = num_64_byte_chunks * 64;
+ // copy left over bytes in tmp
+ memcpy(tmp,data + off, left_over);
+ tmp[left_over] = 0x80;
+ for (Uint32 i = left_over + 1;i < 64;i++)
+ tmp[i] = 0;
+
+ // process first chunk
+ processChunk(tmp);
+
+ for (Uint32 i = 0;i < 56;i++)
+ tmp[i] = 0;
+
+ // put in the length as 64-bit integer (BIG-ENDIAN)
+ WriteUint32(tmp,56,high);
+ WriteUint32(tmp,60,low);
+ // process the second chunk
+ processChunk(tmp);
+ }
+
+ // construct final message
+ Uint8 hash[20];
+ WriteUint32(hash,0,h0);
+ WriteUint32(hash,4,h1);
+ WriteUint32(hash,8,h2);
+ WriteUint32(hash,12,h3);
+ WriteUint32(hash,16,h4);
+
+ return SHA1Hash(hash);
+ }
+
+
+
+ void SHA1HashGen::processChunk(const Uint8* chunk)
+ {
+ Uint32 w[80];
+ for (int i = 0;i < 80;i++)
+ {
+ if (i < 16)
+ {
+ w[i] = ntohl(*(const Uint32*)(chunk + (4*i)));
+ /* w[i] = (chunk[4*i] << 24) |
+ (chunk[4*i + 1] << 16) |
+ (chunk[4*i + 2] << 8) |
+ chunk[4*i + 3];
+ */
+ }
+ else
+ {
+ w[i] = LeftRotate(w[i-3] ^ w[i-8] ^ w[i-14] ^ w[i-16],1);
+ }
+ }
+
+ Uint32 a = h0;
+ Uint32 b = h1;
+ Uint32 c = h2;
+ Uint32 d = h3;
+ Uint32 e = h4;
+
+ for (int i = 0;i < 80;i++)
+ {
+ Uint32 f,k;
+ if (i < 20)
+ {
+ f = (b & c) | ((~b) & d);
+ k = 0x5A827999;
+ }
+ else if (i < 40)
+ {
+ f = b ^ c ^ d;
+ k = 0x6ED9EBA1;
+ }
+ else if (i < 60)
+ {
+ f = (b & c) | (b & d) | (c & d);
+ k = 0x8F1BBCDC;
+ }
+ else
+ {
+ f = b ^ c ^ d;
+ k = 0xCA62C1D6;
+ }
+
+ Uint32 temp = LeftRotate(a,5) + f + e + k + w[i];
+ e = d;
+ d = c;
+ c = LeftRotate(b,30);
+ b = a;
+ a = temp;
+ }
+ h0 = (h0 + a) & 0xffffffff;
+ h1 = (h1 + b) & 0xffffffff;
+ h2 = (h2 + c) & 0xffffffff;
+ h3 = (h3 + d) & 0xffffffff;
+ h4 = (h4 + e) & 0xffffffff;
+ }
+
+
+ void SHA1HashGen::start()
+ {
+ h0 = 0x67452301;
+ h1 = 0xEFCDAB89;
+ h2 = 0x98BADCFE;
+ h3 = 0x10325476;
+ h4 = 0xC3D2E1F0;
+ tmp_len = total_len = 0;
+ memset(tmp,0,64);
+ }
+
+ void SHA1HashGen::update(const Uint8* data,Uint32 len)
+ {
+ if (tmp_len == 0)
+ {
+ Uint32 num_64_byte_chunks = len / 64;
+ Uint32 left_over = len % 64;
+ // proces data in chunks of 64 byte
+ for (Uint32 i = 0;i < num_64_byte_chunks;i++)
+ {
+ processChunk(data + (64*i));
+ }
+
+ if (left_over > 0)
+ {
+ // if there is anything left over, copy it in tmp
+ memcpy(tmp,data + (64 * num_64_byte_chunks),left_over);
+ tmp_len = left_over;
+ }
+ total_len += len;
+ }
+ else
+ {
+
+ if (tmp_len + len < 64)
+ {
+ // special case, not enough of data to fill tmp completely
+ memcpy(tmp + tmp_len,data,len);
+ tmp_len += len;
+ total_len += len;
+ }
+ else
+ {
+ // copy start of data in tmp and process it
+ Uint32 off = 64 - tmp_len;
+ memcpy(tmp + tmp_len,data, 64 - tmp_len);
+ processChunk(tmp);
+ tmp_len = 0;
+
+ Uint32 num_64_byte_chunks = (len - off) / 64;
+ Uint32 left_over = (len - off) % 64;
+
+ for (Uint32 i = 0;i < num_64_byte_chunks;i++)
+ {
+ processChunk(data + (off + (64*i)));
+ }
+
+ if (left_over > 0)
+ {
+ // if there is anything left over, copy it in tmp
+ memcpy(tmp,data + (off + 64 * num_64_byte_chunks),left_over);
+ tmp_len = left_over;
+ }
+ total_len += len;
+ }
+ }
+ }
+
+
+ void SHA1HashGen::end()
+ {
+ // calculate the low and high byte of the data length
+ Uint32 total[2] = {0,0};
+ total[0] += total_len;
+ total[0] &= 0xFFFFFFFF;
+
+ if (total[0] < total_len)
+ total[1]++;
+
+ Uint32 high = ( total[0] >> 29 ) | ( total[1] << 3 );
+ Uint32 low = ( total[0] << 3 );
+
+ if (tmp_len == 0)
+ {
+ tmp[0] = 0x80;
+ for (Uint32 i = 1;i < 56;i++)
+ tmp[i] = 0;
+
+ // put in the length as 64-bit integer (BIG-ENDIAN)
+ WriteUint32(tmp,56,high);
+ WriteUint32(tmp,60,low);
+ // process the padding
+ processChunk(tmp);
+ }
+ else if (tmp_len < 56)
+ {
+ tmp[tmp_len] = 0x80;
+ for (Uint32 i = tmp_len + 1;i < 56;i++)
+ tmp[i] = 0;
+
+ // put in the length as 64-bit integer (BIG-ENDIAN)
+ WriteUint32(tmp,56,high);
+ WriteUint32(tmp,60,low);
+ // process the padding
+ processChunk(tmp);
+ }
+ else
+ {
+ // now we need to process 2 chunks
+ tmp[tmp_len] = 0x80;
+ for (Uint32 i = tmp_len + 1;i < 56;i++)
+ tmp[i] = 0;
+
+ // process first chunk
+ processChunk(tmp);
+
+ for (Uint32 i = 0;i < 56;i++)
+ tmp[i] = 0;
+
+ // put in the length as 64-bit integer (BIG-ENDIAN)
+ WriteUint32(tmp,56,high);
+ WriteUint32(tmp,60,low);
+ // process the second chunk
+ processChunk(tmp);
+ }
+ }
+
+
+ SHA1Hash SHA1HashGen::get() const
+ {
+ // construct final message
+ Uint8 hash[20];
+ WriteUint32(hash,0,h0);
+ WriteUint32(hash,4,h1);
+ WriteUint32(hash,8,h2);
+ WriteUint32(hash,12,h3);
+ WriteUint32(hash,16,h4);
+
+ return SHA1Hash(hash);
+ }
+}
diff --git a/libktorrent/util/sha1hashgen.h b/libktorrent/util/sha1hashgen.h
new file mode 100644
index 0000000..08cc3ad
--- /dev/null
+++ b/libktorrent/util/sha1hashgen.h
@@ -0,0 +1,90 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef BTSHA1HASHGEN_H
+#define BTSHA1HASHGEN_H
+
+#include "constants.h"
+#include "sha1hash.h"
+
+namespace bt
+{
+
+ /**
+ * @author Joris Guisson
+ *
+ * Generates a SHA1 hash, code based on wikipedia's pseudocode
+ * There are 2 ways to use this class :
+ * - generate : all data is present from the start
+ * - start, update and end : data can be delivered in chunks
+ *
+ * Mixing the 2, is not a good idea
+ */
+ class SHA1HashGen
+ {
+ Uint32 h0;
+ Uint32 h1;
+ Uint32 h2;
+ Uint32 h3;
+ Uint32 h4;
+ Uint8 tmp[64];
+ Uint32 tmp_len;
+ Uint32 total_len;
+ public:
+ SHA1HashGen();
+ ~SHA1HashGen();
+
+ /**
+ * Generate a hash from a bunch of data.
+ * @param data The data
+ * @param len The length
+ * @return The SHA1 hash
+ */
+ SHA1Hash generate(const Uint8* data,Uint32 len);
+
+ /**
+ * Start SHA1 hash generation in chunks.
+ */
+ void start();
+
+ /**
+ * Update the hash.
+ * @param data The data
+ * @param len Length of the data
+ */
+ void update(const Uint8* data,Uint32 len);
+
+
+ /**
+ * All data has been delivered, calculate the final hash.
+ * @return
+ */
+ void end();
+
+ /**
+ * Get the hash generated.
+ */
+ SHA1Hash get() const;
+ private:
+ void processChunk(const Uint8* c);
+ };
+
+}
+
+#endif
diff --git a/libktorrent/util/timer.cpp b/libktorrent/util/timer.cpp
new file mode 100644
index 0000000..c06b728
--- /dev/null
+++ b/libktorrent/util/timer.cpp
@@ -0,0 +1,64 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+
+#include "timer.h"
+
+namespace bt
+{
+
+ Timer::Timer() : elapsed(0)
+ {
+ last = QTime::currentTime();
+ }
+
+ Timer::Timer(const Timer & t) : last(t.last),elapsed(t.elapsed)
+ {}
+
+ Timer::~Timer()
+ {}
+
+
+ void Timer::update()
+ {
+ QTime now = QTime::currentTime();
+
+ int d = last.msecsTo(now);
+ if (d < 0)
+ d = 0;
+ elapsed = d;
+ last = now;
+ }
+
+ Uint32 Timer::getElapsedSinceUpdate() const
+ {
+ QTime now = QTime::currentTime();
+ int d = last.msecsTo(now);
+ if (d < 0)
+ d = 0;
+ return d;
+ }
+
+ Timer & Timer::operator = (const Timer & t)
+ {
+ last = t.last;
+ elapsed = t.elapsed;
+ return *this;
+ }
+}
diff --git a/libktorrent/util/timer.h b/libktorrent/util/timer.h
new file mode 100644
index 0000000..3277185
--- /dev/null
+++ b/libktorrent/util/timer.h
@@ -0,0 +1,49 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef BTTIMER_H
+#define BTTIMER_H
+
+#include <qdatetime.h>
+#include "constants.h"
+
+namespace bt
+{
+
+ /**
+ @author Joris Guisson
+ */
+ class Timer
+ {
+ QTime last;
+ Uint32 elapsed;
+ public:
+ Timer();
+ Timer(const Timer & t);
+ virtual ~Timer();
+
+ void update();
+ Uint32 getElapsed() const {return elapsed;}
+ Uint32 getElapsedSinceUpdate() const;
+ Timer & operator = (const Timer & t);
+ };
+
+}
+
+#endif
diff --git a/libktorrent/util/urlencoder.cpp b/libktorrent/util/urlencoder.cpp
new file mode 100644
index 0000000..c1776de
--- /dev/null
+++ b/libktorrent/util/urlencoder.cpp
@@ -0,0 +1,92 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include "urlencoder.h"
+
+
+namespace bt
+{
+ QString hex[] = {
+ "%00", "%01", "%02", "%03", "%04", "%05", "%06", "%07",
+ "%08", "%09", "%0a", "%0b", "%0c", "%0d", "%0e", "%0f",
+ "%10", "%11", "%12", "%13", "%14", "%15", "%16", "%17",
+ "%18", "%19", "%1a", "%1b", "%1c", "%1d", "%1e", "%1f",
+ "%20", "%21", "%22", "%23", "%24", "%25", "%26", "%27",
+ "%28", "%29", "%2a", "%2b", "%2c", "%2d", "%2e", "%2f",
+ "%30", "%31", "%32", "%33", "%34", "%35", "%36", "%37",
+ "%38", "%39", "%3a", "%3b", "%3c", "%3d", "%3e", "%3f",
+ "%40", "%41", "%42", "%43", "%44", "%45", "%46", "%47",
+ "%48", "%49", "%4a", "%4b", "%4c", "%4d", "%4e", "%4f",
+ "%50", "%51", "%52", "%53", "%54", "%55", "%56", "%57",
+ "%58", "%59", "%5a", "%5b", "%5c", "%5d", "%5e", "%5f",
+ "%60", "%61", "%62", "%63", "%64", "%65", "%66", "%67",
+ "%68", "%69", "%6a", "%6b", "%6c", "%6d", "%6e", "%6f",
+ "%70", "%71", "%72", "%73", "%74", "%75", "%76", "%77",
+ "%78", "%79", "%7a", "%7b", "%7c", "%7d", "%7e", "%7f",
+ "%80", "%81", "%82", "%83", "%84", "%85", "%86", "%87",
+ "%88", "%89", "%8a", "%8b", "%8c", "%8d", "%8e", "%8f",
+ "%90", "%91", "%92", "%93", "%94", "%95", "%96", "%97",
+ "%98", "%99", "%9a", "%9b", "%9c", "%9d", "%9e", "%9f",
+ "%a0", "%a1", "%a2", "%a3", "%a4", "%a5", "%a6", "%a7",
+ "%a8", "%a9", "%aa", "%ab", "%ac", "%ad", "%ae", "%af",
+ "%b0", "%b1", "%b2", "%b3", "%b4", "%b5", "%b6", "%b7",
+ "%b8", "%b9", "%ba", "%bb", "%bc", "%bd", "%be", "%bf",
+ "%c0", "%c1", "%c2", "%c3", "%c4", "%c5", "%c6", "%c7",
+ "%c8", "%c9", "%ca", "%cb", "%cc", "%cd", "%ce", "%cf",
+ "%d0", "%d1", "%d2", "%d3", "%d4", "%d5", "%d6", "%d7",
+ "%d8", "%d9", "%da", "%db", "%dc", "%dd", "%de", "%df",
+ "%e0", "%e1", "%e2", "%e3", "%e4", "%e5", "%e6", "%e7",
+ "%e8", "%e9", "%ea", "%eb", "%ec", "%ed", "%ee", "%ef",
+ "%f0", "%f1", "%f2", "%f3", "%f4", "%f5", "%f6", "%f7",
+ "%f8", "%f9", "%fa", "%fb", "%fc", "%fd", "%fe", "%ff"
+ };
+
+
+ QString URLEncoder::encode(const char* buf,Uint32 size)
+ {
+ QString res = "";
+
+ for (Uint32 i = 0; i < size; i++)
+ {
+ Uint8 ch = buf[i];
+ if (('A' <= ch && ch <= 'Z') || ('a' <= ch && ch <= 'z') || ('0' <= ch && ch <= '9'))
+ { // 'A'..'Z'
+ res.append((char)ch);
+ }
+ else if (ch == ' ')
+ { // space
+ res.append("%20");
+ }
+ else if (ch == '-' || ch == '_' // unreserved
+ || ch == '.' || ch == '!'
+ || ch == '~' || ch == '*'
+ || ch == '\'' || ch == '('
+ || ch == ')')
+ {
+ res.append((char)ch);
+ }
+ else
+ { // other ASCII
+ res.append(hex[ch]);
+ }
+ }
+ return res;
+ }
+
+}
diff --git a/libktorrent/util/urlencoder.h b/libktorrent/util/urlencoder.h
new file mode 100644
index 0000000..edac33a
--- /dev/null
+++ b/libktorrent/util/urlencoder.h
@@ -0,0 +1,40 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef BTURLENCODER_H
+#define BTURLENCODER_H
+
+#include <qstring.h>
+#include "constants.h"
+
+namespace bt
+{
+
+ /**
+ @author Joris Guisson
+ */
+ class URLEncoder
+ {
+ public:
+ static QString encode(const char* buf,Uint32 size);
+ };
+
+}
+
+#endif
diff --git a/libktorrent/util/waitjob.cpp b/libktorrent/util/waitjob.cpp
new file mode 100644
index 0000000..d11fa14
--- /dev/null
+++ b/libktorrent/util/waitjob.cpp
@@ -0,0 +1,86 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <torrent/globals.h>
+#include <kio/netaccess.h>
+#include "waitjob.h"
+#include "log.h"
+
+namespace bt
+{
+
+ WaitJob::WaitJob(Uint32 millis) : KIO::Job(false)
+ {
+ connect(&timer,SIGNAL(timeout()),this,SLOT(timerDone()));
+ timer.start(millis,true);
+ }
+
+
+ WaitJob::~WaitJob()
+ {}
+
+ void WaitJob::kill(bool)
+ {
+ m_error = 0;
+ emitResult();
+ }
+
+ void WaitJob::timerDone()
+ {
+ // set the error to null and emit the result
+ m_error = 0;
+ emitResult();
+ }
+
+ void WaitJob::addExitOperation(kt::ExitOperation* op)
+ {
+ exit_ops.append(op);
+ connect(op,SIGNAL(operationFinished( kt::ExitOperation* )),
+ this,SLOT(operationFinished( kt::ExitOperation* )));
+ }
+
+ void WaitJob::operationFinished(kt::ExitOperation* op)
+ {
+ if (exit_ops.count() > 0)
+ {
+ exit_ops.remove(op);
+ if (op->deleteAllowed())
+ op->deleteLater();
+
+ if (exit_ops.count() == 0)
+ timerDone();
+ }
+ }
+
+ void WaitJob::execute(WaitJob* job)
+ {
+ KIO::NetAccess::synchronousRun(job,0);
+ }
+
+ void SynchronousWait(Uint32 millis)
+ {
+ Out() << "SynchronousWait" << endl;
+ WaitJob* j = new WaitJob(millis);
+ KIO::NetAccess::synchronousRun(j,0);
+ }
+
+}
+
+#include "waitjob.moc"
+
diff --git a/libktorrent/util/waitjob.h b/libktorrent/util/waitjob.h
new file mode 100644
index 0000000..a85ba63
--- /dev/null
+++ b/libktorrent/util/waitjob.h
@@ -0,0 +1,78 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef BTWAITJOB_H
+#define BTWAITJOB_H
+
+#include <qtimer.h>
+#include <kio/job.h>
+#include <qvaluelist.h>
+#include <interfaces/exitoperation.h>
+#include "constants.h"
+
+namespace bt
+{
+
+ /**
+ * @author Joris Guisson <joris.guisson@gmail.com>
+ *
+ * Job to wait for a certain amount of time or until one or more ExitOperation's have
+ * finished.
+ */
+ class WaitJob : public KIO::Job
+ {
+ Q_OBJECT
+ public:
+ WaitJob(Uint32 millis);
+ virtual ~WaitJob();
+
+ virtual void kill(bool quietly=true);
+
+ /**
+ * Add an ExitOperation;
+ * @param op The operation
+ */
+ void addExitOperation(kt::ExitOperation* op);
+
+
+ /**
+ * Execute a WaitJob
+ * @param job The Job
+ */
+ static void execute(WaitJob* job);
+
+ /// Are there any ExitOperation's we need to wait for
+ bool needToWait() const {return exit_ops.count() > 0;}
+
+ private slots:
+ void timerDone();
+ void operationFinished(kt::ExitOperation* op);
+
+ private:
+ QTimer timer;
+ QValueList<kt::ExitOperation*> exit_ops;
+ };
+
+ void SynchronousWait(Uint32 millis);
+
+
+
+}
+
+#endif