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"