Browse Source

Handle exception when torrent file cannot be exported

Both `lt::create_torrent` constructor and `lt::create_torrent::generate()`
can throw an exception so we need to handle it to prevent the app from crashing.
adaptive-webui-19844
Vladimir Golovnev (Glassez) 3 years ago committed by Vladimir Golovnev
parent
commit
3faa7226e7
  1. 14
      src/base/bittorrent/bencoderesumedatastorage.cpp
  2. 15
      src/base/bittorrent/dbresumedatastorage.cpp
  3. 2
      src/base/bittorrent/session.cpp
  4. 38
      src/base/bittorrent/torrentcreatorthread.cpp
  5. 10
      src/base/bittorrent/torrentcreatorthread.h
  6. 25
      src/base/bittorrent/torrentinfo.cpp
  7. 3
      src/gui/addnewtorrentdialog.cpp

14
src/base/bittorrent/bencoderesumedatastorage.cpp

@ -338,18 +338,24 @@ void BitTorrent::BencodeResumeDataStorage::Worker::store(const TorrentID &id, co
if (torrentInfo) if (torrentInfo)
{ {
const QString torrentFilepath = m_resumeDataDir.absoluteFilePath(QString::fromLatin1("%1.torrent").arg(id.toString())); const QString torrentFilepath = m_resumeDataDir.absoluteFilePath(QString::fromLatin1("%1.torrent").arg(id.toString()));
const lt::create_torrent torrentCreator = lt::create_torrent(*torrentInfo);
const lt::entry metadata = torrentCreator.generate();
try try
{ {
const auto torrentCreator = lt::create_torrent(*torrentInfo);
const lt::entry metadata = torrentCreator.generate();
writeEntryToFile(torrentFilepath, metadata); writeEntryToFile(torrentFilepath, metadata);
} }
catch (const RuntimeError &err) catch (const RuntimeError &err)
{ {
LogMsg(tr("Couldn't save torrent metadata to '%1'. Error: %2") LogMsg(tr("Couldn't save torrent metadata to '%1'. Error: %2.")
.arg(torrentFilepath, err.message()), Log::CRITICAL); .arg(torrentFilepath, err.message()), Log::CRITICAL);
return; return;
} }
catch (const std::exception &err)
{
LogMsg(tr("Couldn't save torrent metadata to '%1'. Error: %2.")
.arg(torrentFilepath, QString::fromLocal8Bit(err.what())), Log::CRITICAL);
return;
}
} }
lt::entry data = lt::write_resume_data(p); lt::entry data = lt::write_resume_data(p);
@ -371,7 +377,7 @@ void BitTorrent::BencodeResumeDataStorage::Worker::store(const TorrentID &id, co
} }
catch (const RuntimeError &err) catch (const RuntimeError &err)
{ {
LogMsg(tr("Couldn't save torrent resume data to '%1'. Error: %2") LogMsg(tr("Couldn't save torrent resume data to '%1'. Error: %2.")
.arg(resumeFilepath, err.message()), Log::CRITICAL); .arg(resumeFilepath, err.message()), Log::CRITICAL);
} }
} }

15
src/base/bittorrent/dbresumedatastorage.cpp

@ -459,9 +459,18 @@ void BitTorrent::DBResumeDataStorage::Worker::store(const TorrentID &id, const L
const std::shared_ptr<lt::torrent_info> torrentInfo = std::move(p.ti); const std::shared_ptr<lt::torrent_info> torrentInfo = std::move(p.ti);
if (torrentInfo) if (torrentInfo)
{ {
const lt::create_torrent torrentCreator = lt::create_torrent(*torrentInfo); try
const lt::entry metadata = torrentCreator.generate(); {
lt::bencode(std::back_inserter(bencodedMetadata), metadata); const auto torrentCreator = lt::create_torrent(*torrentInfo);
const lt::entry metadata = torrentCreator.generate();
lt::bencode(std::back_inserter(bencodedMetadata), metadata);
}
catch (const std::exception &err)
{
LogMsg(tr("Couldn't save torrent metadata. Error: %1.")
.arg(QString::fromLocal8Bit(err.what())), Log::CRITICAL);
return;
}
columns.append(DB_COLUMN_METADATA); columns.append(DB_COLUMN_METADATA);
} }

2
src/base/bittorrent/session.cpp

@ -2321,7 +2321,7 @@ void Session::exportTorrentFile(const Torrent *torrent, TorrentExportFolder fold
} }
catch (const RuntimeError &err) catch (const RuntimeError &err)
{ {
LogMsg(tr("Couldn't export torrent metadata file '%1'. Reason: %2") LogMsg(tr("Couldn't export torrent metadata file '%1'. Reason: %2.")
.arg(newTorrentPath, err.message()), Log::WARNING); .arg(newTorrentPath, err.message()), Log::WARNING);
} }
} }

38
src/base/bittorrent/torrentcreatorthread.cpp

@ -98,6 +98,12 @@ void TorrentCreatorThread::sendProgressSignal(int currentPieceIdx, int totalPiec
emit updateProgress(static_cast<int>((currentPieceIdx * 100.) / totalPieces)); emit updateProgress(static_cast<int>((currentPieceIdx * 100.) / totalPieces));
} }
void TorrentCreatorThread::checkInterruptionRequested() const
{
if (isInterruptionRequested())
throw RuntimeError(tr("Operation aborted"));
}
void TorrentCreatorThread::run() void TorrentCreatorThread::run()
{ {
emit updateProgress(0); emit updateProgress(0);
@ -118,7 +124,7 @@ void TorrentCreatorThread::run()
// need to sort the file names by natural sort order // need to sort the file names by natural sort order
QStringList dirs = {m_params.inputPath}; QStringList dirs = {m_params.inputPath};
QDirIterator dirIter(m_params.inputPath, (QDir::AllDirs | QDir::NoDotAndDotDot), QDirIterator::Subdirectories); QDirIterator dirIter {m_params.inputPath, (QDir::AllDirs | QDir::NoDotAndDotDot), QDirIterator::Subdirectories};
while (dirIter.hasNext()) while (dirIter.hasNext())
{ {
dirIter.next(); dirIter.next();
@ -151,7 +157,7 @@ void TorrentCreatorThread::run()
fs.add_file(fileName.toStdString(), fileSizeMap[fileName]); fs.add_file(fileName.toStdString(), fileSizeMap[fileName]);
} }
if (isInterruptionRequested()) return; checkInterruptionRequested();
#if (LIBTORRENT_VERSION_NUM >= 20000) #if (LIBTORRENT_VERSION_NUM >= 20000)
lt::create_torrent newTorrent {fs, m_params.pieceSize, toNativeTorrentFormatFlag(m_params.torrentFormat)}; lt::create_torrent newTorrent {fs, m_params.pieceSize, toNativeTorrentFormatFlag(m_params.torrentFormat)};
@ -181,9 +187,7 @@ void TorrentCreatorThread::run()
lt::set_piece_hashes(newTorrent, Utils::Fs::toNativePath(parentPath).toStdString() lt::set_piece_hashes(newTorrent, Utils::Fs::toNativePath(parentPath).toStdString()
, [this, &newTorrent](const lt::piece_index_t n) , [this, &newTorrent](const lt::piece_index_t n)
{ {
if (isInterruptionRequested()) checkInterruptionRequested();
throw RuntimeError {tr("Create new torrent aborted.")};
sendProgressSignal(static_cast<LTUnderlyingType<lt::piece_index_t>>(n), newTorrent.num_pieces()); sendProgressSignal(static_cast<LTUnderlyingType<lt::piece_index_t>>(n), newTorrent.num_pieces());
}); });
@ -194,7 +198,7 @@ void TorrentCreatorThread::run()
// Is private ? // Is private ?
newTorrent.set_priv(m_params.isPrivate); newTorrent.set_priv(m_params.isPrivate);
if (isInterruptionRequested()) return; checkInterruptionRequested();
lt::entry entry = newTorrent.generate(); lt::entry entry = newTorrent.generate();
@ -202,32 +206,30 @@ void TorrentCreatorThread::run()
if (!m_params.source.isEmpty()) if (!m_params.source.isEmpty())
entry["info"]["source"] = m_params.source.toStdString(); entry["info"]["source"] = m_params.source.toStdString();
if (isInterruptionRequested()) return; checkInterruptionRequested();
// create the torrent // create the torrent
QFile outfile {m_params.savePath}; QFile outfile {m_params.savePath};
if (!outfile.open(QIODevice::WriteOnly)) if (!outfile.open(QIODevice::WriteOnly))
{ throw RuntimeError(outfile.errorString());
throw RuntimeError {tr("Create new torrent file failed. Reason: %1")
.arg(outfile.errorString())};
}
if (isInterruptionRequested()) return; checkInterruptionRequested();
lt::bencode(Utils::IO::FileDeviceOutputIterator {outfile}, entry); lt::bencode(Utils::IO::FileDeviceOutputIterator {outfile}, entry);
if (outfile.error() != QFileDevice::NoError) if (outfile.error() != QFileDevice::NoError)
{ throw RuntimeError(outfile.errorString());
throw RuntimeError {tr("Create new torrent file failed. Reason: %1")
.arg(outfile.errorString())};
}
outfile.close(); outfile.close();
emit updateProgress(100); emit updateProgress(100);
emit creationSuccess(m_params.savePath, parentPath); emit creationSuccess(m_params.savePath, parentPath);
} }
catch (const std::exception &e) catch (const RuntimeError &err)
{
emit creationFailure(tr("Create new torrent file failed. Reason: %1.").arg(err.message()));
}
catch (const std::exception &err)
{ {
emit creationFailure(e.what()); emit creationFailure(tr("Create new torrent file failed. Reason: %1.").arg(QString::fromLocal8Bit(err.what())));
} }
} }

10
src/base/bittorrent/torrentcreatorthread.h

@ -65,10 +65,11 @@ namespace BitTorrent
class TorrentCreatorThread final : public QThread class TorrentCreatorThread final : public QThread
{ {
Q_OBJECT Q_OBJECT
Q_DISABLE_COPY_MOVE(TorrentCreatorThread)
public: public:
TorrentCreatorThread(QObject *parent = nullptr); explicit TorrentCreatorThread(QObject *parent = nullptr);
~TorrentCreatorThread(); ~TorrentCreatorThread() override;
void create(const TorrentCreatorParams &params); void create(const TorrentCreatorParams &params);
@ -79,16 +80,15 @@ namespace BitTorrent
, const int pieceSize, const bool isAlignmentOptimized, int paddedFileSizeLimit); , const int pieceSize, const bool isAlignmentOptimized, int paddedFileSizeLimit);
#endif #endif
protected:
void run() override;
signals: signals:
void creationFailure(const QString &msg); void creationFailure(const QString &msg);
void creationSuccess(const QString &path, const QString &branchPath); void creationSuccess(const QString &path, const QString &branchPath);
void updateProgress(int progress); void updateProgress(int progress);
private: private:
void run() override;
void sendProgressSignal(int currentPieceIdx, int totalPieces); void sendProgressSignal(int currentPieceIdx, int totalPieces);
void checkInterruptionRequested() const;
TorrentCreatorParams m_params; TorrentCreatorParams m_params;
}; };

25
src/base/bittorrent/torrentinfo.cpp

@ -167,18 +167,25 @@ TorrentInfo TorrentInfo::loadFromFile(const QString &path, QString *error) noexc
void TorrentInfo::saveToFile(const QString &path) const void TorrentInfo::saveToFile(const QString &path) const
{ {
if (!isValid()) if (!isValid())
throw RuntimeError {tr("Invalid metadata.")}; throw RuntimeError {tr("Invalid metadata")};
const lt::create_torrent torrentCreator = lt::create_torrent(*(nativeInfo())); try
const lt::entry torrentEntry = torrentCreator.generate(); {
const auto torrentCreator = lt::create_torrent(*nativeInfo());
const lt::entry torrentEntry = torrentCreator.generate();
QFile torrentFile {path}; QFile torrentFile {path};
if (!torrentFile.open(QIODevice::WriteOnly)) if (!torrentFile.open(QIODevice::WriteOnly))
throw RuntimeError {torrentFile.errorString()}; throw RuntimeError(torrentFile.errorString());
lt::bencode(Utils::IO::FileDeviceOutputIterator {torrentFile}, torrentEntry); lt::bencode(Utils::IO::FileDeviceOutputIterator {torrentFile}, torrentEntry);
if (torrentFile.error() != QFileDevice::NoError) if (torrentFile.error() != QFileDevice::NoError)
throw RuntimeError {torrentFile.errorString()}; throw RuntimeError(torrentFile.errorString());
}
catch (const lt::system_error &err)
{
throw RuntimeError(QString::fromLocal8Bit(err.what()));
}
} }
bool TorrentInfo::isValid() const bool TorrentInfo::isValid() const

3
src/gui/addnewtorrentdialog.cpp

@ -475,7 +475,8 @@ void AddNewTorrentDialog::saveTorrentFile()
} }
catch (const RuntimeError &err) catch (const RuntimeError &err)
{ {
QMessageBox::critical(this, tr("I/O Error"), err.message()); QMessageBox::critical(this, tr("I/O Error")
, tr("Couldn't export torrent metadata file '%1'. Reason: %2.").arg(path, err.message()));
} }
} }

Loading…
Cancel
Save