File indexing completed on 2024-04-14 03:53:02

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"