File indexing completed on 2023-09-24 04:08:44
0001 /* 0002 This file is part of the KDE libraries 0003 SPDX-FileCopyrightText: 2000 Stephan Kulow <coolo@kde.org> 0004 SPDX-FileCopyrightText: 2000-2009 David Faure <faure@kde.org> 0005 0006 SPDX-License-Identifier: LGPL-2.0-or-later 0007 */ 0008 0009 #include "storedtransferjob.h" 0010 #include "job_p.h" 0011 #include <KConfig> 0012 #include <KConfigGroup> 0013 #include <QTimer> 0014 #include <kurlauthorized.h> 0015 0016 using namespace KIO; 0017 0018 class KIO::StoredTransferJobPrivate : public TransferJobPrivate 0019 { 0020 public: 0021 StoredTransferJobPrivate(const QUrl &url, int command, const QByteArray &packedArgs, const QByteArray &_staticData) 0022 : TransferJobPrivate(url, command, packedArgs, _staticData) 0023 , m_uploadOffset(0) 0024 { 0025 } 0026 StoredTransferJobPrivate(const QUrl &url, int command, const QByteArray &packedArgs, QIODevice *ioDevice) 0027 : TransferJobPrivate(url, command, packedArgs, ioDevice) 0028 , m_uploadOffset(0) 0029 { 0030 } 0031 0032 QByteArray m_data; 0033 int m_uploadOffset; 0034 0035 void slotStoredData(KIO::Job *job, const QByteArray &data); 0036 void slotStoredDataReq(KIO::Job *job, QByteArray &data); 0037 0038 Q_DECLARE_PUBLIC(StoredTransferJob) 0039 0040 static inline StoredTransferJob *newJob(const QUrl &url, int command, const QByteArray &packedArgs, const QByteArray &staticData, JobFlags flags) 0041 { 0042 StoredTransferJob *job = new StoredTransferJob(*new StoredTransferJobPrivate(url, command, packedArgs, staticData)); 0043 job->setUiDelegate(KIO::createDefaultJobUiDelegate()); 0044 if (!(flags & HideProgressInfo)) { 0045 job->setFinishedNotificationHidden(); 0046 KIO::getJobTracker()->registerJob(job); 0047 } 0048 if (!(flags & NoPrivilegeExecution)) { 0049 job->d_func()->m_privilegeExecutionEnabled = true; 0050 job->d_func()->m_operationType = Transfer; 0051 } 0052 return job; 0053 } 0054 0055 static inline StoredTransferJob *newJob(const QUrl &url, int command, const QByteArray &packedArgs, QIODevice *ioDevice, JobFlags flags) 0056 { 0057 StoredTransferJob *job = new StoredTransferJob(*new StoredTransferJobPrivate(url, command, packedArgs, ioDevice)); 0058 job->setUiDelegate(KIO::createDefaultJobUiDelegate()); 0059 if (!(flags & HideProgressInfo)) { 0060 job->setFinishedNotificationHidden(); 0061 KIO::getJobTracker()->registerJob(job); 0062 } 0063 if (!(flags & NoPrivilegeExecution)) { 0064 job->d_func()->m_privilegeExecutionEnabled = true; 0065 job->d_func()->m_operationType = Transfer; 0066 } 0067 return job; 0068 } 0069 }; 0070 0071 StoredTransferJob::StoredTransferJob(StoredTransferJobPrivate &dd) 0072 : TransferJob(dd) 0073 { 0074 connect(this, &TransferJob::data, this, [this](KIO::Job *job, const QByteArray &data) { 0075 d_func()->slotStoredData(job, data); 0076 }); 0077 connect(this, &TransferJob::dataReq, this, [this](KIO::Job *job, QByteArray &data) { 0078 d_func()->slotStoredDataReq(job, data); 0079 }); 0080 } 0081 0082 StoredTransferJob::~StoredTransferJob() 0083 { 0084 } 0085 0086 void StoredTransferJob::setData(const QByteArray &arr) 0087 { 0088 Q_D(StoredTransferJob); 0089 Q_ASSERT(d->m_data.isNull()); // check that we're only called once 0090 Q_ASSERT(d->m_uploadOffset == 0); // no upload started yet 0091 d->m_data = arr; 0092 setTotalSize(d->m_data.size()); 0093 } 0094 0095 QByteArray StoredTransferJob::data() const 0096 { 0097 return d_func()->m_data; 0098 } 0099 0100 void StoredTransferJobPrivate::slotStoredData(KIO::Job *, const QByteArray &data) 0101 { 0102 // check for end-of-data marker: 0103 if (data.size() == 0) { 0104 return; 0105 } 0106 unsigned int oldSize = m_data.size(); 0107 m_data.resize(oldSize + data.size()); 0108 memcpy(m_data.data() + oldSize, data.data(), data.size()); 0109 } 0110 0111 void StoredTransferJobPrivate::slotStoredDataReq(KIO::Job *, QByteArray &data) 0112 { 0113 // Inspired from kmail's KMKernel::byteArrayToRemoteFile 0114 // send the data in 64 KB chunks 0115 const int MAX_CHUNK_SIZE = 64 * 1024; 0116 int remainingBytes = m_data.size() - m_uploadOffset; 0117 if (remainingBytes > MAX_CHUNK_SIZE) { 0118 // send MAX_CHUNK_SIZE bytes to the receiver (deep copy) 0119 data = QByteArray(m_data.data() + m_uploadOffset, MAX_CHUNK_SIZE); 0120 m_uploadOffset += MAX_CHUNK_SIZE; 0121 // qDebug() << "Sending " << MAX_CHUNK_SIZE << " bytes (" 0122 // << remainingBytes - MAX_CHUNK_SIZE << " bytes remain)\n"; 0123 } else { 0124 // send the remaining bytes to the receiver (deep copy) 0125 data = QByteArray(m_data.data() + m_uploadOffset, remainingBytes); 0126 m_data = QByteArray(); 0127 m_uploadOffset = 0; 0128 // qDebug() << "Sending " << remainingBytes << " bytes\n"; 0129 } 0130 } 0131 0132 StoredTransferJob *KIO::storedGet(const QUrl &url, LoadType reload, JobFlags flags) 0133 { 0134 // Send decoded path and encoded query 0135 KIO_ARGS << url; 0136 StoredTransferJob *job = StoredTransferJobPrivate::newJob(url, CMD_GET, packedArgs, QByteArray(), flags); 0137 if (reload == Reload) { 0138 job->addMetaData(QStringLiteral("cache"), QStringLiteral("reload")); 0139 } 0140 return job; 0141 } 0142 0143 StoredTransferJob *KIO::storedPut(const QByteArray &arr, const QUrl &url, int permissions, JobFlags flags) 0144 { 0145 KIO_ARGS << url << qint8((flags & Overwrite) ? 1 : 0) << qint8((flags & Resume) ? 1 : 0) << permissions; 0146 StoredTransferJob *job = StoredTransferJobPrivate::newJob(url, CMD_PUT, packedArgs, QByteArray(), flags); 0147 job->setData(arr); 0148 return job; 0149 } 0150 0151 StoredTransferJob *KIO::storedPut(QIODevice *input, const QUrl &url, int permissions, JobFlags flags) 0152 { 0153 Q_ASSERT(input && input->isReadable()); 0154 KIO_ARGS << url << qint8((flags & Overwrite) ? 1 : 0) << qint8((flags & Resume) ? 1 : 0) << permissions; 0155 StoredTransferJob *job = StoredTransferJobPrivate::newJob(url, CMD_PUT, packedArgs, input, flags); 0156 if (!input->isSequential()) { 0157 job->setTotalSize(input->size()); 0158 } 0159 return job; 0160 } 0161 0162 namespace KIO 0163 { 0164 class PostErrorJob : public StoredTransferJob 0165 { 0166 Q_OBJECT 0167 public: 0168 PostErrorJob(int _error, const QString &url, const QByteArray &packedArgs, const QByteArray &postData) 0169 : StoredTransferJob(*new StoredTransferJobPrivate(QUrl(), CMD_SPECIAL, packedArgs, postData)) 0170 { 0171 setError(_error); 0172 setErrorText(url); 0173 } 0174 0175 PostErrorJob(int _error, const QString &url, const QByteArray &packedArgs, QIODevice *ioDevice) 0176 : StoredTransferJob(*new StoredTransferJobPrivate(QUrl(), CMD_SPECIAL, packedArgs, ioDevice)) 0177 { 0178 setError(_error); 0179 setErrorText(url); 0180 } 0181 }; 0182 } 0183 0184 static int isUrlPortBad(const QUrl &url) 0185 { 0186 int _error = 0; 0187 0188 // filter out some malicious ports 0189 static const int bad_ports[] = {1, // tcpmux 0190 7, // echo 0191 9, // discard 0192 11, // systat 0193 13, // daytime 0194 15, // netstat 0195 17, // qotd 0196 19, // chargen 0197 20, // ftp-data 0198 21, // ftp-cntl 0199 22, // ssh 0200 23, // telnet 0201 25, // smtp 0202 37, // time 0203 42, // name 0204 43, // nicname 0205 53, // domain 0206 77, // priv-rjs 0207 79, // finger 0208 87, // ttylink 0209 95, // supdup 0210 101, // hostriame 0211 102, // iso-tsap 0212 103, // gppitnp 0213 104, // acr-nema 0214 109, // pop2 0215 110, // pop3 0216 111, // sunrpc 0217 113, // auth 0218 115, // sftp 0219 117, // uucp-path 0220 119, // nntp 0221 123, // NTP 0222 135, // loc-srv / epmap 0223 139, // netbios 0224 143, // imap2 0225 179, // BGP 0226 389, // ldap 0227 512, // print / exec 0228 513, // login 0229 514, // shell 0230 515, // printer 0231 526, // tempo 0232 530, // courier 0233 531, // Chat 0234 532, // netnews 0235 540, // uucp 0236 556, // remotefs 0237 587, // sendmail 0238 601, // 0239 989, // ftps data 0240 990, // ftps 0241 992, // telnets 0242 993, // imap/SSL 0243 995, // pop3/SSL 0244 1080, // SOCKS 0245 2049, // nfs 0246 4045, // lockd 0247 6000, // x11 0248 6667, // irc 0249 0}; 0250 if (url.port() != 80) { 0251 const int port = url.port(); 0252 for (int cnt = 0; bad_ports[cnt] && bad_ports[cnt] <= port; ++cnt) { 0253 if (port == bad_ports[cnt]) { 0254 _error = KIO::ERR_POST_DENIED; 0255 break; 0256 } 0257 } 0258 } 0259 0260 if (_error) { 0261 static bool override_loaded = false; 0262 static QList<int> *overriden_ports = nullptr; 0263 if (!override_loaded) { 0264 KConfig cfg(QStringLiteral("kio_httprc")); 0265 overriden_ports = new QList<int>; 0266 *overriden_ports = cfg.group(QString()).readEntry("OverriddenPorts", QList<int>()); 0267 override_loaded = true; 0268 } 0269 for (QList<int>::ConstIterator it = overriden_ports->constBegin(); it != overriden_ports->constEnd(); ++it) { 0270 if (overriden_ports->contains(url.port())) { 0271 _error = 0; 0272 } 0273 } 0274 } 0275 0276 // filter out non https? protocols 0277 if ((url.scheme() != QLatin1String("http")) && (url.scheme() != QLatin1String("https"))) { 0278 _error = KIO::ERR_POST_DENIED; 0279 } 0280 0281 if (!_error && !KUrlAuthorized::authorizeUrlAction(QStringLiteral("open"), QUrl(), url)) { 0282 _error = KIO::ERR_ACCESS_DENIED; 0283 } 0284 0285 return _error; 0286 } 0287 0288 static KIO::PostErrorJob *precheckHttpPost(const QUrl &url, QIODevice *ioDevice, JobFlags flags) 0289 { 0290 // if request is not valid, return an invalid transfer job 0291 const int _error = isUrlPortBad(url); 0292 0293 if (_error) { 0294 KIO_ARGS << (int)1 << url; 0295 PostErrorJob *job = new PostErrorJob(_error, url.toString(), packedArgs, ioDevice); 0296 job->setUiDelegate(KIO::createDefaultJobUiDelegate()); 0297 if (!(flags & HideProgressInfo)) { 0298 KIO::getJobTracker()->registerJob(job); 0299 } 0300 return job; 0301 } 0302 0303 // all is ok, return 0 0304 return nullptr; 0305 } 0306 0307 static KIO::PostErrorJob *precheckHttpPost(const QUrl &url, const QByteArray &postData, JobFlags flags) 0308 { 0309 // if request is not valid, return an invalid transfer job 0310 const int _error = isUrlPortBad(url); 0311 0312 if (_error) { 0313 KIO_ARGS << (int)1 << url; 0314 PostErrorJob *job = new PostErrorJob(_error, url.toString(), packedArgs, postData); 0315 job->setUiDelegate(KIO::createDefaultJobUiDelegate()); 0316 if (!(flags & HideProgressInfo)) { 0317 KIO::getJobTracker()->registerJob(job); 0318 } 0319 return job; 0320 } 0321 0322 // all is ok, return 0 0323 return nullptr; 0324 } 0325 0326 TransferJob *KIO::http_post(const QUrl &url, const QByteArray &postData, JobFlags flags) 0327 { 0328 bool redirection = false; 0329 QUrl _url(url); 0330 if (_url.path().isEmpty()) { 0331 redirection = true; 0332 _url.setPath(QStringLiteral("/")); 0333 } 0334 0335 TransferJob *job = precheckHttpPost(_url, postData, flags); 0336 if (job) { 0337 return job; 0338 } 0339 0340 // Send http post command (1), decoded path and encoded query 0341 KIO_ARGS << (int)1 << _url << static_cast<qint64>(postData.size()); 0342 job = TransferJobPrivate::newJob(_url, CMD_SPECIAL, packedArgs, postData, flags); 0343 0344 if (redirection) { 0345 QTimer::singleShot(0, job, [job]() { 0346 Q_EMIT job->redirection(job, job->url()); 0347 }); 0348 } 0349 0350 return job; 0351 } 0352 0353 TransferJob *KIO::http_post(const QUrl &url, QIODevice *ioDevice, qint64 size, JobFlags flags) 0354 { 0355 bool redirection = false; 0356 QUrl _url(url); 0357 if (_url.path().isEmpty()) { 0358 redirection = true; 0359 _url.setPath(QStringLiteral("/")); 0360 } 0361 0362 TransferJob *job = precheckHttpPost(_url, ioDevice, flags); 0363 if (job) { 0364 return job; 0365 } 0366 0367 // If no size is specified and the QIODevice is not a sequential one, 0368 // attempt to obtain the size information from it. 0369 Q_ASSERT(ioDevice); 0370 if (size < 0) { 0371 size = ((ioDevice && !ioDevice->isSequential()) ? ioDevice->size() : -1); 0372 } 0373 0374 // Send http post command (1), decoded path and encoded query 0375 KIO_ARGS << (int)1 << _url << size; 0376 job = TransferJobPrivate::newJob(_url, CMD_SPECIAL, packedArgs, ioDevice, flags); 0377 0378 if (redirection) { 0379 QTimer::singleShot(0, job, [job]() { 0380 Q_EMIT job->redirection(job, job->url()); 0381 }); 0382 } 0383 0384 return job; 0385 } 0386 0387 TransferJob *KIO::http_delete(const QUrl &url, JobFlags flags) 0388 { 0389 // Send decoded path and encoded query 0390 KIO_ARGS << url; 0391 TransferJob *job = TransferJobPrivate::newJob(url, CMD_DEL, packedArgs, QByteArray(), flags); 0392 return job; 0393 } 0394 0395 StoredTransferJob *KIO::storedHttpPost(const QByteArray &postData, const QUrl &url, JobFlags flags) 0396 { 0397 QUrl _url(url); 0398 if (_url.path().isEmpty()) { 0399 _url.setPath(QStringLiteral("/")); 0400 } 0401 0402 StoredTransferJob *job = precheckHttpPost(_url, postData, flags); 0403 if (job) { 0404 return job; 0405 } 0406 0407 // Send http post command (1), decoded path and encoded query 0408 KIO_ARGS << (int)1 << _url << static_cast<qint64>(postData.size()); 0409 job = StoredTransferJobPrivate::newJob(_url, CMD_SPECIAL, packedArgs, postData, flags); 0410 return job; 0411 } 0412 0413 StoredTransferJob *KIO::storedHttpPost(QIODevice *ioDevice, const QUrl &url, qint64 size, JobFlags flags) 0414 { 0415 QUrl _url(url); 0416 if (_url.path().isEmpty()) { 0417 _url.setPath(QStringLiteral("/")); 0418 } 0419 0420 StoredTransferJob *job = precheckHttpPost(_url, ioDevice, flags); 0421 if (job) { 0422 return job; 0423 } 0424 0425 // If no size is specified and the QIODevice is not a sequential one, 0426 // attempt to obtain the size information from it. 0427 Q_ASSERT(ioDevice); 0428 if (size < 0) { 0429 size = ((ioDevice && !ioDevice->isSequential()) ? ioDevice->size() : -1); 0430 } 0431 0432 // Send http post command (1), decoded path and encoded query 0433 KIO_ARGS << (int)1 << _url << size; 0434 job = StoredTransferJobPrivate::newJob(_url, CMD_SPECIAL, packedArgs, ioDevice, flags); 0435 return job; 0436 } 0437 0438 // http post got redirected from http://host to http://host/ by TransferJob 0439 // We must do this redirection ourselves because redirections by the 0440 // worker change post jobs into get jobs. 0441 void TransferJobPrivate::slotPostRedirection() 0442 { 0443 Q_Q(TransferJob); 0444 // qDebug() << m_url; 0445 // Tell the user about the new url. 0446 Q_EMIT q->redirection(q, m_url); 0447 } 0448 0449 TransferJob *KIO::put(const QUrl &url, int permissions, JobFlags flags) 0450 { 0451 KIO_ARGS << url << qint8((flags & Overwrite) ? 1 : 0) << qint8((flags & Resume) ? 1 : 0) << permissions; 0452 return TransferJobPrivate::newJob(url, CMD_PUT, packedArgs, QByteArray(), flags); 0453 } 0454 0455 #include "moc_storedtransferjob.cpp" 0456 #include "storedtransferjob.moc"