#ifdef DECENTRALIZED_UPDATE_ENABLED
#include <QFileInfo>
#include <QDebug>
#include <QThread>
#include <QCoreApplication>
#include <QNetworkProxy>
#include <vector>
#include <iostream>
#include "helpers_p.hpp"
#include "qappimageupdateenums.hpp"
#include "seeder_p.hpp"
SeederPrivate::SeederPrivate(QNetworkAccessManager *manager)
: QObject() {
lt::session_params p = lt::session_params();
p.settings.set_int(lt::settings_pack::alert_mask,
lt::alert_category::status |
lt::alert_category::error |
lt::alert_category::storage);
//// Set proxy for libtorrent.
auto proxy = manager->proxy();
if(proxy.type() != QNetworkProxy::NoProxy) {
emit logger("Using proxy for torrent seeding.");
p.settings.set_str(lt::settings_pack::proxy_hostname,
proxy.hostName().toStdString());
p.settings.set_int(lt::settings_pack::proxy_port,
(int)proxy.port());
p.settings.set_str(lt::settings_pack::proxy_username,
proxy.user().toStdString());
p.settings.set_str(lt::settings_pack::proxy_password,
proxy.password().toStdString());
/// Set Proxy type.
if(proxy.type() == QNetworkProxy::Socks5Proxy) {
p.settings.set_int(lt::settings_pack::proxy_type,
lt::settings_pack::socks5_pw);
}else if(proxy.type() == QNetworkProxy::HttpProxy) {
p.settings.set_int(lt::settings_pack::proxy_type,
lt::settings_pack::http_pw);
}else{
emit logger("Cannot find proxy type. Failed to set proxy.");
}
}
m_Manager = manager;
m_Session.reset(new lt::session(p));
m_TorrentMeta.reset(new QByteArray);
m_TimeoutTimer.setSingleShot(true);
m_TimeoutTimer.setInterval(100 * 1000); // 100 seconds
connect(&m_TimeoutTimer, &QTimer::timeout,
this, &SeederPrivate::handleTimeout,
Qt::QueuedConnection);
m_Timer.setSingleShot(false);
m_Timer.setInterval(100); // 1ms?
connect(&m_Timer, &QTimer::timeout,
this, &SeederPrivate::torrentLoop,
Qt::QueuedConnection);
}
SeederPrivate::~SeederPrivate() {
m_Session->abort();
}
void SeederPrivate::start(QJsonObject info) {
if(b_Running) {
return;
}
if(info.isEmpty()) {
emit error(QAppImageUpdateEnums::Error::ProtocolFailure);
return;
}
b_Running = false;
auto embeddedUpdateInformation = info["EmbededUpdateInformation"].toObject();
auto oldVersionInformation = embeddedUpdateInformation["FileInformation"].toObject();
QString remoteTargetFileSHA1Hash = info["RemoteTargetFileSHA1Hash"].toString(),
localAppImageSHA1Hash = oldVersionInformation["AppImageSHA1Hash"].toString(),
localAppImagePath = oldVersionInformation["AppImageFilePath"].toString();
bool torrentSupported = info["TorrentSupported"].toBool();
auto torrentFileUrl = info["TorrentFileUrl"].toString();
auto targetFileName = info["RemoteTargetFileName"].toString();
if(localAppImageSHA1Hash != remoteTargetFileSHA1Hash) {
emit error(QAppImageUpdateEnums::Error::OutdatedAppImageForSeed);
return;
}
if(!torrentSupported) {
emit error(QAppImageUpdateEnums::Error::TorrentNotSupported);
return;
}
m_TargetFilePath = localAppImagePath;
m_TorrentMeta->clear();
QNetworkRequest request;
request.setUrl(torrentFileUrl);
request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
auto reply = m_Manager->get(request);
connect(reply, SIGNAL(error(QNetworkReply::NetworkError)),
this, SLOT(handleTorrentFileError(QNetworkReply::NetworkError)));
connect(reply, SIGNAL(downloadProgress(qint64, qint64)),
this, SLOT(handleTorrentFileData(qint64, qint64)));
connect(reply, SIGNAL(finished()),
this, SLOT(handleTorrentFileFinish()));
b_Running = true;
emit started();
}
void SeederPrivate::cancel() {
if(!b_Running || b_CancelRequested) {
return;
}
b_CancelRequested = true;
}
void SeederPrivate::handleTorrentFileError(QNetworkReply::NetworkError code) {
QNetworkReply *reply = qobject_cast<QNetworkReply*>(QObject::sender());
if(!reply) {
return;
}
reply->disconnect();
reply->deleteLater();
emit error(translateQNetworkReplyError(code));
}
void SeederPrivate::handleTorrentFileData(qint64 br, qint64 bt) {
Q_UNUSED(br);
Q_UNUSED(bt);
auto reply = qobject_cast<QNetworkReply*>(QObject::sender());
if(!reply) {
return;
}
if(reply->error() != QNetworkReply::NoError) {
return;
}
if(reply->isReadable()) {
m_TorrentMeta->append(reply->readAll());
}
}
void SeederPrivate::handleTorrentFileFinish() {
auto reply = qobject_cast<QNetworkReply*>(QObject::sender());
m_TorrentMeta->append(reply->readAll());
reply->disconnect();
reply->deleteLater();
if(b_CancelRequested) {
b_CancelRequested = false;
b_Running = false;
emit canceled();
return;
}
lt::add_torrent_params params;
QString savePath = QFileInfo(m_TargetFilePath).path() + "/";
params.save_path = savePath.toStdString();
auto ti = std::make_shared<lt::torrent_info>(m_TorrentMeta->constData(), (int)m_TorrentMeta->size());
/// We know that MakeAppImageTorrent only packs a single file that is the
/// the Target AppImage. So We just need to check if it is bundled correctly.
if(ti->num_files() != 1) {
emit error(QNetworkReply::ProtocolFailure);
return;
}
/// Since only 1 file is packaged in the torrent, we can
/// assume that the file index for our Target AppImage is 0
ti->rename_file(0,
QFileInfo(m_TargetFilePath).fileName().toStdString());
//ti->add_url_seed(m_TargetFileUrl.toString().toStdString());
params.ti = ti;
m_Handle = m_Session->add_torrent(params);
if(!m_Handle.is_valid()) {
emit error(QAppImageUpdateEnums::Error::ProtocolFailure);
return;
}
m_Timer.setSingleShot(false);
m_Timer.setInterval(100);
m_Timer.start();
m_TimeoutTimer.start();
return;
}
void SeederPrivate::handleTimeout() {
m_TimeoutTimer.stop();
emit logger(QString::fromStdString(" handleTimeout: Torrent Seeder Timeout, failing."));
m_Session->abort();
b_Running = false;
emit error(QAppImageUpdateEnums::Error::ProtocolFailure);
}
void SeederPrivate::torrentLoop() {
if(!b_Running) {
/// To avoid queued calls from being called
return;
}
if(b_CancelRequested) {
m_TimeoutTimer.stop();
m_Timer.stop();
{
// The destruction of session proxy
// assures that all call writes and everything
// is finished. This is sync.
auto sess_proxy = m_Session->abort();
}
b_CancelRequested = false;
b_Running = false;
emit canceled();
return;
}
auto status = m_Handle.status();
emit torrentStatus(status.num_seeds, status.num_peers);
if(status.state == lt::torrent_status::seeding) {
m_TimeoutTimer.start();
}
if(status.state == lt::torrent_status::downloading) {
m_Timer.stop();
m_TimeoutTimer.stop();
{
// The destruction of session proxy
// assures that all call writes and everything
// is finished. This is sync.
auto sess_proxy = m_Session->abort();
}
b_Running = false;
emit error(QAppImageUpdateEnums::Error::IncompleteAppImageForSeed);
return;
}
std::vector<lt::alert*> alerts;
m_Session->pop_alerts(&alerts);
for (lt::alert const* a : alerts) {
if (lt::alert_cast<lt::torrent_error_alert>(a)) {
emit logger(QString::fromStdString(a->message()));
m_Timer.stop();
m_TimeoutTimer.stop();
{
// The destruction of session proxy
// assures that all call writes and everything
// is finished. This is sync.
auto sess_proxy = m_Session->abort();
}
b_Running = false;
emit error(QNetworkReply::ProtocolFailure);
return;
}
QCoreApplication::processEvents();
}
}
#endif // DECENTRALIZED_UPDATE_ENABLED
↑ V1048 The 'b_Running' variable was assigned the same value.