| 1 | /**************************************************************************** | 
| 2 | ** | 
| 3 | ** Copyright (C) 2016 The Qt Company Ltd. | 
| 4 | ** Contact: https://www.qt.io/licensing/ | 
| 5 | ** | 
| 6 | ** This file is part of the examples of the Qt Toolkit. | 
| 7 | ** | 
| 8 | ** $QT_BEGIN_LICENSE:BSD$ | 
| 9 | ** Commercial License Usage | 
| 10 | ** Licensees holding valid commercial Qt licenses may use this file in | 
| 11 | ** accordance with the commercial license agreement provided with the | 
| 12 | ** Software or, alternatively, in accordance with the terms contained in | 
| 13 | ** a written agreement between you and The Qt Company. For licensing terms | 
| 14 | ** and conditions see https://www.qt.io/terms-conditions. For further | 
| 15 | ** information use the contact form at https://www.qt.io/contact-us. | 
| 16 | ** | 
| 17 | ** BSD License Usage | 
| 18 | ** Alternatively, you may use this file under the terms of the BSD license | 
| 19 | ** as follows: | 
| 20 | ** | 
| 21 | ** "Redistribution and use in source and binary forms, with or without | 
| 22 | ** modification, are permitted provided that the following conditions are | 
| 23 | ** met: | 
| 24 | **   * Redistributions of source code must retain the above copyright | 
| 25 | **     notice, this list of conditions and the following disclaimer. | 
| 26 | **   * Redistributions in binary form must reproduce the above copyright | 
| 27 | **     notice, this list of conditions and the following disclaimer in | 
| 28 | **     the documentation and/or other materials provided with the | 
| 29 | **     distribution. | 
| 30 | **   * Neither the name of The Qt Company Ltd nor the names of its | 
| 31 | **     contributors may be used to endorse or promote products derived | 
| 32 | **     from this software without specific prior written permission. | 
| 33 | ** | 
| 34 | ** | 
| 35 | ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | 
| 36 | ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | 
| 37 | ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | 
| 38 | ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | 
| 39 | ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | 
| 40 | ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | 
| 41 | ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | 
| 42 | ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 
| 43 | ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 
| 44 | ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 
| 45 | ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." | 
| 46 | ** | 
| 47 | ** $QT_END_LICENSE$ | 
| 48 | ** | 
| 49 | ****************************************************************************/ | 
| 50 |  | 
| 51 | #include "connectionmanager.h" | 
| 52 | #include "filemanager.h" | 
| 53 | #include "metainfo.h" | 
| 54 | #include "torrentclient.h" | 
| 55 | #include "torrentserver.h" | 
| 56 | #include "trackerclient.h" | 
| 57 | #include "peerwireclient.h" | 
| 58 | #include "ratecontroller.h" | 
| 59 |  | 
| 60 | #include <QtCore> | 
| 61 | #include <QNetworkInterface> | 
| 62 |  | 
| 63 | #include <algorithm> | 
| 64 |  | 
| 65 | // These constants could also be configurable by the user. | 
| 66 | static const int ServerMinPort = 6881; | 
| 67 | static const int ServerMaxPort = /* 6889 */ 7000; | 
| 68 | static const int BlockSize = 16384; | 
| 69 | static const int MaxBlocksInProgress = 5; | 
| 70 | static const int MaxBlocksInMultiMode = 2; | 
| 71 | static const int MaxConnectionPerPeer = 1; | 
| 72 | static const int RateControlWindowLength = 10; | 
| 73 | static const int RateControlTimerDelay = 1000; | 
| 74 | static const int MinimumTimeBeforeRevisit = 30; | 
| 75 | static const int MaxUploads = 4; | 
| 76 | static const int UploadScheduleInterval = 10000; | 
| 77 |  | 
| 78 | struct TorrentPiece { | 
| 79 |     QBitArray completedBlocks; | 
| 80 |     QBitArray requestedBlocks; | 
| 81 |     int index = 0; | 
| 82 |     int length = 0; | 
| 83 |     bool inProgress = false; | 
| 84 | }; | 
| 85 |  | 
| 86 | class TorrentClientPrivate | 
| 87 | { | 
| 88 | public: | 
| 89 |     TorrentClientPrivate(TorrentClient *qq); | 
| 90 |  | 
| 91 |     // State / error | 
| 92 |     void setError(TorrentClient::Error error); | 
| 93 |     void setState(TorrentClient::State state); | 
| 94 |     TorrentClient::Error error; | 
| 95 |     TorrentClient::State state; | 
| 96 |     QString errorString; | 
| 97 |     QString stateString; | 
| 98 |  | 
| 99 |     // Where to save data | 
| 100 |     QString destinationFolder; | 
| 101 |     MetaInfo metaInfo; | 
| 102 |  | 
| 103 |     // Announce tracker and file manager | 
| 104 |     QByteArray peerId; | 
| 105 |     QByteArray infoHash; | 
| 106 |     TrackerClient trackerClient; | 
| 107 |     FileManager fileManager; | 
| 108 |  | 
| 109 |     // Connections | 
| 110 |     QList<PeerWireClient *> connections; | 
| 111 |     QList<TorrentPeer *> peers; | 
| 112 |     bool schedulerCalled; | 
| 113 |     void callScheduler(); | 
| 114 |     bool connectingToClients; | 
| 115 |     void callPeerConnector(); | 
| 116 |     int uploadScheduleTimer; | 
| 117 |  | 
| 118 |     // Pieces | 
| 119 |     QMap<int, PeerWireClient *> readIds; | 
| 120 |     QMultiMap<PeerWireClient *, TorrentPiece *> payloads; | 
| 121 |     QMap<int, TorrentPiece *> pendingPieces; | 
| 122 |     QBitArray completedPieces; | 
| 123 |     QBitArray incompletePieces; | 
| 124 |     int pieceCount; | 
| 125 |  | 
| 126 |     // Progress | 
| 127 |     int lastProgressValue; | 
| 128 |     qint64 downloadedBytes; | 
| 129 |     qint64 uploadedBytes; | 
| 130 |     int downloadRate[RateControlWindowLength]; | 
| 131 |     int uploadRate[RateControlWindowLength]; | 
| 132 |     int transferRateTimer; | 
| 133 |  | 
| 134 |     TorrentClient *q; | 
| 135 | }; | 
| 136 |  | 
| 137 | TorrentClientPrivate::TorrentClientPrivate(TorrentClient *qq) | 
| 138 |     : trackerClient(qq), q(qq) | 
| 139 | { | 
| 140 |     error = TorrentClient::UnknownError; | 
| 141 |     state = TorrentClient::Idle; | 
| 142 |     errorString = QT_TRANSLATE_NOOP(TorrentClient, "Unknown error" ); | 
| 143 |     stateString = QT_TRANSLATE_NOOP(TorrentClient, "Idle" ); | 
| 144 |     schedulerCalled = false; | 
| 145 |     connectingToClients = false; | 
| 146 |     uploadScheduleTimer = 0; | 
| 147 |     lastProgressValue = -1; | 
| 148 |     pieceCount = 0; | 
| 149 |     downloadedBytes = 0; | 
| 150 |     uploadedBytes = 0; | 
| 151 |     memset(s: downloadRate, c: 0, n: sizeof(downloadRate)); | 
| 152 |     memset(s: uploadRate, c: 0, n: sizeof(uploadRate)); | 
| 153 |     transferRateTimer = 0; | 
| 154 | } | 
| 155 |  | 
| 156 | void TorrentClientPrivate::setError(TorrentClient::Error errorCode) | 
| 157 | { | 
| 158 |     this->error = errorCode; | 
| 159 |     switch (error) { | 
| 160 |     case TorrentClient::UnknownError: | 
| 161 |         errorString = QT_TRANSLATE_NOOP(TorrentClient, "Unknown error" ); | 
| 162 |         break; | 
| 163 |     case TorrentClient::TorrentParseError: | 
| 164 |         errorString = QT_TRANSLATE_NOOP(TorrentClient, "Invalid torrent data" ); | 
| 165 |         break; | 
| 166 |     case TorrentClient::InvalidTrackerError: | 
| 167 |         errorString = QT_TRANSLATE_NOOP(TorrentClient, "Unable to connect to tracker" ); | 
| 168 |         break; | 
| 169 |     case TorrentClient::FileError: | 
| 170 |         errorString = QT_TRANSLATE_NOOP(TorrentClient, "File error" ); | 
| 171 |         break; | 
| 172 |     case TorrentClient::ServerError: | 
| 173 |         errorString = QT_TRANSLATE_NOOP(TorrentClient, "Unable to initialize server" ); | 
| 174 |         break; | 
| 175 |     } | 
| 176 |     emit q->error(error: errorCode); | 
| 177 | } | 
| 178 |  | 
| 179 | void TorrentClientPrivate::setState(TorrentClient::State state) | 
| 180 | { | 
| 181 |     this->state = state; | 
| 182 |     switch (state) { | 
| 183 |     case TorrentClient::Idle: | 
| 184 |         stateString = QT_TRANSLATE_NOOP(TorrentClient, "Idle" ); | 
| 185 |         break; | 
| 186 |     case TorrentClient::Paused: | 
| 187 |         stateString = QT_TRANSLATE_NOOP(TorrentClient, "Paused" ); | 
| 188 |         break; | 
| 189 |     case TorrentClient::Stopping: | 
| 190 |         stateString = QT_TRANSLATE_NOOP(TorrentClient, "Stopping" ); | 
| 191 |         break; | 
| 192 |     case TorrentClient::Preparing: | 
| 193 |         stateString = QT_TRANSLATE_NOOP(TorrentClient, "Preparing" ); | 
| 194 |         break; | 
| 195 |     case TorrentClient::Searching: | 
| 196 |         stateString = QT_TRANSLATE_NOOP(TorrentClient, "Searching" ); | 
| 197 |         break; | 
| 198 |     case TorrentClient::Connecting: | 
| 199 |         stateString = QT_TRANSLATE_NOOP(TorrentClient, "Connecting" ); | 
| 200 |         break; | 
| 201 |     case TorrentClient::WarmingUp: | 
| 202 |         stateString = QT_TRANSLATE_NOOP(TorrentClient, "Warming up" ); | 
| 203 |         break; | 
| 204 |     case TorrentClient::Downloading: | 
| 205 |         stateString = QT_TRANSLATE_NOOP(TorrentClient, "Downloading" ); | 
| 206 |         break; | 
| 207 |     case TorrentClient::Endgame: | 
| 208 |         stateString = QT_TRANSLATE_NOOP(TorrentClient, "Finishing" ); | 
| 209 |         break; | 
| 210 |     case TorrentClient::Seeding: | 
| 211 |         stateString = QT_TRANSLATE_NOOP(TorrentClient, "Seeding" ); | 
| 212 |         break; | 
| 213 |     } | 
| 214 |     emit q->stateChanged(state); | 
| 215 | } | 
| 216 |  | 
| 217 | void TorrentClientPrivate::callScheduler() | 
| 218 | { | 
| 219 |     if (!schedulerCalled) { | 
| 220 |         schedulerCalled = true; | 
| 221 |         QMetaObject::invokeMethod(obj: q, member: "scheduleDownloads" , type: Qt::QueuedConnection); | 
| 222 |     } | 
| 223 | } | 
| 224 |  | 
| 225 | void TorrentClientPrivate::callPeerConnector() | 
| 226 | { | 
| 227 |     if (!connectingToClients) { | 
| 228 |         connectingToClients = true; | 
| 229 |         QTimer::singleShot(interval: 10000, receiver: q, slot: &TorrentClient::connectToPeers); | 
| 230 |     } | 
| 231 | } | 
| 232 |  | 
| 233 | TorrentClient::TorrentClient(QObject *parent) | 
| 234 |     : QObject(parent), d(new TorrentClientPrivate(this)) | 
| 235 | { | 
| 236 |     // Connect the file manager | 
| 237 |     connect(sender: &d->fileManager, signal: &FileManager::dataRead, | 
| 238 |             receiver: this, slot: &TorrentClient::sendToPeer); | 
| 239 |     connect(sender: &d->fileManager, signal: &FileManager::verificationProgress, | 
| 240 |             receiver: this, slot: &TorrentClient::updateProgress); | 
| 241 |     connect(sender: &d->fileManager, signal: &FileManager::verificationDone, | 
| 242 |             receiver: this, slot: &TorrentClient::fullVerificationDone); | 
| 243 |     connect(sender: &d->fileManager, signal: &FileManager::pieceVerified, | 
| 244 |             receiver: this, slot: &TorrentClient::pieceVerified); | 
| 245 |     connect(sender: &d->fileManager, signal: &FileManager::error, | 
| 246 |             receiver: this, slot: &TorrentClient::handleFileError); | 
| 247 |  | 
| 248 |     // Connect the tracker client | 
| 249 |     connect(sender: &d->trackerClient, signal: &TrackerClient::peerListUpdated, | 
| 250 |             receiver: this, slot: &TorrentClient::addToPeerList); | 
| 251 |     connect(sender: &d->trackerClient, signal: &TrackerClient::stopped, | 
| 252 |             receiver: this, slot: &TorrentClient::stopped); | 
| 253 | } | 
| 254 |  | 
| 255 | TorrentClient::~TorrentClient() | 
| 256 | { | 
| 257 |     qDeleteAll(c: d->peers); | 
| 258 |     qDeleteAll(c: d->pendingPieces); | 
| 259 |     delete d; | 
| 260 | } | 
| 261 |  | 
| 262 | bool TorrentClient::setTorrent(const QString &fileName) | 
| 263 | { | 
| 264 |     QFile file(fileName); | 
| 265 |     if (!file.open(flags: QIODevice::ReadOnly) || !setTorrent(file.readAll())) { | 
| 266 |         d->setError(TorrentParseError); | 
| 267 |         return false; | 
| 268 |     } | 
| 269 |     return true; | 
| 270 | } | 
| 271 |  | 
| 272 | bool TorrentClient::setTorrent(const QByteArray &torrentData) | 
| 273 | { | 
| 274 |     if (!d->metaInfo.parse(data: torrentData)) { | 
| 275 |         d->setError(TorrentParseError); | 
| 276 |         return false; | 
| 277 |     } | 
| 278 |  | 
| 279 |     // Calculate SHA1 hash of the "info" section in the torrent | 
| 280 |     QByteArray infoValue = d->metaInfo.infoValue(); | 
| 281 |     d->infoHash = QCryptographicHash::hash(data: infoValue, method: QCryptographicHash::Sha1); | 
| 282 |  | 
| 283 |     return true; | 
| 284 | } | 
| 285 |  | 
| 286 | MetaInfo TorrentClient::metaInfo() const | 
| 287 | { | 
| 288 |     return d->metaInfo; | 
| 289 | } | 
| 290 |  | 
| 291 | void TorrentClient::setDestinationFolder(const QString &directory) | 
| 292 | { | 
| 293 |     d->destinationFolder = directory; | 
| 294 | } | 
| 295 |  | 
| 296 | QString TorrentClient::destinationFolder() const | 
| 297 | { | 
| 298 |     return d->destinationFolder; | 
| 299 | } | 
| 300 |  | 
| 301 | void TorrentClient::setDumpedState(const QByteArray &dumpedState) | 
| 302 | { | 
| 303 |     // Recover partially completed pieces | 
| 304 |     QDataStream stream(dumpedState); | 
| 305 |  | 
| 306 |     quint16 version = 0; | 
| 307 |     stream >> version; | 
| 308 |     if (version != 2) | 
| 309 |         return; | 
| 310 |  | 
| 311 |     stream >> d->completedPieces; | 
| 312 |  | 
| 313 |     while (!stream.atEnd()) { | 
| 314 |         int index; | 
| 315 |         int length; | 
| 316 |         QBitArray completed; | 
| 317 |         stream >> index >> length >> completed; | 
| 318 |         if (stream.status() != QDataStream::Ok) { | 
| 319 |             d->completedPieces.clear(); | 
| 320 |             break; | 
| 321 |         } | 
| 322 |  | 
| 323 |         TorrentPiece *piece = new TorrentPiece; | 
| 324 |         piece->index = index; | 
| 325 |         piece->length = length; | 
| 326 |         piece->completedBlocks = completed; | 
| 327 |         piece->requestedBlocks.resize(size: completed.size()); | 
| 328 |         piece->inProgress = false; | 
| 329 |         d->pendingPieces[index] = piece; | 
| 330 |     } | 
| 331 | } | 
| 332 |  | 
| 333 | QByteArray TorrentClient::dumpedState() const | 
| 334 | { | 
| 335 |     QByteArray partials; | 
| 336 |     QDataStream stream(&partials, QIODevice::WriteOnly); | 
| 337 |  | 
| 338 |     stream << quint16(2); | 
| 339 |     stream << d->completedPieces; | 
| 340 |  | 
| 341 |     // Save the state of all partially downloaded pieces into a format | 
| 342 |     // suitable for storing in settings. | 
| 343 |     QMap<int, TorrentPiece *>::ConstIterator it = d->pendingPieces.constBegin(); | 
| 344 |     while (it != d->pendingPieces.constEnd()) { | 
| 345 |         TorrentPiece *piece = it.value(); | 
| 346 |         if (blocksLeftForPiece(piece) > 0 && blocksLeftForPiece(piece) < piece->completedBlocks.size()) { | 
| 347 |             stream << piece->index; | 
| 348 |             stream << piece->length; | 
| 349 |             stream << piece->completedBlocks; | 
| 350 |         } | 
| 351 |         ++it; | 
| 352 |     } | 
| 353 |  | 
| 354 |     return partials; | 
| 355 | } | 
| 356 |  | 
| 357 | qint64 TorrentClient::progress() const | 
| 358 | { | 
| 359 |     return d->lastProgressValue; | 
| 360 | } | 
| 361 |  | 
| 362 | void TorrentClient::setDownloadedBytes(qint64 bytes) | 
| 363 | { | 
| 364 |     d->downloadedBytes = bytes; | 
| 365 | } | 
| 366 |  | 
| 367 | qint64 TorrentClient::downloadedBytes() const | 
| 368 | { | 
| 369 |     return d->downloadedBytes; | 
| 370 | } | 
| 371 |  | 
| 372 | void TorrentClient::setUploadedBytes(qint64 bytes) | 
| 373 | { | 
| 374 |     d->uploadedBytes = bytes; | 
| 375 | } | 
| 376 |  | 
| 377 | qint64 TorrentClient::uploadedBytes() const | 
| 378 | { | 
| 379 |     return d->uploadedBytes; | 
| 380 | } | 
| 381 |  | 
| 382 | int TorrentClient::connectedPeerCount() const | 
| 383 | { | 
| 384 |     int tmp = 0; | 
| 385 |     for (PeerWireClient *client : d->connections) { | 
| 386 |         if (client->state() == QAbstractSocket::ConnectedState) | 
| 387 |             ++tmp; | 
| 388 |     } | 
| 389 |     return tmp; | 
| 390 | } | 
| 391 |  | 
| 392 | int TorrentClient::seedCount() const | 
| 393 | { | 
| 394 |     int tmp = 0; | 
| 395 |     for (PeerWireClient *client : d->connections) { | 
| 396 |         if (client->availablePieces().count(on: true) == d->pieceCount) | 
| 397 |             ++tmp; | 
| 398 |     } | 
| 399 |     return tmp; | 
| 400 | } | 
| 401 |  | 
| 402 | TorrentClient::State TorrentClient::state() const | 
| 403 | { | 
| 404 |     return d->state; | 
| 405 | } | 
| 406 |  | 
| 407 | QString TorrentClient::stateString() const | 
| 408 | { | 
| 409 |     return d->stateString; | 
| 410 | } | 
| 411 |  | 
| 412 | TorrentClient::Error TorrentClient::error() const | 
| 413 | { | 
| 414 |     return d->error; | 
| 415 | } | 
| 416 |  | 
| 417 | QString TorrentClient::errorString() const | 
| 418 | { | 
| 419 |     return d->errorString; | 
| 420 | } | 
| 421 |  | 
| 422 | QByteArray TorrentClient::peerId() const | 
| 423 | { | 
| 424 |     return d->peerId; | 
| 425 | } | 
| 426 |  | 
| 427 | QByteArray TorrentClient::infoHash() const | 
| 428 | { | 
| 429 |     return d->infoHash; | 
| 430 | } | 
| 431 |  | 
| 432 | void TorrentClient::start() | 
| 433 | { | 
| 434 |     if (d->state != Idle) | 
| 435 |         return; | 
| 436 |  | 
| 437 |     TorrentServer::instance()->addClient(client: this); | 
| 438 |  | 
| 439 |     // Initialize the file manager | 
| 440 |     d->setState(Preparing); | 
| 441 |     d->fileManager.setMetaInfo(d->metaInfo); | 
| 442 |     d->fileManager.setDestinationFolder(d->destinationFolder); | 
| 443 |     d->fileManager.setCompletedPieces(d->completedPieces); | 
| 444 |     d->fileManager.start(QThread::LowestPriority); | 
| 445 |     d->fileManager.startDataVerification(); | 
| 446 | } | 
| 447 |  | 
| 448 | void TorrentClient::stop() | 
| 449 | { | 
| 450 |     if (d->state == Stopping) | 
| 451 |         return; | 
| 452 |  | 
| 453 |     TorrentServer::instance()->removeClient(client: this); | 
| 454 |  | 
| 455 |     // Update the state | 
| 456 |     State oldState = d->state; | 
| 457 |     d->setState(Stopping); | 
| 458 |  | 
| 459 |     // Stop the timer | 
| 460 |     if (d->transferRateTimer) { | 
| 461 |         killTimer(id: d->transferRateTimer); | 
| 462 |         d->transferRateTimer = 0; | 
| 463 |     } | 
| 464 |  | 
| 465 |     // Abort all existing connections | 
| 466 |     for (PeerWireClient *client : qAsConst(t&: d->connections)) { | 
| 467 |         RateController::instance()->removeSocket(socket: client); | 
| 468 |         ConnectionManager::instance()->removeConnection(connection: client); | 
| 469 |         client->abort(); | 
| 470 |     } | 
| 471 |     d->connections.clear(); | 
| 472 |  | 
| 473 |     // Perhaps stop the tracker | 
| 474 |     if (oldState > Preparing) { | 
| 475 |         d->trackerClient.stop(); | 
| 476 |     } else { | 
| 477 |         d->setState(Idle); | 
| 478 |         emit stopped(); | 
| 479 |     } | 
| 480 | } | 
| 481 |  | 
| 482 | void TorrentClient::setPaused(bool paused) | 
| 483 | { | 
| 484 |     if (paused) { | 
| 485 |         // Abort all connections, and set the max number of | 
| 486 |         // connections to 0. Keep the list of peers, so we can quickly | 
| 487 |         // resume later. | 
| 488 |         d->setState(Paused); | 
| 489 |         for (PeerWireClient *client : qAsConst(t&: d->connections)) | 
| 490 |             client->abort(); | 
| 491 |         d->connections.clear(); | 
| 492 |         TorrentServer::instance()->removeClient(client: this); | 
| 493 |     } else { | 
| 494 |         // Restore the max number of connections, and start the peer | 
| 495 |         // connector. We should also quickly start receiving incoming | 
| 496 |         // connections. | 
| 497 |         d->setState(d->completedPieces.count(on: true) == d->fileManager.pieceCount() | 
| 498 |                     ? Seeding : Searching); | 
| 499 |         connectToPeers(); | 
| 500 |         TorrentServer::instance()->addClient(client: this); | 
| 501 |     } | 
| 502 | } | 
| 503 |  | 
| 504 | void TorrentClient::timerEvent(QTimerEvent *event) | 
| 505 | { | 
| 506 |     if (event->timerId() == d->uploadScheduleTimer) { | 
| 507 |         // Update the state of who's choked and who's not | 
| 508 |         scheduleUploads(); | 
| 509 |         return; | 
| 510 |     } | 
| 511 |  | 
| 512 |     if (event->timerId() != d->transferRateTimer) { | 
| 513 |         QObject::timerEvent(event); | 
| 514 |         return; | 
| 515 |     } | 
| 516 |  | 
| 517 |     // Calculate average upload/download rate | 
| 518 |     qint64 uploadBytesPerSecond = 0; | 
| 519 |     qint64 downloadBytesPerSecond = 0; | 
| 520 |     for (int i = 0; i < RateControlWindowLength; ++i) { | 
| 521 |         uploadBytesPerSecond += d->uploadRate[i]; | 
| 522 |         downloadBytesPerSecond += d->downloadRate[i]; | 
| 523 |     } | 
| 524 |     uploadBytesPerSecond /= qint64(RateControlWindowLength); | 
| 525 |     downloadBytesPerSecond /= qint64(RateControlWindowLength); | 
| 526 |     for (int i = RateControlWindowLength - 2; i >= 0; --i) { | 
| 527 |         d->uploadRate[i + 1] = d->uploadRate[i]; | 
| 528 |         d->downloadRate[i + 1] = d->downloadRate[i]; | 
| 529 |     } | 
| 530 |     d->uploadRate[0] = 0; | 
| 531 |     d->downloadRate[0] = 0; | 
| 532 |     emit uploadRateUpdated(bytesPerSecond: int(uploadBytesPerSecond)); | 
| 533 |     emit downloadRateUpdated(bytesPerSecond: int(downloadBytesPerSecond)); | 
| 534 |  | 
| 535 |     // Stop the timer if there is no activity. | 
| 536 |     if (downloadBytesPerSecond == 0 && uploadBytesPerSecond == 0) { | 
| 537 |         killTimer(id: d->transferRateTimer); | 
| 538 |         d->transferRateTimer = 0; | 
| 539 |     } | 
| 540 | } | 
| 541 |  | 
| 542 | void TorrentClient::sendToPeer(int readId, int pieceIndex, int begin, const QByteArray &data) | 
| 543 | { | 
| 544 |     // Send the requested block to the peer if the client connection | 
| 545 |     // still exists; otherwise do nothing. This slot is called by the | 
| 546 |     // file manager after it has read a block of data. | 
| 547 |     PeerWireClient *client = d->readIds.value(akey: readId); | 
| 548 |     if (client) { | 
| 549 |         if ((client->peerWireState() & PeerWireClient::ChokingPeer) == 0) | 
| 550 |             client->sendBlock(piece: pieceIndex, offset: begin, data); | 
| 551 |     } | 
| 552 |     d->readIds.remove(akey: readId); | 
| 553 | } | 
| 554 |  | 
| 555 | void TorrentClient::fullVerificationDone() | 
| 556 | { | 
| 557 |     // Update our list of completed and incomplete pieces. | 
| 558 |     d->completedPieces = d->fileManager.completedPieces(); | 
| 559 |     d->incompletePieces.resize(size: d->completedPieces.size()); | 
| 560 |     d->pieceCount = d->completedPieces.size(); | 
| 561 |     for (int i = 0; i < d->fileManager.pieceCount(); ++i) { | 
| 562 |         if (!d->completedPieces.testBit(i)) | 
| 563 |             d->incompletePieces.setBit(i); | 
| 564 |     } | 
| 565 |  | 
| 566 |     updateProgress(); | 
| 567 |  | 
| 568 |     // If the checksums show that what the dumped state thought was | 
| 569 |     // partial was in fact complete, then we trust the checksums. | 
| 570 |     QMap<int, TorrentPiece *>::Iterator it = d->pendingPieces.begin(); | 
| 571 |     while (it != d->pendingPieces.end()) { | 
| 572 |         if (d->completedPieces.testBit(i: it.key())) | 
| 573 |             it = d->pendingPieces.erase(it); | 
| 574 |         else | 
| 575 |             ++it; | 
| 576 |     } | 
| 577 |  | 
| 578 |     d->uploadScheduleTimer = startTimer(interval: UploadScheduleInterval); | 
| 579 |  | 
| 580 |     // Start the server | 
| 581 |     TorrentServer *server = TorrentServer::instance(); | 
| 582 |     if (!server->isListening()) { | 
| 583 |         // Set up the peer wire server | 
| 584 |         for (int i = ServerMinPort; i <= ServerMaxPort; ++i) { | 
| 585 |             if (server->listen(address: QHostAddress::Any, port: i)) | 
| 586 |                 break; | 
| 587 |         } | 
| 588 |         if (!server->isListening()) { | 
| 589 |             d->setError(ServerError); | 
| 590 |             return; | 
| 591 |         } | 
| 592 |     } | 
| 593 |  | 
| 594 |     d->setState(d->completedPieces.count(on: true) == d->pieceCount ? Seeding : Searching); | 
| 595 |  | 
| 596 |     // Start the tracker client | 
| 597 |     d->trackerClient.start(info: d->metaInfo); | 
| 598 | } | 
| 599 |  | 
| 600 | void TorrentClient::pieceVerified(int pieceIndex, bool ok) | 
| 601 | { | 
| 602 |     TorrentPiece *piece = d->pendingPieces.value(akey: pieceIndex); | 
| 603 |  | 
| 604 |     // Remove this piece from all payloads | 
| 605 |     QMultiMap<PeerWireClient *, TorrentPiece *>::Iterator it = d->payloads.begin(); | 
| 606 |     while (it != d->payloads.end()) { | 
| 607 |         if (it.value()->index == pieceIndex) | 
| 608 |             it = d->payloads.erase(it); | 
| 609 |         else | 
| 610 |             ++it; | 
| 611 |     } | 
| 612 |  | 
| 613 |     if (!ok) { | 
| 614 |         // If a piece did not pass the SHA1 check, we'll simply clear | 
| 615 |         // its state, and the scheduler will re-request it | 
| 616 |         piece->inProgress = false; | 
| 617 |         piece->completedBlocks.fill(aval: false); | 
| 618 |         piece->requestedBlocks.fill(aval: false); | 
| 619 |         d->callScheduler(); | 
| 620 |         return; | 
| 621 |     } | 
| 622 |  | 
| 623 |     // Update the peer list so we know who's still interesting. | 
| 624 |     for (TorrentPeer *peer : qAsConst(t&: d->peers)) { | 
| 625 |         if (!peer->interesting) | 
| 626 |             continue; | 
| 627 |         bool interesting = false; | 
| 628 |         for (int i = 0; i < d->pieceCount; ++i) { | 
| 629 |             if (peer->pieces.testBit(i) && d->incompletePieces.testBit(i)) { | 
| 630 |                 interesting = true; | 
| 631 |                 break; | 
| 632 |             } | 
| 633 |         } | 
| 634 |         peer->interesting = interesting; | 
| 635 |     } | 
| 636 |  | 
| 637 |     // Delete the piece and update our structures. | 
| 638 |     delete piece; | 
| 639 |     d->pendingPieces.remove(akey: pieceIndex); | 
| 640 |     d->completedPieces.setBit(pieceIndex); | 
| 641 |     d->incompletePieces.clearBit(i: pieceIndex); | 
| 642 |  | 
| 643 |     // Notify connected peers. | 
| 644 |     for (PeerWireClient *client : qAsConst(t&: d->connections)) { | 
| 645 |         if (client->state() == QAbstractSocket::ConnectedState | 
| 646 |             && !client->availablePieces().testBit(i: pieceIndex)) { | 
| 647 |             client->sendPieceNotification(piece: pieceIndex); | 
| 648 |         } | 
| 649 |     } | 
| 650 |  | 
| 651 |     // Notify the tracker if we've entered Seeding status; otherwise | 
| 652 |     // call the scheduler. | 
| 653 |     int completed = d->completedPieces.count(on: true); | 
| 654 |     if (completed == d->pieceCount) { | 
| 655 |         if (d->state != Seeding) { | 
| 656 |             d->setState(Seeding); | 
| 657 |             d->trackerClient.startSeeding(); | 
| 658 |         } | 
| 659 |     } else { | 
| 660 |         if (completed == 1) | 
| 661 |             d->setState(Downloading); | 
| 662 |         else if (d->incompletePieces.count(on: true) < 5 && d->pendingPieces.size() > d->incompletePieces.count(on: true)) | 
| 663 |             d->setState(Endgame); | 
| 664 |         d->callScheduler(); | 
| 665 |     } | 
| 666 |  | 
| 667 |     updateProgress(); | 
| 668 | } | 
| 669 |  | 
| 670 | void TorrentClient::handleFileError() | 
| 671 | { | 
| 672 |     if (d->state == Paused) | 
| 673 |         return; | 
| 674 |     setPaused(true); | 
| 675 |     emit error(error: FileError); | 
| 676 | } | 
| 677 |  | 
| 678 | void TorrentClient::connectToPeers() | 
| 679 | { | 
| 680 |     d->connectingToClients = false; | 
| 681 |  | 
| 682 |     if (d->state == Stopping || d->state == Idle || d->state == Paused) | 
| 683 |         return; | 
| 684 |  | 
| 685 |     if (d->state == Searching) | 
| 686 |         d->setState(Connecting); | 
| 687 |  | 
| 688 |     // Find the list of peers we are not currently connected to, where | 
| 689 |     // the more interesting peers are listed more than once. | 
| 690 |     QList<TorrentPeer *> weighedPeers = weighedFreePeers(); | 
| 691 |  | 
| 692 |     // Start as many connections as we can | 
| 693 |     while (!weighedPeers.isEmpty() && ConnectionManager::instance()->canAddConnection() | 
| 694 |            && (QRandomGenerator::global()->bounded(highest: ConnectionManager::instance()->maxConnections() / 2))) { | 
| 695 |         PeerWireClient *client = new PeerWireClient(ConnectionManager::instance()->clientId(), this); | 
| 696 |         RateController::instance()->addSocket(socket: client); | 
| 697 |         ConnectionManager::instance()->addConnection(connection: client); | 
| 698 |  | 
| 699 |         initializeConnection(client); | 
| 700 |         d->connections << client; | 
| 701 |  | 
| 702 |         // Pick a random peer from the list of weighed peers. | 
| 703 |         TorrentPeer *peer = weighedPeers.takeAt(i: QRandomGenerator::global()->bounded(highest: weighedPeers.size())); | 
| 704 |         weighedPeers.removeAll(t: peer); | 
| 705 |         peer->connectStart = QDateTime::currentSecsSinceEpoch(); | 
| 706 |         peer->lastVisited = peer->connectStart; | 
| 707 |  | 
| 708 |         // Connect to the peer. | 
| 709 |         client->setPeer(peer); | 
| 710 |         client->connectToHost(address: peer->address, port: peer->port); | 
| 711 |     } | 
| 712 | } | 
| 713 |  | 
| 714 | QList<TorrentPeer *> TorrentClient::weighedFreePeers() const | 
| 715 | { | 
| 716 |     QList<TorrentPeer *> weighedPeers; | 
| 717 |  | 
| 718 |     // Generate a list of peers that we want to connect to. | 
| 719 |     qint64 now = QDateTime::currentSecsSinceEpoch(); | 
| 720 |     QList<TorrentPeer *> freePeers; | 
| 721 |     QMap<QString, int> connectionsPerPeer; | 
| 722 |     for (TorrentPeer *peer : qAsConst(t&: d->peers)) { | 
| 723 |         bool busy = false; | 
| 724 |         for (PeerWireClient *client : qAsConst(t&: d->connections)) { | 
| 725 |             if (client->state() == PeerWireClient::ConnectedState | 
| 726 |                 && client->peerAddress() == peer->address | 
| 727 |                 && client->peerPort() == peer->port) { | 
| 728 |                 if (++connectionsPerPeer[peer->address.toString()] >= MaxConnectionPerPeer) { | 
| 729 |                     busy = true; | 
| 730 |                     break; | 
| 731 |                 } | 
| 732 |             } | 
| 733 |         } | 
| 734 |         if (!busy && (now - peer->lastVisited) > uint(MinimumTimeBeforeRevisit)) | 
| 735 |             freePeers << peer; | 
| 736 |     } | 
| 737 |  | 
| 738 |     // Nothing to connect to | 
| 739 |     if (freePeers.isEmpty()) | 
| 740 |         return weighedPeers; | 
| 741 |  | 
| 742 |     // Assign points based on connection speed and pieces available. | 
| 743 |     QList<QPair<int, TorrentPeer *> > points; | 
| 744 |     for (TorrentPeer *peer : qAsConst(t&: freePeers)) { | 
| 745 |         int tmp = 0; | 
| 746 |         if (peer->interesting) { | 
| 747 |             tmp += peer->numCompletedPieces; | 
| 748 |             if (d->state == Seeding) | 
| 749 |                 tmp = d->pieceCount - tmp; | 
| 750 |             if (!peer->connectStart) // An unknown peer is as interesting as a seed | 
| 751 |                 tmp += d->pieceCount; | 
| 752 |  | 
| 753 |             // 1/5 of the total score for each second below 5 it takes to | 
| 754 |             // connect. | 
| 755 |             if (peer->connectTime < 5) | 
| 756 |                 tmp += (d->pieceCount / 10) * (5 - peer->connectTime); | 
| 757 |         } | 
| 758 |         points << QPair<int, TorrentPeer *>(tmp, peer); | 
| 759 |     } | 
| 760 |     std::sort(first: points.begin(), last: points.end()); | 
| 761 |  | 
| 762 |     // Minimize the list so the point difference is never more than 1. | 
| 763 |     typedef QPair<int,TorrentPeer*> PointPair; | 
| 764 |     QMultiMap<int, TorrentPeer *> pointMap; | 
| 765 |     int lowestScore = 0; | 
| 766 |     int lastIndex = 0; | 
| 767 |     for (const PointPair &point : qAsConst(t&: points)) { | 
| 768 |         if (point.first > lowestScore) { | 
| 769 |             lowestScore = point.first; | 
| 770 |             ++lastIndex; | 
| 771 |         } | 
| 772 |         pointMap.insert(akey: lastIndex, avalue: point.second); | 
| 773 |     } | 
| 774 |  | 
| 775 |     // Now make up a list of peers where the ones with more points are | 
| 776 |     // listed many times. | 
| 777 |     QMultiMap<int, TorrentPeer *>::ConstIterator it = pointMap.constBegin(); | 
| 778 |     while (it != pointMap.constEnd()) { | 
| 779 |         for (int i = 0; i < it.key() + 1; ++i) | 
| 780 |             weighedPeers << it.value(); | 
| 781 |         ++it; | 
| 782 |     } | 
| 783 |  | 
| 784 |     return weighedPeers; | 
| 785 | } | 
| 786 |  | 
| 787 | void TorrentClient::setupIncomingConnection(PeerWireClient *client) | 
| 788 | { | 
| 789 |     // Connect signals | 
| 790 |     initializeConnection(client); | 
| 791 |  | 
| 792 |     // Initialize this client | 
| 793 |     RateController::instance()->addSocket(socket: client); | 
| 794 |     d->connections << client; | 
| 795 |  | 
| 796 |     client->initialize(infoHash: d->infoHash, pieceCount: d->pieceCount); | 
| 797 |     client->sendPieceList(bitField: d->completedPieces); | 
| 798 |  | 
| 799 |     emit peerInfoUpdated(); | 
| 800 |  | 
| 801 |     if (d->state == Searching || d->state == Connecting) { | 
| 802 |         int completed = d->completedPieces.count(on: true); | 
| 803 |         if (completed == 0) | 
| 804 |             d->setState(WarmingUp); | 
| 805 |         else if (d->incompletePieces.count(on: true) < 5 && d->pendingPieces.size() > d->incompletePieces.count(on: true)) | 
| 806 |             d->setState(Endgame); | 
| 807 |     } | 
| 808 |  | 
| 809 |     if (d->connections.isEmpty()) | 
| 810 |         scheduleUploads(); | 
| 811 | } | 
| 812 |  | 
| 813 | void TorrentClient::setupOutgoingConnection() | 
| 814 | { | 
| 815 |     PeerWireClient *client = qobject_cast<PeerWireClient *>(object: sender()); | 
| 816 |  | 
| 817 |     // Update connection statistics. | 
| 818 |     for (TorrentPeer *peer : qAsConst(t&: d->peers)) { | 
| 819 |         if (peer->port == client->peerPort() && peer->address == client->peerAddress()) { | 
| 820 |             peer->connectTime = peer->lastVisited - peer->connectStart; | 
| 821 |             break; | 
| 822 |         } | 
| 823 |     } | 
| 824 |  | 
| 825 |     // Send handshake and piece list | 
| 826 |     client->initialize(infoHash: d->infoHash, pieceCount: d->pieceCount); | 
| 827 |     client->sendPieceList(bitField: d->completedPieces); | 
| 828 |  | 
| 829 |     emit peerInfoUpdated(); | 
| 830 |  | 
| 831 |     if (d->state == Searching || d->state == Connecting) { | 
| 832 |         int completed = d->completedPieces.count(on: true); | 
| 833 |         if (completed == 0) | 
| 834 |             d->setState(WarmingUp); | 
| 835 |         else if (d->incompletePieces.count(on: true) < 5 && d->pendingPieces.size() > d->incompletePieces.count(on: true)) | 
| 836 |             d->setState(Endgame); | 
| 837 |     } | 
| 838 | } | 
| 839 |  | 
| 840 | void TorrentClient::initializeConnection(PeerWireClient *client) | 
| 841 | { | 
| 842 |     connect(sender: client, signal: &PeerWireClient::connected, | 
| 843 |             receiver: this, slot: &TorrentClient::setupOutgoingConnection); | 
| 844 |     connect(sender: client, signal: &PeerWireClient::disconnected, | 
| 845 |             receiver: this, slot: &TorrentClient::removeClient); | 
| 846 |     connect(sender: client, signal: &PeerWireClient::errorOccurred, | 
| 847 |             receiver: this, slot: &TorrentClient::removeClient); | 
| 848 |     connect(sender: client, signal: &PeerWireClient::piecesAvailable, | 
| 849 |             receiver: this, slot: &TorrentClient::peerPiecesAvailable); | 
| 850 |     connect(sender: client, signal: &PeerWireClient::blockRequested, | 
| 851 |             receiver: this, slot: &TorrentClient::peerRequestsBlock); | 
| 852 |     connect(sender: client, signal: &PeerWireClient::blockReceived, | 
| 853 |             receiver: this, slot: &TorrentClient::blockReceived); | 
| 854 |     connect(sender: client, signal: &PeerWireClient::choked, | 
| 855 |             receiver: this, slot: &TorrentClient::peerChoked); | 
| 856 |     connect(sender: client, signal: &PeerWireClient::unchoked, | 
| 857 |             receiver: this, slot: &TorrentClient::peerUnchoked); | 
| 858 |     connect(sender: client, signal: &PeerWireClient::bytesWritten, | 
| 859 |             receiver: this, slot: &TorrentClient::peerWireBytesWritten); | 
| 860 |     connect(sender: client, signal: &PeerWireClient::bytesReceived, | 
| 861 |             receiver: this, slot: &TorrentClient::peerWireBytesReceived); | 
| 862 | } | 
| 863 |  | 
| 864 | void TorrentClient::removeClient() | 
| 865 | { | 
| 866 |     PeerWireClient *client = static_cast<PeerWireClient *>(sender()); | 
| 867 |  | 
| 868 |     // Remove the host from our list of known peers if the connection | 
| 869 |     // failed. | 
| 870 |     if (client->peer() && client->error() == QAbstractSocket::ConnectionRefusedError) | 
| 871 |         d->peers.removeAll(t: client->peer()); | 
| 872 |  | 
| 873 |     // Remove the client from RateController and all structures. | 
| 874 |     RateController::instance()->removeSocket(socket: client); | 
| 875 |     d->connections.removeAll(t: client); | 
| 876 |     for (auto it = d->payloads.find(akey: client); it != d->payloads.end() && it.key() == client; /*erasing*/) { | 
| 877 |         TorrentPiece *piece = it.value(); | 
| 878 |         piece->inProgress = false; | 
| 879 |         piece->requestedBlocks.fill(aval: false); | 
| 880 |         it = d->payloads.erase(it); | 
| 881 |     } | 
| 882 |  | 
| 883 |     // Remove pending read requests. | 
| 884 |     for (auto it = d->readIds.begin(), end = d->readIds.end(); it != end; /*erasing*/) { | 
| 885 |         if (it.value() == client) | 
| 886 |             it = d->readIds.erase(it); | 
| 887 |         else | 
| 888 |             ++it; | 
| 889 |     } | 
| 890 |  | 
| 891 |     // Delete the client later. | 
| 892 |     disconnect(sender: client, signal: &PeerWireClient::disconnected, | 
| 893 |                receiver: this, slot: &TorrentClient::removeClient); | 
| 894 |     client->deleteLater(); | 
| 895 |     ConnectionManager::instance()->removeConnection(connection: client); | 
| 896 |  | 
| 897 |     emit peerInfoUpdated(); | 
| 898 |     d->callPeerConnector(); | 
| 899 | } | 
| 900 |  | 
| 901 | void TorrentClient::peerPiecesAvailable(const QBitArray &pieces) | 
| 902 | { | 
| 903 |     PeerWireClient *client = qobject_cast<PeerWireClient *>(object: sender()); | 
| 904 |  | 
| 905 |     // Find the peer in our list of announced peers. If it's there, | 
| 906 |     // then we can use the piece list into to gather statistics that | 
| 907 |     // help us decide what peers to connect to. | 
| 908 |     TorrentPeer *peer = nullptr; | 
| 909 |     QList<TorrentPeer *>::Iterator it = d->peers.begin(); | 
| 910 |     while (it != d->peers.end()) { | 
| 911 |         if ((*it)->address == client->peerAddress() && (*it)->port == client->peerPort()) { | 
| 912 |             peer = *it; | 
| 913 |             break; | 
| 914 |         } | 
| 915 |         ++it; | 
| 916 |     } | 
| 917 |  | 
| 918 |     // If the peer is a seed, and we are in seeding mode, then the | 
| 919 |     // peer is uninteresting. | 
| 920 |     if (pieces.count(on: true) == d->pieceCount) { | 
| 921 |         if (peer) | 
| 922 |             peer->seed = true; | 
| 923 |         emit peerInfoUpdated(); | 
| 924 |         if (d->state == Seeding) { | 
| 925 |             client->abort(); | 
| 926 |             return; | 
| 927 |         } else { | 
| 928 |             if (peer) | 
| 929 |                 peer->interesting = true; | 
| 930 |             if ((client->peerWireState() & PeerWireClient::InterestedInPeer) == 0) | 
| 931 |                 client->sendInterested(); | 
| 932 |             d->callScheduler(); | 
| 933 |             return; | 
| 934 |         } | 
| 935 |     } | 
| 936 |  | 
| 937 |     // Update our list of available pieces. | 
| 938 |     if (peer) { | 
| 939 |         peer->pieces = pieces; | 
| 940 |         peer->numCompletedPieces = pieces.count(on: true); | 
| 941 |     } | 
| 942 |  | 
| 943 |     // Check for interesting pieces, and tell the peer whether we are | 
| 944 |     // interested or not. | 
| 945 |     bool interested = false; | 
| 946 |     int piecesSize = pieces.size(); | 
| 947 |     for (int pieceIndex = 0; pieceIndex < piecesSize; ++pieceIndex) { | 
| 948 |         if (!pieces.testBit(i: pieceIndex)) | 
| 949 |             continue; | 
| 950 |         if (!d->completedPieces.testBit(i: pieceIndex)) { | 
| 951 |             interested = true; | 
| 952 |             if ((client->peerWireState() & PeerWireClient::InterestedInPeer) == 0) { | 
| 953 |                 if (peer) | 
| 954 |                     peer->interesting = true; | 
| 955 |                 client->sendInterested(); | 
| 956 |             } | 
| 957 |  | 
| 958 |             QMultiMap<PeerWireClient *, TorrentPiece *>::Iterator it = d->payloads.find(akey: client); | 
| 959 |             int inProgress = 0; | 
| 960 |             while (it != d->payloads.end() && it.key() == client) { | 
| 961 |                 if (it.value()->inProgress) | 
| 962 |                     inProgress += it.value()->requestedBlocks.count(on: true); | 
| 963 |                 ++it; | 
| 964 |             } | 
| 965 |             if (!inProgress) | 
| 966 |                 d->callScheduler(); | 
| 967 |             break; | 
| 968 |         } | 
| 969 |     } | 
| 970 |     if (!interested && (client->peerWireState() & PeerWireClient::InterestedInPeer)) { | 
| 971 |         if (peer) | 
| 972 |             peer->interesting = false; | 
| 973 |         client->sendNotInterested(); | 
| 974 |     } | 
| 975 | } | 
| 976 |  | 
| 977 | void TorrentClient::peerRequestsBlock(int pieceIndex, int begin, int length) | 
| 978 | { | 
| 979 |     PeerWireClient *client = qobject_cast<PeerWireClient *>(object: sender()); | 
| 980 |  | 
| 981 |     // Silently ignore requests from choked peers | 
| 982 |     if (client->peerWireState() & PeerWireClient::ChokingPeer) | 
| 983 |         return; | 
| 984 |  | 
| 985 |     // Silently ignore requests for pieces we don't have. | 
| 986 |     if (!d->completedPieces.testBit(i: pieceIndex)) | 
| 987 |         return; | 
| 988 |  | 
| 989 |     // Request the block from the file manager | 
| 990 |     d->readIds.insert(akey: d->fileManager.read(pieceIndex, offset: begin, length), | 
| 991 |                       avalue: qobject_cast<PeerWireClient *>(object: sender())); | 
| 992 | } | 
| 993 |  | 
| 994 | void TorrentClient::blockReceived(int pieceIndex, int begin, const QByteArray &data) | 
| 995 | { | 
| 996 |     PeerWireClient *client = qobject_cast<PeerWireClient *>(object: sender()); | 
| 997 |     if (data.size() == 0) { | 
| 998 |         client->abort(); | 
| 999 |         return; | 
| 1000 |     } | 
| 1001 |  | 
| 1002 |     // Ignore it if we already have this block. | 
| 1003 |     int blockBit = begin / BlockSize; | 
| 1004 |     TorrentPiece *piece = d->pendingPieces.value(akey: pieceIndex); | 
| 1005 |     if (!piece || piece->completedBlocks.testBit(i: blockBit)) { | 
| 1006 |         // Discard blocks that we already have, and fill up the pipeline. | 
| 1007 |         requestMore(client); | 
| 1008 |         return; | 
| 1009 |     } | 
| 1010 |  | 
| 1011 |     // If we are in warmup or endgame mode, cancel all duplicate | 
| 1012 |     // requests for this block. | 
| 1013 |     if (d->state == WarmingUp || d->state == Endgame) { | 
| 1014 |         QMultiMap<PeerWireClient *, TorrentPiece *>::Iterator it = d->payloads.begin(); | 
| 1015 |         while (it != d->payloads.end()) { | 
| 1016 |             PeerWireClient *otherClient = it.key(); | 
| 1017 |             if (otherClient != client && it.value()->index == pieceIndex) { | 
| 1018 |                 if (otherClient->incomingBlocks().contains(t: TorrentBlock(pieceIndex, begin, data.size()))) | 
| 1019 |                     it.key()->cancelRequest(piece: pieceIndex, offset: begin, length: data.size()); | 
| 1020 |             } | 
| 1021 |             ++it; | 
| 1022 |         } | 
| 1023 |     } | 
| 1024 |  | 
| 1025 |     if (d->state != Downloading && d->state != Endgame && d->completedPieces.count(on: true) > 0) | 
| 1026 |         d->setState(Downloading); | 
| 1027 |  | 
| 1028 |     // Store this block | 
| 1029 |     d->fileManager.write(pieceIndex, offset: begin, data); | 
| 1030 |     piece->completedBlocks.setBit(blockBit); | 
| 1031 |     piece->requestedBlocks.clearBit(i: blockBit); | 
| 1032 |  | 
| 1033 |     if (blocksLeftForPiece(piece) == 0) { | 
| 1034 |         // Ask the file manager to verify the newly downloaded piece | 
| 1035 |         d->fileManager.verifyPiece(pieceIndex: piece->index); | 
| 1036 |  | 
| 1037 |         // Remove this piece from all payloads | 
| 1038 |         QMultiMap<PeerWireClient *, TorrentPiece *>::Iterator it = d->payloads.begin(); | 
| 1039 |         while (it != d->payloads.end()) { | 
| 1040 |             if (!it.value() || it.value()->index == piece->index) | 
| 1041 |                 it = d->payloads.erase(it); | 
| 1042 |             else | 
| 1043 |                 ++it; | 
| 1044 |         } | 
| 1045 |     } | 
| 1046 |  | 
| 1047 |     // Fill up the pipeline. | 
| 1048 |     requestMore(client); | 
| 1049 | } | 
| 1050 |  | 
| 1051 | void TorrentClient::peerWireBytesWritten(qint64 size) | 
| 1052 | { | 
| 1053 |     if (!d->transferRateTimer) | 
| 1054 |         d->transferRateTimer = startTimer(interval: RateControlTimerDelay); | 
| 1055 |  | 
| 1056 |     d->uploadRate[0] += size; | 
| 1057 |     d->uploadedBytes += size; | 
| 1058 |     emit dataSent(uploadedBytes: size); | 
| 1059 | } | 
| 1060 |  | 
| 1061 | void TorrentClient::peerWireBytesReceived(qint64 size) | 
| 1062 | { | 
| 1063 |     if (!d->transferRateTimer) | 
| 1064 |         d->transferRateTimer = startTimer(interval: RateControlTimerDelay); | 
| 1065 |  | 
| 1066 |     d->downloadRate[0] += size; | 
| 1067 |     d->downloadedBytes += size; | 
| 1068 |     emit dataSent(uploadedBytes: size); | 
| 1069 | } | 
| 1070 |  | 
| 1071 | int TorrentClient::blocksLeftForPiece(const TorrentPiece *piece) const | 
| 1072 | { | 
| 1073 |     int blocksLeft = 0; | 
| 1074 |     int completedBlocksSize = piece->completedBlocks.size(); | 
| 1075 |     for (int i = 0; i < completedBlocksSize; ++i) { | 
| 1076 |         if (!piece->completedBlocks.testBit(i)) | 
| 1077 |             ++blocksLeft; | 
| 1078 |     } | 
| 1079 |     return blocksLeft; | 
| 1080 | } | 
| 1081 |  | 
| 1082 | void TorrentClient::scheduleUploads() | 
| 1083 | { | 
| 1084 |     // Generate a list of clients sorted by their transfer | 
| 1085 |     // speeds.  When leeching, we sort by download speed, and when | 
| 1086 |     // seeding, we sort by upload speed. Seeds are left out; there's | 
| 1087 |     // no use in unchoking them. | 
| 1088 |     QList<PeerWireClient *> allClients = d->connections; | 
| 1089 |     QVector<QPair<qint64, PeerWireClient *>> transferSpeeds; | 
| 1090 |     for (PeerWireClient *client : qAsConst(t&: allClients)) { | 
| 1091 |         if (client->state() == QAbstractSocket::ConnectedState | 
| 1092 |             && client->availablePieces().count(on: true) != d->pieceCount) { | 
| 1093 |             if (d->state == Seeding) { | 
| 1094 |                 transferSpeeds.push_back(t: {client->uploadSpeed(), client}); | 
| 1095 |             } else { | 
| 1096 |                 transferSpeeds.push_back(t: {client->downloadSpeed(), client}); | 
| 1097 |             } | 
| 1098 |         } | 
| 1099 |     } | 
| 1100 |  | 
| 1101 |     std::sort(first: transferSpeeds.begin(), last: transferSpeeds.end()); | 
| 1102 |  | 
| 1103 |     // Unchoke the top 'MaxUploads' downloaders (peers that we are | 
| 1104 |     // uploading to) and choke all others. | 
| 1105 |     int maxUploaders = MaxUploads; | 
| 1106 |     for (auto rit = transferSpeeds.crbegin(), rend = transferSpeeds.crend(); rit != rend; ++rit) { | 
| 1107 |         PeerWireClient *client = rit->second; | 
| 1108 |         bool interested = (client->peerWireState() & PeerWireClient::PeerIsInterested); | 
| 1109 |  | 
| 1110 |         if (maxUploaders) { | 
| 1111 |             allClients.removeAll(t: client); | 
| 1112 |             if (client->peerWireState() & PeerWireClient::ChokingPeer) | 
| 1113 |                 client->unchokePeer(); | 
| 1114 |             --maxUploaders; | 
| 1115 |             continue; | 
| 1116 |         } | 
| 1117 |  | 
| 1118 |         if ((client->peerWireState() & PeerWireClient::ChokingPeer) == 0) { | 
| 1119 |             if ((QRandomGenerator::global()->bounded(highest: 10)) == 0) | 
| 1120 |                 client->abort(); | 
| 1121 |             else | 
| 1122 |                 client->chokePeer(); | 
| 1123 |             allClients.removeAll(t: client); | 
| 1124 |         } | 
| 1125 |         if (!interested) | 
| 1126 |             allClients.removeAll(t: client); | 
| 1127 |     } | 
| 1128 |  | 
| 1129 |     // Only interested peers are left in allClients. Unchoke one | 
| 1130 |     // random peer to allow it to compete for a position among the | 
| 1131 |     // downloaders.  (This is known as an "optimistic unchoke".) | 
| 1132 |     if (!allClients.isEmpty()) { | 
| 1133 |         PeerWireClient *client = allClients[QRandomGenerator::global()->bounded(highest: allClients.size())]; | 
| 1134 |         if (client->peerWireState() & PeerWireClient::ChokingPeer) | 
| 1135 |             client->unchokePeer(); | 
| 1136 |     } | 
| 1137 | } | 
| 1138 |  | 
| 1139 | void TorrentClient::scheduleDownloads() | 
| 1140 | { | 
| 1141 |     d->schedulerCalled = false; | 
| 1142 |  | 
| 1143 |     if (d->state == Stopping || d->state == Paused || d->state == Idle) | 
| 1144 |         return; | 
| 1145 |  | 
| 1146 |     // Check what each client is doing, and assign payloads to those | 
| 1147 |     // who are either idle or done. | 
| 1148 |     for (PeerWireClient *client : qAsConst(t&: d->connections)) | 
| 1149 |         schedulePieceForClient(client); | 
| 1150 | } | 
| 1151 |  | 
| 1152 | void TorrentClient::schedulePieceForClient(PeerWireClient *client) | 
| 1153 | { | 
| 1154 |     // Only schedule connected clients. | 
| 1155 |     if (client->state() != QTcpSocket::ConnectedState) | 
| 1156 |         return; | 
| 1157 |  | 
| 1158 |     // The peer has choked us; try again later. | 
| 1159 |     if (client->peerWireState() & PeerWireClient::ChokedByPeer) | 
| 1160 |         return; | 
| 1161 |  | 
| 1162 |     // Make a list of all the client's pending pieces, and count how | 
| 1163 |     // many blocks have been requested. | 
| 1164 |     QList<int> currentPieces; | 
| 1165 |     bool somePiecesAreNotInProgress = false; | 
| 1166 |     TorrentPiece *lastPendingPiece = nullptr; | 
| 1167 |     QMultiMap<PeerWireClient *, TorrentPiece *>::Iterator it = d->payloads.find(akey: client); | 
| 1168 |     while (it != d->payloads.end() && it.key() == client) { | 
| 1169 |         lastPendingPiece = it.value(); | 
| 1170 |         if (lastPendingPiece->inProgress) { | 
| 1171 |             currentPieces << lastPendingPiece->index; | 
| 1172 |         } else { | 
| 1173 |             somePiecesAreNotInProgress = true; | 
| 1174 |         } | 
| 1175 |         ++it; | 
| 1176 |     } | 
| 1177 |  | 
| 1178 |     // Skip clients that already have too many blocks in progress. | 
| 1179 |     if (client->incomingBlocks().size() >= ((d->state == Endgame || d->state == WarmingUp) | 
| 1180 |                                             ? MaxBlocksInMultiMode : MaxBlocksInProgress)) | 
| 1181 |         return; | 
| 1182 |  | 
| 1183 |     // If all pieces are in progress, but we haven't filled up our | 
| 1184 |     // block requesting quota, then we need to schedule another piece. | 
| 1185 |     if (!somePiecesAreNotInProgress || client->incomingBlocks().size() > 0) | 
| 1186 |         lastPendingPiece = nullptr; | 
| 1187 |     TorrentPiece *piece = lastPendingPiece; | 
| 1188 |  | 
| 1189 |     // In warmup state, all clients request blocks from the same pieces. | 
| 1190 |     if (d->state == WarmingUp && d->pendingPieces.size() >= 4) { | 
| 1191 |         piece = d->payloads.value(akey: client); | 
| 1192 |         if (!piece) { | 
| 1193 |             QList<TorrentPiece *> values = d->pendingPieces.values(); | 
| 1194 |             piece = values.value(i: QRandomGenerator::global()->bounded(highest: values.size())); | 
| 1195 |             piece->inProgress = true; | 
| 1196 |             d->payloads.insert(akey: client, avalue: piece); | 
| 1197 |         } | 
| 1198 |         if (piece->completedBlocks.count(on: false) == client->incomingBlocks().size()) | 
| 1199 |             return; | 
| 1200 |     } | 
| 1201 |  | 
| 1202 |     // If no pieces are currently in progress, schedule a new one. | 
| 1203 |     if (!piece) { | 
| 1204 |         // Build up a list of what pieces that we have not completed | 
| 1205 |         // are available to this client. | 
| 1206 |         QBitArray incompletePiecesAvailableToClient = d->incompletePieces; | 
| 1207 |  | 
| 1208 |         // Remove all pieces that are marked as being in progress | 
| 1209 |         // already (i.e., pieces that this or other clients are | 
| 1210 |         // already waiting for). A special rule applies to warmup and | 
| 1211 |         // endgame mode; there, we allow several clients to request | 
| 1212 |         // the same piece. In endgame mode, this only applies to | 
| 1213 |         // clients that are currently uploading (more than 1.0KB/s). | 
| 1214 |         if ((d->state == Endgame && client->uploadSpeed() < 1024) || d->state != WarmingUp) { | 
| 1215 |             QMap<int, TorrentPiece *>::ConstIterator it = d->pendingPieces.constBegin(); | 
| 1216 |             while (it != d->pendingPieces.constEnd()) { | 
| 1217 |                 if (it.value()->inProgress) | 
| 1218 |                     incompletePiecesAvailableToClient.clearBit(i: it.key()); | 
| 1219 |                 ++it; | 
| 1220 |             } | 
| 1221 |         } | 
| 1222 |  | 
| 1223 |         // Remove all pieces that the client cannot download. | 
| 1224 |         incompletePiecesAvailableToClient &= client->availablePieces(); | 
| 1225 |  | 
| 1226 |         // Remove all pieces that this client has already requested. | 
| 1227 |         for (int i : qAsConst(t&: currentPieces)) | 
| 1228 |             incompletePiecesAvailableToClient.clearBit(i); | 
| 1229 |  | 
| 1230 |         // Only continue if more pieces can be scheduled. If no pieces | 
| 1231 |         // are available and no blocks are in progress, just leave | 
| 1232 |         // the connection idle; it might become interesting later. | 
| 1233 |         if (incompletePiecesAvailableToClient.count(on: true) == 0) | 
| 1234 |             return; | 
| 1235 |  | 
| 1236 |         // Check if any of the partially completed pieces can be | 
| 1237 |         // recovered, and if so, pick a random one of them. | 
| 1238 |         QList<TorrentPiece *> partialPieces; | 
| 1239 |         QMap<int, TorrentPiece *>::ConstIterator it = d->pendingPieces.constBegin(); | 
| 1240 |         while (it != d->pendingPieces.constEnd()) { | 
| 1241 |             TorrentPiece *tmp = it.value(); | 
| 1242 |             if (incompletePiecesAvailableToClient.testBit(i: it.key())) { | 
| 1243 |                 if (!tmp->inProgress || d->state == WarmingUp || d->state == Endgame) { | 
| 1244 |                     partialPieces << tmp; | 
| 1245 |                     break; | 
| 1246 |                 } | 
| 1247 |             } | 
| 1248 |             ++it; | 
| 1249 |         } | 
| 1250 |         if (!partialPieces.isEmpty()) | 
| 1251 |             piece = partialPieces.value(i: QRandomGenerator::global()->bounded(highest: partialPieces.size())); | 
| 1252 |  | 
| 1253 |         if (!piece) { | 
| 1254 |             // Pick a random piece 3 out of 4 times; otherwise, pick either | 
| 1255 |             // one of the most common or the least common pieces available, | 
| 1256 |             // depending on the state we're in. | 
| 1257 |             int pieceIndex = 0; | 
| 1258 |             if (d->state == WarmingUp || (QRandomGenerator::global()->generate() & 4) == 0) { | 
| 1259 |                 int *occurrences = new int[d->pieceCount]; | 
| 1260 |                 memset(s: occurrences, c: 0, n: d->pieceCount * sizeof(int)); | 
| 1261 |  | 
| 1262 |                 // Count how many of each piece are available. | 
| 1263 |                 for (PeerWireClient *peer : qAsConst(t&: d->connections)) { | 
| 1264 |                     QBitArray peerPieces = peer->availablePieces(); | 
| 1265 |                     int peerPiecesSize = peerPieces.size(); | 
| 1266 |                     for (int i = 0; i < peerPiecesSize; ++i) { | 
| 1267 |                         if (peerPieces.testBit(i)) | 
| 1268 |                             ++occurrences[i]; | 
| 1269 |                     } | 
| 1270 |                 } | 
| 1271 |  | 
| 1272 |                 // Find the rarest or most common pieces. | 
| 1273 |                 int numOccurrences = d->state == WarmingUp ? 0 : 99999; | 
| 1274 |                 QList<int> piecesReadyForDownload; | 
| 1275 |                 for (int i = 0; i < d->pieceCount; ++i) { | 
| 1276 |                     if (d->state == WarmingUp) { | 
| 1277 |                         // Add common pieces | 
| 1278 |                         if (occurrences[i] >= numOccurrences | 
| 1279 |                             && incompletePiecesAvailableToClient.testBit(i)) { | 
| 1280 |                             if (occurrences[i] > numOccurrences) | 
| 1281 |                                 piecesReadyForDownload.clear(); | 
| 1282 |                             piecesReadyForDownload.append(t: i); | 
| 1283 |                             numOccurrences = occurrences[i]; | 
| 1284 |                         } | 
| 1285 |                     } else { | 
| 1286 |                         // Add rare pieces | 
| 1287 |                         if (occurrences[i] <= numOccurrences | 
| 1288 |                             && incompletePiecesAvailableToClient.testBit(i)) { | 
| 1289 |                             if (occurrences[i] < numOccurrences) | 
| 1290 |                                 piecesReadyForDownload.clear(); | 
| 1291 |                             piecesReadyForDownload.append(t: i); | 
| 1292 |                             numOccurrences = occurrences[i]; | 
| 1293 |                         } | 
| 1294 |                     } | 
| 1295 |                 } | 
| 1296 |  | 
| 1297 |                 // Select one piece randomly | 
| 1298 |                 pieceIndex = piecesReadyForDownload.at(i: QRandomGenerator::global()->bounded(highest: piecesReadyForDownload.size())); | 
| 1299 |                 delete [] occurrences; | 
| 1300 |             } else { | 
| 1301 |                 // Make up a list of available piece indices, and pick | 
| 1302 |                 // a random one. | 
| 1303 |                 QList<int> values; | 
| 1304 |                 int incompletePiecesAvailableToClientSize = incompletePiecesAvailableToClient.size(); | 
| 1305 |                 for (int i = 0; i < incompletePiecesAvailableToClientSize; ++i) { | 
| 1306 |                     if (incompletePiecesAvailableToClient.testBit(i)) | 
| 1307 |                         values << i; | 
| 1308 |                 } | 
| 1309 |                 pieceIndex = values.at(i: QRandomGenerator::global()->bounded(highest: values.size())); | 
| 1310 |             } | 
| 1311 |  | 
| 1312 |             // Create a new TorrentPiece and fill in all initial | 
| 1313 |             // properties. | 
| 1314 |             piece = new TorrentPiece; | 
| 1315 |             piece->index = pieceIndex; | 
| 1316 |             piece->length = d->fileManager.pieceLengthAt(pieceIndex); | 
| 1317 |             int numBlocks = piece->length / BlockSize; | 
| 1318 |             if (piece->length % BlockSize) | 
| 1319 |                 ++numBlocks; | 
| 1320 |             piece->completedBlocks.resize(size: numBlocks); | 
| 1321 |             piece->requestedBlocks.resize(size: numBlocks); | 
| 1322 |             d->pendingPieces.insert(akey: pieceIndex, avalue: piece); | 
| 1323 |         } | 
| 1324 |  | 
| 1325 |         piece->inProgress = true; | 
| 1326 |         d->payloads.insert(akey: client, avalue: piece); | 
| 1327 |     } | 
| 1328 |  | 
| 1329 |     // Request more blocks from all pending pieces. | 
| 1330 |     requestMore(client); | 
| 1331 | } | 
| 1332 |  | 
| 1333 | void TorrentClient::requestMore(PeerWireClient *client) | 
| 1334 | { | 
| 1335 |     // Make a list of all pieces this client is currently waiting for, | 
| 1336 |     // and count the number of blocks in progress. | 
| 1337 |     QMultiMap<PeerWireClient *, TorrentPiece *>::Iterator it = d->payloads.find(akey: client); | 
| 1338 |     int numBlocksInProgress = client->incomingBlocks().size(); | 
| 1339 |     QList<TorrentPiece *> piecesInProgress; | 
| 1340 |     while (it != d->payloads.end() && it.key() == client) { | 
| 1341 |         TorrentPiece *piece = it.value(); | 
| 1342 |         if (piece->inProgress || (d->state == WarmingUp || d->state == Endgame)) | 
| 1343 |             piecesInProgress << piece; | 
| 1344 |         ++it; | 
| 1345 |     } | 
| 1346 |  | 
| 1347 |     // If no pieces are in progress, call the scheduler. | 
| 1348 |     if (piecesInProgress.isEmpty() && d->incompletePieces.count(on: true)) { | 
| 1349 |         d->callScheduler(); | 
| 1350 |         return; | 
| 1351 |     } | 
| 1352 |  | 
| 1353 |     // If too many pieces are in progress, there's nothing to do. | 
| 1354 |     int maxInProgress = ((d->state == Endgame || d->state == WarmingUp) | 
| 1355 |                          ? MaxBlocksInMultiMode : MaxBlocksInProgress); | 
| 1356 |     if (numBlocksInProgress == maxInProgress) | 
| 1357 |         return; | 
| 1358 |  | 
| 1359 |     // Starting with the first piece that we're waiting for, request | 
| 1360 |     // blocks until the quota is filled up. | 
| 1361 |     for (TorrentPiece *piece : qAsConst(t&: piecesInProgress)) { | 
| 1362 |         numBlocksInProgress += requestBlocks(client, piece, maxBlocks: maxInProgress - numBlocksInProgress); | 
| 1363 |         if (numBlocksInProgress == maxInProgress) | 
| 1364 |             break; | 
| 1365 |     } | 
| 1366 |  | 
| 1367 |     // If we still didn't fill up the quota, we need to schedule more | 
| 1368 |     // pieces. | 
| 1369 |     if (numBlocksInProgress < maxInProgress && d->state != WarmingUp) | 
| 1370 |         d->callScheduler(); | 
| 1371 | } | 
| 1372 |  | 
| 1373 | int TorrentClient::requestBlocks(PeerWireClient *client, TorrentPiece *piece, int maxBlocks) | 
| 1374 | { | 
| 1375 |     // Generate the list of incomplete blocks for this piece. | 
| 1376 |     QVector<int> bits; | 
| 1377 |     int completedBlocksSize = piece->completedBlocks.size(); | 
| 1378 |     for (int i = 0; i < completedBlocksSize; ++i) { | 
| 1379 |         if (!piece->completedBlocks.testBit(i) && !piece->requestedBlocks.testBit(i)) | 
| 1380 |             bits << i; | 
| 1381 |     } | 
| 1382 |  | 
| 1383 |     // Nothing more to request. | 
| 1384 |     if (bits.size() == 0) { | 
| 1385 |         if (d->state != WarmingUp && d->state != Endgame) | 
| 1386 |             return 0; | 
| 1387 |         bits.clear(); | 
| 1388 |         for (int i = 0; i < completedBlocksSize; ++i) { | 
| 1389 |             if (!piece->completedBlocks.testBit(i)) | 
| 1390 |                 bits << i; | 
| 1391 |         } | 
| 1392 |     } | 
| 1393 |  | 
| 1394 |     if (d->state == WarmingUp || d->state == Endgame) { | 
| 1395 |         // By randomizing the list of blocks to request, we | 
| 1396 |         // significantly speed up the warmup and endgame modes, where | 
| 1397 |         // the same blocks are requested from multiple peers. The | 
| 1398 |         // speedup comes from an increased chance of receiving | 
| 1399 |         // different blocks from the different peers. | 
| 1400 |         for (int i = 0; i < bits.size(); ++i) { | 
| 1401 |             int a = QRandomGenerator::global()->bounded(highest: bits.size()); | 
| 1402 |             int b = QRandomGenerator::global()->bounded(highest: bits.size()); | 
| 1403 |             int tmp = bits[a]; | 
| 1404 |             bits[a] = bits[b]; | 
| 1405 |             bits[b] = tmp; | 
| 1406 |         } | 
| 1407 |     } | 
| 1408 |  | 
| 1409 |     // Request no more blocks than we've been asked to. | 
| 1410 |     int blocksToRequest = qMin(a: maxBlocks, b: bits.size()); | 
| 1411 |  | 
| 1412 |     // Calculate the offset and size of each block, and send requests. | 
| 1413 |     for (int i = 0; i < blocksToRequest; ++i) { | 
| 1414 |         int blockSize = BlockSize; | 
| 1415 |         if ((piece->length % BlockSize) && bits.at(i) == completedBlocksSize - 1) | 
| 1416 |             blockSize = piece->length % BlockSize; | 
| 1417 |         client->requestBlock(piece: piece->index, offset: bits.at(i) * BlockSize, length: blockSize); | 
| 1418 |         piece->requestedBlocks.setBit(bits.at(i)); | 
| 1419 |     } | 
| 1420 |  | 
| 1421 |     return blocksToRequest; | 
| 1422 | } | 
| 1423 |  | 
| 1424 | void TorrentClient::peerChoked() | 
| 1425 | { | 
| 1426 |     PeerWireClient *client = qobject_cast<PeerWireClient *>(object: sender()); | 
| 1427 |     if (!client) | 
| 1428 |         return; | 
| 1429 |  | 
| 1430 |     // When the peer chokes us, we immediately forget about all blocks | 
| 1431 |     // we've requested from it. We also remove the piece from out | 
| 1432 |     // payload, making it available to other clients. | 
| 1433 |     QMultiMap<PeerWireClient *, TorrentPiece *>::Iterator it = d->payloads.find(akey: client); | 
| 1434 |     while (it != d->payloads.end() && it.key() == client) { | 
| 1435 |         it.value()->inProgress = false; | 
| 1436 |         it.value()->requestedBlocks.fill(aval: false); | 
| 1437 |         it = d->payloads.erase(it); | 
| 1438 |     } | 
| 1439 | } | 
| 1440 |  | 
| 1441 | void TorrentClient::peerUnchoked() | 
| 1442 | { | 
| 1443 |     PeerWireClient *client = qobject_cast<PeerWireClient *>(object: sender()); | 
| 1444 |     if (!client) | 
| 1445 |         return; | 
| 1446 |  | 
| 1447 |     // We got unchoked, which means we can request more blocks. | 
| 1448 |     if (d->state != Seeding) | 
| 1449 |         d->callScheduler(); | 
| 1450 | } | 
| 1451 |  | 
| 1452 | void TorrentClient::addToPeerList(const QList<TorrentPeer> &peerList) | 
| 1453 | { | 
| 1454 |     // Add peers we don't already know of to our list of peers. | 
| 1455 |     const QList<QHostAddress> addresses =  QNetworkInterface::allAddresses(); | 
| 1456 |     for (const TorrentPeer &peer : peerList) { | 
| 1457 |         if (addresses.contains(t: peer.address) | 
| 1458 |             && peer.port == TorrentServer::instance()->serverPort()) { | 
| 1459 |             // Skip our own server. | 
| 1460 |             continue; | 
| 1461 |         } | 
| 1462 |  | 
| 1463 |         bool known = false; | 
| 1464 |         for (const TorrentPeer *knownPeer : qAsConst(t&: d->peers)) { | 
| 1465 |             if (knownPeer->port == peer.port | 
| 1466 |                 && knownPeer->address == peer.address) { | 
| 1467 |                 known = true; | 
| 1468 |                 break; | 
| 1469 |             } | 
| 1470 |         } | 
| 1471 |         if (!known) { | 
| 1472 |             TorrentPeer *newPeer = new TorrentPeer; | 
| 1473 |             *newPeer = peer; | 
| 1474 |             newPeer->interesting = true; | 
| 1475 |             newPeer->seed = false; | 
| 1476 |             newPeer->lastVisited = 0; | 
| 1477 |             newPeer->connectStart = 0; | 
| 1478 |             newPeer->connectTime = 999999; | 
| 1479 |             newPeer->pieces.resize(size: d->pieceCount); | 
| 1480 |             newPeer->numCompletedPieces = 0; | 
| 1481 |             d->peers << newPeer; | 
| 1482 |         } | 
| 1483 |     } | 
| 1484 |  | 
| 1485 |     // If we've got more peers than we can connect to, we remove some | 
| 1486 |     // of the peers that have no (or low) activity. | 
| 1487 |     int maxPeers = ConnectionManager::instance()->maxConnections() * 3; | 
| 1488 |     if (d->peers.size() > maxPeers) { | 
| 1489 |         auto tooMany = d->peers.size() - maxPeers; | 
| 1490 |  | 
| 1491 |         // Find what peers are currently connected & active | 
| 1492 |         const auto firstNInactivePeers = [&tooMany, this] (TorrentPeer *peer) { | 
| 1493 |             if (!tooMany) | 
| 1494 |                 return false; | 
| 1495 |             for (const PeerWireClient *client : qAsConst(t&: d->connections)) { | 
| 1496 |                 if (client->peer() == peer && (client->downloadSpeed() + client->uploadSpeed()) > 1024) | 
| 1497 |                     return false; | 
| 1498 |             } | 
| 1499 |             --tooMany; | 
| 1500 |             return true; | 
| 1501 |         }; | 
| 1502 |         // Remove inactive peers from the peer list until we're below | 
| 1503 |         // the max connections count. | 
| 1504 |         d->peers.erase(afirst: std::remove_if(first: d->peers.begin(), last: d->peers.end(), | 
| 1505 |                                       pred: firstNInactivePeers), | 
| 1506 |                        alast: d->peers.end()); | 
| 1507 |         // If we still have too many peers, remove the oldest ones. | 
| 1508 |         d->peers.erase(afirst: d->peers.begin(), alast: d->peers.begin() + tooMany); | 
| 1509 |     } | 
| 1510 |  | 
| 1511 |     if (d->state != Paused && d->state != Stopping && d->state != Idle) { | 
| 1512 |         if (d->state == Searching || d->state == WarmingUp) | 
| 1513 |             connectToPeers(); | 
| 1514 |         else | 
| 1515 |             d->callPeerConnector(); | 
| 1516 |     } | 
| 1517 | } | 
| 1518 |  | 
| 1519 | void TorrentClient::trackerStopped() | 
| 1520 | { | 
| 1521 |     d->setState(Idle); | 
| 1522 |     emit stopped(); | 
| 1523 | } | 
| 1524 |  | 
| 1525 | void TorrentClient::updateProgress(int progress) | 
| 1526 | { | 
| 1527 |     if (progress == -1 && d->pieceCount > 0) { | 
| 1528 |         int newProgress = (d->completedPieces.count(on: true) * 100) / d->pieceCount; | 
| 1529 |         if (d->lastProgressValue != newProgress) { | 
| 1530 |             d->lastProgressValue = newProgress; | 
| 1531 |             emit progressUpdated(percentProgress: newProgress); | 
| 1532 |         } | 
| 1533 |     } else if (d->lastProgressValue != progress) { | 
| 1534 |         d->lastProgressValue = progress; | 
| 1535 |         emit progressUpdated(percentProgress: progress); | 
| 1536 |     } | 
| 1537 | } | 
| 1538 |  |