File indexing completed on 2026-02-08 04:58:05

0001 /*
0002     SPDX-FileCopyrightText: 2010 Joris Guisson <joris.guisson@gmail.com>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "ktcli.h"
0008 
0009 #include <cstdlib>
0010 #include <ctime>
0011 #include <unistd.h>
0012 
0013 #include <QCommandLineParser>
0014 #include <QDir>
0015 #include <QRandomGenerator>
0016 
0017 #include <interfaces/serverinterface.h>
0018 #include <peer/authenticationmonitor.h>
0019 #include <peer/utpex.h>
0020 #include <util/error.h>
0021 #include <util/fileops.h>
0022 #include <util/functions.h>
0023 #include <util/waitjob.h>
0024 #include <version.h>
0025 
0026 using namespace bt;
0027 
0028 KTCLI::KTCLI(int argc, char **argv)
0029     : QCoreApplication(argc, argv)
0030     , tc(new TorrentControl())
0031     , updates(0)
0032 {
0033     parser.addPositionalArgument("url", tr("Torrent to open"));
0034     parser.addOption(QCommandLineOption("port", tr("Port to use"), "<port>", "6881"));
0035     parser.addOption(QCommandLineOption("tmpdir", tr("Port to use"), "<tmpdir>", QDir::tempPath()));
0036     parser.addOption(QCommandLineOption("encryption", tr("Whether or not to enable encryption")));
0037     parser.addOption(QCommandLineOption("pex", tr("Whether or not to enable peer exchange")));
0038     parser.addOption(QCommandLineOption("utp", tr("Whether or not to use utp")));
0039     parser.process(*this);
0040 
0041     bt::SetClientInfo("ktcli", bt::MAJOR, bt::MINOR, bt::RELEASE, bt::NORMAL, "KT");
0042     bt::InitLog("ktcli.log", false, true, false);
0043     connect(tc.get(), &TorrentInterface::finished, this, &KTCLI::finished);
0044     connect(this, &QCoreApplication::aboutToQuit, this, &KTCLI::shutdown);
0045 }
0046 
0047 KTCLI::~KTCLI()
0048 {
0049 }
0050 
0051 bool KTCLI::start()
0052 {
0053     bool ok = false;
0054     quint16 port = parser.value("port").toInt(&ok);
0055     if (!ok)
0056         port = 1024 + QRandomGenerator::global()->bounded((1 << 16) - 1 - 1024); // Use non-root ports
0057 
0058     if (parser.isSet("encryption")) {
0059         Out(SYS_GEN | LOG_NOTICE) << "Enabled encryption" << endl;
0060         ServerInterface::enableEncryption(false);
0061     }
0062 
0063     if (parser.isSet("pex")) {
0064         Out(SYS_GEN | LOG_NOTICE) << "Enabled PEX" << endl;
0065         UTPex::setEnabled(true);
0066     }
0067 
0068     if (parser.isSet("utp")) {
0069         Out(SYS_GEN | LOG_NOTICE) << "Enabled UTP" << endl;
0070         if (!bt::Globals::instance().initUTPServer(port)) {
0071             Out(SYS_GEN | LOG_NOTICE) << "Failed to listen on port " << port << endl;
0072             return false;
0073         }
0074 
0075         ServerInterface::setPort(port);
0076         ServerInterface::setUtpEnabled(true, true);
0077         ServerInterface::setPrimaryTransportProtocol(UTP);
0078     } else {
0079         if (!bt::Globals::instance().initTCPServer(port)) {
0080             Out(SYS_GEN | LOG_NOTICE) << "Failed to listen on port " << port << endl;
0081             return false;
0082         }
0083     }
0084 
0085     if (parser.positionalArguments().isEmpty())
0086         return false;
0087     return load(QUrl::fromLocalFile(parser.positionalArguments().at(0)));
0088 }
0089 
0090 bool KTCLI::load(const QUrl &url)
0091 {
0092     QDir dir(url.toLocalFile());
0093     if (dir.exists() && dir.exists("torrent") && dir.exists("stats")) {
0094         // Load existing torrent
0095         if (loadFromDir(dir.absolutePath())) {
0096             tc->start();
0097             connect(&timer, &QTimer::timeout, this, &KTCLI::update);
0098             timer.start(250);
0099             return true;
0100         }
0101     } else if (url.isLocalFile()) {
0102         QString path = url.toLocalFile();
0103         if (loadFromFile(path)) {
0104             tc->start();
0105             connect(&timer, &QTimer::timeout, this, &KTCLI::update);
0106             timer.start(250);
0107             return true;
0108         }
0109     } else {
0110         Out(SYS_GEN | LOG_IMPORTANT) << "Non local files not supported" << endl;
0111     }
0112 
0113     return false;
0114 }
0115 
0116 QString KTCLI::tempDir()
0117 {
0118     QDir tmpdir = QDir(parser.value("tmpdir"));
0119     int i = 0;
0120     while (tmpdir.exists(QString("tor%1").arg(i)))
0121         i++;
0122 
0123     QString sd = QString("tor%1").arg(i);
0124     if (!tmpdir.mkdir(sd))
0125         throw bt::Error(QString("Failed to create temporary directory %1").arg(sd));
0126 
0127     tmpdir.cd(sd);
0128     return tmpdir.absolutePath();
0129 }
0130 
0131 bool KTCLI::loadFromFile(const QString &path)
0132 {
0133     try {
0134         tc->init(this, bt::LoadFile(path), tempDir(), QDir::currentPath());
0135         tc->setLoadUrl(QUrl(path));
0136         tc->createFiles();
0137         return true;
0138     } catch (bt::Error &err) {
0139         Out(SYS_GEN | LOG_IMPORTANT) << err.toString() << endl;
0140         return false;
0141     }
0142 }
0143 
0144 bool KTCLI::loadFromDir(const QString &path)
0145 {
0146     try {
0147         tc->init(this, bt::LoadFile(path + "/torrent"), path, QString());
0148         tc->createFiles();
0149         return true;
0150     } catch (bt::Error &err) {
0151         Out(SYS_GEN | LOG_IMPORTANT) << err.toString() << endl;
0152         return false;
0153     }
0154 }
0155 
0156 bool KTCLI::notify(QObject *obj, QEvent *ev)
0157 {
0158     try {
0159         return QCoreApplication::notify(obj, ev);
0160     } catch (bt::Error &err) {
0161         Out(SYS_GEN | LOG_DEBUG) << "Error: " << err.toString() << endl;
0162     } catch (std::exception &err) {
0163         Out(SYS_GEN | LOG_DEBUG) << "Error: " << err.what() << endl;
0164     }
0165 
0166     return true;
0167 }
0168 
0169 bool KTCLI::alreadyLoaded(const bt::SHA1Hash &ih) const
0170 {
0171     Q_UNUSED(ih);
0172     return false;
0173 }
0174 
0175 void KTCLI::mergeAnnounceList(const bt::SHA1Hash &ih, const bt::TrackerTier *trk)
0176 {
0177     Q_UNUSED(ih);
0178     Q_UNUSED(trk);
0179 }
0180 
0181 void KTCLI::update()
0182 {
0183     bt::UpdateCurrentTime();
0184     AuthenticationMonitor::instance().update();
0185     tc->update();
0186     updates++;
0187     if (updates % 20 == 0) {
0188         Out(SYS_GEN | LOG_DEBUG) << "Speed down " << bt::BytesPerSecToString(tc->getStats().download_rate) << endl;
0189         Out(SYS_GEN | LOG_DEBUG) << "Speed up   " << bt::BytesPerSecToString(tc->getStats().upload_rate) << endl;
0190     }
0191 }
0192 
0193 void KTCLI::finished(bt::TorrentInterface *tor)
0194 {
0195     Q_UNUSED(tor);
0196     Out(SYS_GEN | LOG_NOTICE) << "Torrent fully downloaded" << endl;
0197     QTimer::singleShot(0, this, &KTCLI::shutdown);
0198 }
0199 
0200 void KTCLI::shutdown()
0201 {
0202     AuthenticationMonitor::instance().shutdown();
0203 
0204     WaitJob *j = new WaitJob(2000);
0205     tc->stop(j);
0206     if (j->needToWait())
0207         j->exec();
0208     j->deleteLater();
0209 
0210     Globals::instance().shutdownTCPServer();
0211     Globals::instance().shutdownUTPServer();
0212     quit();
0213 }
0214 
0215 #include "moc_ktcli.cpp"