00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023 #include "Qt.h"
00024 #include "Torrent.h"
00025 #include "TorrentParser.h"
00026 #include "Imports.cpp"
00027
00029
00043 bool TorrentParser::parseAndLoadTorrent (const QByteArray &rawTorrentData,
00044 Torrent &torrent)
00045 {
00046 Torrent parsedTorrent;
00047
00048 bool loadedOk = loadAllTorrentData (rawTorrentData, parsedTorrent);
00049 if (!loadedOk)
00050 return false;
00051
00052 torrent = parsedTorrent;
00053 return true;
00054 }
00055
00057
00060 bool TorrentParser::loadAllTorrentData(const QByteArray &rawData,
00061 Torrent &torrent)
00062 {
00063 if (rawData.isEmpty())
00064 return false;
00065
00066 BDecoder bDecoder (rawData);
00067 auto_ptr <BItem> item = bDecoder.readNext();
00068 if (!bDecoder.hasReadAllCorrectly())
00069 return false;
00070
00071 const BDictionary *bBasicData = dynamic_cast <const BDictionary *>
00072 (item.get());
00073 if (!bBasicData)
00074 return false;
00075
00076 bool basicDataLoaded = loadTorrentBasicData (bBasicData, torrent);
00077 if (!basicDataLoaded)
00078 return false;
00079
00080 const BDictionary *bInfo = dynamic_cast <const BDictionary *>
00081 (bBasicData->item (InfoKeyName));
00082 if (!bInfo)
00083 return false;
00084
00085 bool filesInfoLoaded = loadTorrentFilesInfo (bInfo, torrent);
00086 if (!filesInfoLoaded)
00087 return false;
00088
00089 return true;
00090 }
00091
00093
00096 bool TorrentParser::loadTorrentBasicData (const BDictionary *bBasicData,
00097 Torrent &torrent)
00098 {
00099 Q_ASSERT (bBasicData);
00100
00101 bool announceLoaded = getAndLoadAnnounce (bBasicData, torrent);
00102 if (!announceLoaded)
00103 return false;
00104
00105
00106
00107 getAndLoadAnnounceList (bBasicData, torrent);
00108 getAndLoadCreationDate (bBasicData, torrent);
00109 getAndLoadComment (bBasicData, torrent);
00110 getAndLoadCreatedBy (bBasicData, torrent);
00111
00112 return true;
00113 }
00114
00116
00119 bool TorrentParser::loadTorrentFilesInfo (const BDictionary *bInfo,
00120 Torrent &torrent)
00121 {
00122 Q_ASSERT (bInfo);
00123
00124 bool pieceLengthLoaded = getAndLoadPieceLength (bInfo, torrent);
00125 if (!pieceLengthLoaded)
00126 return false;
00127
00128 bool piecesLoaded = getAndLoadPieces (bInfo, torrent);
00129 if (!piecesLoaded)
00130 return false;
00131
00132 bool singleFileInfoLoaded = getAndLoadSingleModeFileInfo (bInfo, torrent);
00133 if (!singleFileInfoLoaded) {
00134 bool multipleFileInfoLoaded = getAndLoadMultipleModeFileInfo
00135 (bInfo, torrent);
00136 if (!multipleFileInfoLoaded)
00137 return false;
00138 }
00139
00140
00141 getAndLoadPrivate (bInfo, torrent);
00142
00143 return true;
00144 }
00145
00147
00151 bool TorrentParser::getAndLoadAnnounce (const BDictionary *bBasicData,
00152 Torrent &torrent)
00153 {
00154 Q_ASSERT (bBasicData);
00155 const BString *bAnnounceUrl = dynamic_cast <const BString *>
00156 (bBasicData->item (AnnounceKeyName));
00157 if (!bAnnounceUrl)
00158 return false;
00159
00160 Uri announceUrl = Uri::fromUnencoded (bAnnounceUrl->value());
00161 if (!isValidAnnounceUrl (announceUrl))
00162 return false;
00163
00164 torrent.d->announce = announceUrl;
00165 return true;
00166 }
00167
00169
00173 bool TorrentParser::getAndLoadAnnounceList (const BDictionary *bBasicData,
00174 Torrent &torrent)
00175 {
00176 Q_ASSERT (bBasicData);
00177 const BList *bAnnounceList = dynamic_cast <const BList *>
00178 (bBasicData->item (AnnounceListKeyName));
00179 if (!bAnnounceList)
00180 return false;
00181
00182
00183
00184
00185
00186
00187
00188
00189 Torrent::AnnounceList loadedAnnounceList;
00190
00191
00192
00193 for (int i = 0; i < bAnnounceList->size(); ++i) {
00194 const BList *bTier = dynamic_cast <const BList *>
00195 (bAnnounceList->item (i));
00196 if (!bTier)
00197 return false;
00198
00199 QList <Uri> tier;
00200 bool convertedOk = convertBTierToTier (bTier, tier);
00201 if (convertedOk && !tier.isEmpty())
00202 loadedAnnounceList.append (tier);
00203 else
00204 return false;
00205 }
00206
00207 if (loadedAnnounceList.isEmpty())
00208 return false;
00209
00210 torrent.d->announceList = loadedAnnounceList;
00211 return true;
00212 }
00213
00215
00221 bool TorrentParser::getAndLoadCreationDate (const BDictionary *bBasicData,
00222 Torrent &torrent)
00223 {
00224 Q_ASSERT (bBasicData);
00225 const BInt *bTimeStamp = dynamic_cast <const BInt *>
00226 (bBasicData->item (CreationDateKeyName));
00227 if (!bTimeStamp)
00228 return false;
00229 else if (!isValidCreationDate (bTimeStamp->value()))
00230 return false;
00231
00232 torrent.d->creationDate.setTime_t (bTimeStamp->value());
00233 return true;
00234 }
00235
00237
00241 bool TorrentParser::getAndLoadComment (const BDictionary *bBasicData,
00242 Torrent &torrent)
00243 {
00244 Q_ASSERT (bBasicData);
00245 const BString *bComment = dynamic_cast <const BString *>
00246 (bBasicData->item (CommentKeyName));
00247 if (!bComment)
00248 return false;
00249
00250 torrent.d->comment = QString::fromUtf8 (bComment->value());
00251 return true;
00252 }
00253
00255
00259 bool TorrentParser::getAndLoadCreatedBy (const BDictionary *bBasicData,
00260 Torrent &torrent)
00261 {
00262 Q_ASSERT (bBasicData);
00263 const BString *bCreatedBy = dynamic_cast <const BString *>
00264 (bBasicData->item (CreatedByKeyName));
00265 if (!bCreatedBy)
00266 return false;
00267
00268 torrent.d->createdBy = QString::fromUtf8 (bCreatedBy->value());
00269 return true;
00270 }
00271
00273
00279 bool TorrentParser::getAndLoadPieceLength (const BDictionary *bInfo,
00280 Torrent &torrent)
00281 {
00282 Q_ASSERT (bInfo);
00283 const BInt *bPieceLength = dynamic_cast <const BInt *>
00284 (bInfo->item (PieceLengthKeyName));
00285 if (!bPieceLength)
00286 return false;
00287 else if (!isValidPieceLength (bPieceLength->value()))
00288 return false;
00289
00290 torrent.d->pieceLength = bPieceLength->value();
00291 return true;
00292 }
00293
00295
00300 bool TorrentParser::getAndLoadPieces (const BDictionary *bInfo,
00301 Torrent &torrent)
00302 {
00303 Q_ASSERT (bInfo);
00304 const BString *bPieces = dynamic_cast <const BString *>
00305 (bInfo->item (PiecesKeyName));
00306 if (!bPieces)
00307 return false;
00308
00309 Torrent::PieceList pieceList;
00310 bool convertedOk = convertPiecesToPieceList (bPieces->value(), pieceList);
00311 if (!convertedOk)
00312 return false;
00313
00314 torrent.d->pieces = pieceList;
00315 return true;
00316 }
00317
00319
00325 bool TorrentParser::getAndLoadPrivate (const BDictionary *bInfo,
00326 Torrent &torrent)
00327 {
00328 Q_ASSERT (bInfo);
00329 const BInt *bPrivateFlag = dynamic_cast <const BInt *>
00330 (bInfo->item (PrivateKeyName));
00331 if (!bPrivateFlag)
00332 return false;
00333 else if (!isValidPrivate (bPrivateFlag->value()))
00334 return false;
00335
00336 torrent.d->isPrivate = convertPrivateFlagToBool
00337 (bPrivateFlag->value());
00338 return true;
00339 }
00340
00342
00346 bool TorrentParser::getAndLoadSingleModeFileInfo (const BDictionary *bInfo,
00347 Torrent &torrent)
00348 {
00349 Q_ASSERT (bInfo);
00350 Torrent::FileInfo fileInfo;
00351 bool fileLengthLoaded = getAndLoadFileLength (bInfo, fileInfo);
00352 if (!fileLengthLoaded)
00353 return false;
00354
00355 QString fileName;
00356 bool fileNameLoaded = getAndLoadFileName (bInfo, fileName);
00357 if (!fileNameLoaded)
00358 return false;
00359 else
00360 fileInfo.filePath = fileName;
00361
00362
00363 getAndLoadFileChecksum (bInfo, fileInfo);
00364
00365 torrent.d->files.push_back (fileInfo);
00366 return true;
00367 }
00368
00370
00374 bool TorrentParser::getAndLoadMultipleModeFileInfo (const BDictionary *bInfo,
00375 Torrent &torrent)
00376 {
00377 Q_ASSERT (bInfo);
00378 QString directoryName;
00379 bool directoryNameLoaded = getAndLoadDirectoryName (bInfo, directoryName);
00380 if (!directoryNameLoaded)
00381 return false;
00382
00383 const BList *bFileInfoList = dynamic_cast <const BList *>
00384 (bInfo->item (FilesKeyName));
00385 if (!bFileInfoList)
00386 return false;
00387
00388 Torrent::FileInfoList fileInfoList;
00389 bool fileListLoaded = getAndLoadFileInfoList (bFileInfoList, fileInfoList);
00390 if (!fileListLoaded)
00391 return false;
00392
00393 appendDirectoryName (directoryName, DirectoryDelimiter, fileInfoList);
00394
00395 torrent.d->files = fileInfoList;
00396 return true;
00397 }
00398
00400
00406 bool TorrentParser::getAndLoadDirectoryName (const BDictionary *bFileInfo,
00407 QString &directoryName)
00408 {
00409 Q_ASSERT (bFileInfo);
00410
00411 return getAndLoadFileName (bFileInfo, directoryName);
00412 }
00413
00415
00421 bool TorrentParser::getAndLoadFileName (const BDictionary *bFileInfo,
00422 QString &fileName)
00423 {
00424 Q_ASSERT (bFileInfo);
00425 const BString *bFileName = dynamic_cast <const BString *>
00426 (bFileInfo->item (FileNameKeyName));
00427 if (!bFileName)
00428 return false;
00429
00430 fileName = QString::fromUtf8 (bFileName->value());
00431 return true;
00432 }
00433
00435
00439 bool TorrentParser::getAndLoadFilePath (const BDictionary *bFileInfo,
00440 QString &filePath)
00441 {
00442 Q_ASSERT (bFileInfo);
00443 const BList *bFilePath = dynamic_cast <const BList *>
00444 (bFileInfo->item (PathKeyName));
00445 if (!bFilePath)
00446 return false;
00447
00448 bool convertedOk = convertBFilePathToFilePath (bFilePath,
00449 DirectoryDelimiter, filePath);
00450 return convertedOk && !filePath.isEmpty();
00451 }
00452
00454
00460 bool TorrentParser::getAndLoadFileLength (const BDictionary *bFileInfo,
00461 Torrent::FileInfo &fileInfo)
00462 {
00463 Q_ASSERT (bFileInfo);
00464 const BInt *bFileLength = dynamic_cast <const BInt *>
00465 (bFileInfo->item (FileLengthKeyName));
00466 if (!bFileLength)
00467 return false;
00468 else if (!isValidFileLength (bFileLength->value()))
00469 return false;
00470
00471 fileInfo.fileLength = bFileLength->value();
00472 return true;
00473 }
00474
00476
00482 bool TorrentParser::getAndLoadFileChecksum (const BDictionary *bFileInfo,
00483 Torrent::FileInfo &fileInfo)
00484 {
00485 Q_ASSERT (bFileInfo);
00486 const BString *bFileChecksum = dynamic_cast <const BString *>
00487 (bFileInfo->item (ChecksumKeyName));
00488 if (!bFileChecksum)
00489 return false;
00490 else if (!isValidFileChecksum (bFileChecksum->value()))
00491 return false;
00492
00493 fileInfo.fileChecksum = Torrent::FileInfo::Checksum (bFileChecksum->value());
00494 return true;
00495 }
00496
00498
00502 bool TorrentParser::getAndLoadFileInfoList (const BList *bFileInfoList,
00503 Torrent::FileInfoList &fileInfoList)
00504 {
00505 Q_ASSERT (bFileInfoList);
00506
00507
00508 for (int i = 0; i < bFileInfoList->size(); ++i) {
00509 const BDictionary *bFileInfo = dynamic_cast <const BDictionary *>
00510 (bFileInfoList->item (i));
00511 if (!bFileInfo)
00512 return false;
00513
00514 Torrent::FileInfo fileInfo;
00515 bool fileInfoLoaded = getAndLoadFileInfo (bFileInfo, fileInfo);
00516 if (!fileInfoLoaded)
00517 return false;
00518
00519 fileInfoList.push_back (fileInfo);
00520 }
00521
00522 return !fileInfoList.isEmpty();
00523 }
00524
00526
00530 bool TorrentParser::getAndLoadFileInfo (const BDictionary *bFileInfo,
00531 Torrent::FileInfo &fileInfo)
00532 {
00533 Q_ASSERT (bFileInfo);
00534 bool fileLengthLoaded = getAndLoadFileLength (bFileInfo, fileInfo);
00535 if (!fileLengthLoaded)
00536 return false;
00537
00538 QString filePath;
00539 bool filePathLoaded = getAndLoadFilePath (bFileInfo, filePath);
00540 if (!filePathLoaded)
00541 return false;
00542 else
00543 fileInfo.filePath = filePath;
00544
00545
00546 getAndLoadFileChecksum (bFileInfo, fileInfo);
00547
00548 return true;
00549 }
00550
00552
00557 bool TorrentParser::isValidAnnounceUrl (const Uri &announceUrl)
00558 {
00559 return !announceUrl.scheme().isEmpty() && !announceUrl.authority().isEmpty();
00560 }
00561
00563
00567 bool TorrentParser::isValidCreationDate (int creationDate)
00568 {
00569 return creationDate >= 0;
00570 }
00571
00573
00577 bool TorrentParser::isValidPieceLength (int pieceLength)
00578 {
00579 return pieceLength > 0;
00580 }
00581
00583
00587 bool TorrentParser::isValidPieces (const QByteArray &pieces)
00588 {
00589 return !pieces.isEmpty() &&
00590 ((pieces.size() % Torrent::Piece::size()) == 0);
00591 }
00592
00594
00598 bool TorrentParser::isValidPrivate (int privateFlag)
00599 {
00600 return (privateFlag == 0) || (privateFlag == 1);
00601 }
00602
00604
00608 bool TorrentParser::isValidFileLength (qint64 fileLength)
00609 {
00610 return fileLength > 0;
00611 }
00612
00614
00618 bool TorrentParser::isValidFileChecksum (const QByteArray &fileChecksum)
00619 {
00620 return static_cast <uint> (fileChecksum.size()) ==
00621 Torrent::FileInfo::Checksum::size();
00622 }
00623
00625
00629 bool TorrentParser::convertBTierToTier (const BList *bTier, QList <Uri> &tier)
00630 {
00631 Q_ASSERT (bTier);
00632
00633
00634
00635 QList <Uri> loadedTier;
00636
00637
00638
00639 for (int i = 0; i < bTier->size(); ++i) {
00640 const BString *bAnnounceUrl = dynamic_cast <const BString *>
00641 (bTier->item (i));
00642 if (!bAnnounceUrl)
00643 return false;
00644
00645 Uri announceUrl = Uri::fromUnencoded (bAnnounceUrl->value());
00646 if (!isValidAnnounceUrl (announceUrl))
00647 return false;
00648
00649 loadedTier.append (announceUrl);
00650 }
00651
00652 tier = loadedTier;
00653 return true;
00654 }
00655
00657
00661 bool TorrentParser::convertPiecesToPieceList (const QByteArray &pieces,
00662 Torrent::PieceList &pieceList)
00663 {
00664 if (!isValidPieces (pieces))
00665 return false;
00666
00667
00668
00669 Torrent::PieceList convertedPieceList;
00670 for (int pieceIndex = 0; pieceIndex < pieces.size();
00671 pieceIndex += Torrent::Piece::size()) {
00672 convertedPieceList.push_back (
00673 Torrent::Piece (pieces.mid (pieceIndex, Torrent::Piece::size())));
00674 }
00675
00676 pieceList = convertedPieceList;
00677 return true;
00678 }
00679
00681
00688 bool TorrentParser::convertBFilePathToFilePath (const BList *bFilePath,
00689 const QString &delimiter,
00690 QString &filePath)
00691 {
00692 Q_ASSERT (bFilePath);
00693
00694
00695
00696 QString loadedFilePath;
00697
00698
00699
00700 for (int i = 0; i < bFilePath->size(); ++i) {
00701 const BString *bPathPart = dynamic_cast <const BString *>
00702 (bFilePath->item (i));
00703 if (!bPathPart)
00704 return false;
00705
00706 loadedFilePath += QString::fromUtf8 (bPathPart->value());
00707 if ((i + 1) < bFilePath->size())
00708 loadedFilePath += delimiter;
00709 }
00710
00711 filePath = loadedFilePath;
00712 return true;
00713 }
00714
00716
00721 bool TorrentParser::convertPrivateFlagToBool (int privateFlag)
00722 {
00723 Q_ASSERT (isValidPrivate (privateFlag));
00724
00725 return privateFlag == 1;
00726 }
00727
00729
00734 void TorrentParser::appendDirectoryName (const QString &directoryName,
00735 const QString &delimiter,
00736 Torrent::FileInfoList &fileInfoList)
00737 {
00738
00739
00740
00741 typedef QList <Torrent::FileInfo>::iterator FileInfoListIterator;
00742 for (FileInfoListIterator iter = fileInfoList.begin();
00743 iter != fileInfoList.end(); ++iter) {
00744 iter->filePath = directoryName + delimiter + iter->filePath;
00745 }
00746 }
00747
00748
00749 const char *TorrentParser::InfoKeyName = "info";
00750 const char *TorrentParser::AnnounceKeyName = "announce";
00751 const char *TorrentParser::AnnounceListKeyName = "announce-list";
00752 const char *TorrentParser::CreationDateKeyName = "creation date";
00753 const char *TorrentParser::CommentKeyName = "comments";
00754 const char *TorrentParser::CreatedByKeyName = "created by";
00755 const char *TorrentParser::PieceLengthKeyName = "piece length";
00756 const char *TorrentParser::PiecesKeyName = "pieces";
00757 const char *TorrentParser::PrivateKeyName = "private";
00758 const char *TorrentParser::FileNameKeyName = "name";
00759 const char *TorrentParser::FileLengthKeyName = "length";
00760 const char *TorrentParser::ChecksumKeyName = "md5sum";
00761 const char *TorrentParser::FilesKeyName = "files";
00762 const char *TorrentParser::PathKeyName = "path";
00763 const char *TorrentParser::DirectoryDelimiter = "/";