File indexing completed on 2024-05-05 16:08:29

0001 /*
0002     This file is part of the KDE libraries
0003     Copyright (C) 1997 Torben Weis (weis@kde.org)
0004     Copyright (C) 1998 Matthias Ettrich (ettrich@kde.org)
0005     Copyright (C) 1999 David Faure (faure@kde.org)
0006 
0007     This library is free software; you can redistribute it and/or
0008     modify it under the terms of the GNU Library General Public
0009     License as published by the Free Software Foundation; either
0010     version 2 of the License, or (at your option) any later version.
0011 
0012     This library is distributed in the hope that it will be useful,
0013     but WITHOUT ANY WARRANTY; without even the implied warranty of
0014     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0015     Library General Public License for more details.
0016 
0017     You should have received a copy of the GNU Library General Public License
0018     along with this library; see the file COPYING.LIB.  If not, write to
0019     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0020     Boston, MA 02110-1301, USA.
0021 */
0022 
0023 #include "netaccess.h"
0024 
0025 #include <stdlib.h>
0026 #include <stdio.h>
0027 #include <signal.h>
0028 #include <unistd.h>
0029 
0030 #include <cstring>
0031 
0032 #include <QCharRef>
0033 #include <QApplication>
0034 #include <QFile>
0035 #include <QFileInfo>
0036 #include <QMetaClassInfo>
0037 #include <QTextStream>
0038 #include <QDataStream>
0039 #include <qtemporaryfile.h>
0040 
0041 #include <klocalizedstring.h>
0042 #include <kjobwidgets.h>
0043 
0044 #include "kio/job.h"
0045 #include "kio/mkdirjob.h"
0046 #include "kio/copyjob.h"
0047 #include "kio/deletejob.h"
0048 
0049 namespace KIO
0050 {
0051 class NetAccessPrivate
0052 {
0053 public:
0054     NetAccessPrivate()
0055         : m_metaData(nullptr)
0056         , bJobOK(true)
0057     {}
0058     UDSEntry m_entry;
0059     QString m_mimetype;
0060     QByteArray m_data;
0061     QUrl m_url;
0062     QMap<QString, QString> *m_metaData;
0063 
0064     /**
0065      * Whether the download succeeded or not
0066      */
0067     bool bJobOK;
0068 };
0069 
0070 } // namespace KIO
0071 
0072 using namespace KIO;
0073 
0074 /**
0075  * List of temporary files
0076  */
0077 static QStringList *tmpfiles;
0078 
0079 static QString *lastErrorMsg = nullptr;
0080 static int lastErrorCode = 0;
0081 
0082 NetAccess::NetAccess() :
0083     d(new NetAccessPrivate)
0084 {
0085 }
0086 
0087 NetAccess::~NetAccess()
0088 {
0089     delete d;
0090 }
0091 
0092 bool NetAccess::download(const QUrl &u, QString &target, QWidget *window)
0093 {
0094     if (u.isLocalFile()) {
0095         // file protocol. We do not need the network
0096         target = u.toLocalFile();
0097         const bool readable = QFileInfo(target).isReadable();
0098         if (!readable) {
0099             if (!lastErrorMsg) {
0100                 lastErrorMsg = new QString;
0101             }
0102             *lastErrorMsg = i18n("File '%1' is not readable", target);
0103             lastErrorCode = ERR_COULD_NOT_READ;
0104         }
0105         return readable;
0106     }
0107 
0108     if (target.isEmpty()) {
0109         QTemporaryFile tmpFile;
0110         tmpFile.setAutoRemove(false);
0111         tmpFile.open();
0112         target = tmpFile.fileName();
0113         if (!tmpfiles) {
0114             tmpfiles = new QStringList;
0115         }
0116         tmpfiles->append(target);
0117     }
0118 
0119     NetAccess kioNet;
0120     const QUrl dest = QUrl::fromLocalFile(target);
0121     return kioNet.filecopyInternal(u, dest, -1, KIO::Overwrite, window, false /*copy*/);
0122 }
0123 
0124 bool NetAccess::upload(const QString &src, const QUrl &target, QWidget *window)
0125 {
0126     if (target.isEmpty()) {
0127         return false;
0128     }
0129 
0130     // If target is local... well, just copy. This can be useful
0131     // when the client code uses a temp file no matter what.
0132     // Let's make sure it's not the exact same file though
0133     if (target.isLocalFile() && target.toLocalFile() == src) {
0134         return true;
0135     }
0136 
0137     NetAccess kioNet;
0138     const QUrl srcUrl = QUrl::fromLocalFile(src);
0139     return kioNet.filecopyInternal(srcUrl, target, -1, KIO::Overwrite, window, false /*copy*/);
0140 }
0141 
0142 #ifndef KDELIBS4SUPPORT_NO_DEPRECATED
0143 bool NetAccess::file_copy(const QUrl &src, const QUrl &target, QWidget *window)
0144 {
0145     NetAccess kioNet;
0146     return kioNet.filecopyInternal(src, target, -1, KIO::DefaultFlags,
0147                                    window, false /*copy*/);
0148 }
0149 #endif
0150 
0151 #ifndef KDELIBS4SUPPORT_NO_DEPRECATED
0152 bool NetAccess::copy(const QUrl &src, const QUrl &target, QWidget *window)
0153 {
0154     return file_copy(src, target, window);
0155 }
0156 #endif
0157 
0158 #ifndef KDELIBS4SUPPORT_NO_DEPRECATED
0159 bool NetAccess::dircopy(const QUrl &src, const QUrl &target, QWidget *window)
0160 {
0161     QList<QUrl> srcList;
0162     srcList.append(src);
0163     return NetAccess::dircopy(srcList, target, window);
0164 }
0165 #endif
0166 
0167 #ifndef KDELIBS4SUPPORT_NO_DEPRECATED
0168 bool NetAccess::dircopy(const QList<QUrl> &srcList, const QUrl &target, QWidget *window)
0169 {
0170     NetAccess kioNet;
0171     return kioNet.dircopyInternal(srcList, target, window, false /*copy*/);
0172 }
0173 #endif
0174 
0175 #ifndef KDELIBS4SUPPORT_NO_DEPRECATED
0176 bool NetAccess::move(const QUrl &src, const QUrl &target, QWidget *window)
0177 {
0178     QList<QUrl> srcList;
0179     srcList.append(src);
0180     NetAccess kioNet;
0181     return kioNet.dircopyInternal(srcList, target, window, true /*move*/);
0182 }
0183 #endif
0184 
0185 #ifndef KDELIBS4SUPPORT_NO_DEPRECATED
0186 bool NetAccess::move(const QList<QUrl> &srcList, const QUrl &target, QWidget *window)
0187 {
0188     NetAccess kioNet;
0189     return kioNet.dircopyInternal(srcList, target, window, true /*move*/);
0190 }
0191 #endif
0192 
0193 #ifndef KDELIBS4SUPPORT_NO_DEPRECATED
0194 bool NetAccess::exists(const QUrl &url, bool source, QWidget *window)
0195 {
0196     if (url.isLocalFile()) {
0197         return QFile::exists(url.toLocalFile());
0198     }
0199     NetAccess kioNet;
0200     return kioNet.statInternal(url, 0 /*no details*/,
0201                                source ? SourceSide : DestinationSide, window);
0202 }
0203 #endif
0204 
0205 bool NetAccess::exists(const QUrl &url, StatSide side, QWidget *window)
0206 {
0207     if (url.isLocalFile()) {
0208         return QFile::exists(url.toLocalFile());
0209     }
0210     NetAccess kioNet;
0211     return kioNet.statInternal(url, 0 /*no details*/, side, window);
0212 }
0213 
0214 bool NetAccess::stat(const QUrl &url, KIO::UDSEntry &entry, QWidget *window)
0215 {
0216     NetAccess kioNet;
0217     bool ret = kioNet.statInternal(url, 2 /*all details*/, SourceSide, window);
0218     if (ret) {
0219         entry = kioNet.d->m_entry;
0220     }
0221     return ret;
0222 }
0223 
0224 QUrl NetAccess::mostLocalUrl(const QUrl &url, QWidget *window)
0225 {
0226     if (url.isLocalFile()) {
0227         return url;
0228     }
0229 
0230     KIO::UDSEntry entry;
0231     if (!stat(url, entry, window)) {
0232         return url;
0233     }
0234 
0235     const QString path = entry.stringValue(KIO::UDSEntry::UDS_LOCAL_PATH);
0236     if (!path.isEmpty()) {
0237         QUrl new_url = QUrl::fromLocalFile(path);
0238         return new_url;
0239     }
0240 
0241     return url;
0242 }
0243 
0244 #ifndef KDELIBS4SUPPORT_NO_DEPRECATED
0245 bool NetAccess::del(const QUrl &url, QWidget *window)
0246 {
0247     NetAccess kioNet;
0248     return kioNet.delInternal(url, window);
0249 }
0250 #endif
0251 
0252 #ifndef KDELIBS4SUPPORT_NO_DEPRECATED
0253 bool NetAccess::mkdir(const QUrl &url, QWidget *window, int permissions)
0254 {
0255     NetAccess kioNet;
0256     return kioNet.mkdirInternal(url, permissions, window);
0257 }
0258 #endif
0259 
0260 QString NetAccess::fish_execute(const QUrl &url, const QString &command, QWidget *window)
0261 {
0262     NetAccess kioNet;
0263     return kioNet.fish_executeInternal(url, command, window);
0264 }
0265 
0266 bool NetAccess::synchronousRun(Job *job, QWidget *window, QByteArray *data,
0267                                QUrl *finalURL, QMap<QString, QString> *metaData)
0268 {
0269     NetAccess kioNet;
0270     // Disable autodeletion until we are back from this event loop (#170963)
0271     // We just have to hope people don't mess with setAutoDelete in slots connected to the job, though.
0272     const bool wasAutoDelete = job->isAutoDelete();
0273     job->setAutoDelete(false);
0274     const bool ok = kioNet.synchronousRunInternal(job, window, data, finalURL, metaData);
0275     if (wasAutoDelete) {
0276         job->deleteLater();
0277     }
0278     return ok;
0279 }
0280 
0281 QString NetAccess::mimetype(const QUrl &url, QWidget *window)
0282 {
0283     NetAccess kioNet;
0284     return kioNet.mimetypeInternal(url, window);
0285 }
0286 
0287 QString NetAccess::lastErrorString()
0288 {
0289     return lastErrorMsg ? *lastErrorMsg : QString();
0290 }
0291 
0292 int NetAccess::lastError()
0293 {
0294     return lastErrorCode;
0295 }
0296 
0297 void NetAccess::removeTempFile(const QString &name)
0298 {
0299     if (!tmpfiles) {
0300         return;
0301     }
0302     if (tmpfiles->contains(name)) {
0303         QFile::remove(name);
0304         tmpfiles->removeAll(name);
0305     }
0306 }
0307 
0308 bool NetAccess::filecopyInternal(const QUrl &src, const QUrl &target, int permissions,
0309                                  KIO::JobFlags flags, QWidget *window, bool move)
0310 {
0311     d->bJobOK = true; // success unless further error occurs
0312 
0313     KIO::Job *job = move
0314                     ? KIO::file_move(src, target, permissions, flags)
0315                     : KIO::file_copy(src, target, permissions, flags);
0316     KJobWidgets::setWindow(job, window);
0317     connect(job, SIGNAL(result(KJob*)),
0318             this, SLOT(slotResult(KJob*)));
0319 
0320     enter_loop();
0321     return d->bJobOK;
0322 }
0323 
0324 bool NetAccess::dircopyInternal(const QList<QUrl> &src, const QUrl &target,
0325                                 QWidget *window, bool move)
0326 {
0327     d->bJobOK = true; // success unless further error occurs
0328 
0329     KIO::Job *job = move
0330                     ? KIO::move(src, target)
0331                     : KIO::copy(src, target);
0332     KJobWidgets::setWindow(job, window);
0333     connect(job, SIGNAL(result(KJob*)),
0334             this, SLOT(slotResult(KJob*)));
0335 
0336     enter_loop();
0337     return d->bJobOK;
0338 }
0339 
0340 bool NetAccess::statInternal(const QUrl &url, int details, StatSide side,
0341                              QWidget *window)
0342 {
0343     d->bJobOK = true; // success unless further error occurs
0344     KIO::JobFlags flags = url.isLocalFile() ? KIO::HideProgressInfo : KIO::DefaultFlags;
0345     KIO::StatJob *job = KIO::stat(url, flags);
0346     KJobWidgets::setWindow(job, window);
0347     job->setDetails(details);
0348     job->setSide(side == SourceSide ? StatJob::SourceSide : StatJob::DestinationSide);
0349     connect(job, SIGNAL(result(KJob*)),
0350             this, SLOT(slotResult(KJob*)));
0351     enter_loop();
0352     return d->bJobOK;
0353 }
0354 
0355 bool NetAccess::delInternal(const QUrl &url, QWidget *window)
0356 {
0357     d->bJobOK = true; // success unless further error occurs
0358     KIO::Job *job = KIO::del(url);
0359     KJobWidgets::setWindow(job, window);
0360     connect(job, SIGNAL(result(KJob*)),
0361             this, SLOT(slotResult(KJob*)));
0362     enter_loop();
0363     return d->bJobOK;
0364 }
0365 
0366 bool NetAccess::mkdirInternal(const QUrl &url, int permissions,
0367                               QWidget *window)
0368 {
0369     d->bJobOK = true; // success unless further error occurs
0370     KIO::Job *job = KIO::mkdir(url, permissions);
0371     KJobWidgets::setWindow(job, window);
0372     connect(job, SIGNAL(result(KJob*)),
0373             this, SLOT(slotResult(KJob*)));
0374     enter_loop();
0375     return d->bJobOK;
0376 }
0377 
0378 QString NetAccess::mimetypeInternal(const QUrl &url, QWidget *window)
0379 {
0380     d->bJobOK = true; // success unless further error occurs
0381     d->m_mimetype = QLatin1String("unknown");
0382     KIO::Job *job = KIO::mimetype(url);
0383     KJobWidgets::setWindow(job, window);
0384     connect(job, SIGNAL(result(KJob*)),
0385             this, SLOT(slotResult(KJob*)));
0386     connect(job, SIGNAL(mimetype(KIO::Job*,QString)),
0387             this, SLOT(slotMimetype(KIO::Job*,QString)));
0388     enter_loop();
0389     return d->m_mimetype;
0390 }
0391 
0392 void NetAccess::slotMimetype(KIO::Job *, const QString &type)
0393 {
0394     d->m_mimetype = type;
0395 }
0396 
0397 QString NetAccess::fish_executeInternal(const QUrl &url, const QString &command, QWidget *window)
0398 {
0399     QString target, remoteTempFileName, resultData;
0400     QTemporaryFile tmpFile;
0401     tmpFile.open();
0402 
0403     if (url.scheme() == "fish") {
0404         // construct remote temp filename
0405         QUrl tempPathUrl = url;
0406         remoteTempFileName = tmpFile.fileName();
0407         // We only need the filename. The directory might not exist on the remote side.
0408         int pos = remoteTempFileName.lastIndexOf('/');
0409         remoteTempFileName = "/tmp/fishexec_" + remoteTempFileName.mid(pos + 1);
0410         tempPathUrl.setPath(remoteTempFileName);
0411         d->bJobOK = true; // success unless further error occurs
0412         QByteArray packedArgs;
0413         QDataStream stream(&packedArgs, QIODevice::WriteOnly);
0414 
0415         stream << int('X') << tempPathUrl << command;
0416 
0417         KIO::Job *job = KIO::special(tempPathUrl, packedArgs);
0418         KJobWidgets::setWindow(job, window);
0419         connect(job, SIGNAL(result(KJob*)),
0420                 this, SLOT(slotResult(KJob*)));
0421         enter_loop();
0422 
0423         // since the KIO::special does not provide feedback we need to download the result
0424         if (NetAccess::download(tempPathUrl, target, window)) {
0425             QFile resultFile(target);
0426 
0427             if (resultFile.open(QIODevice::ReadOnly)) {
0428                 QTextStream ts(&resultFile);   // default encoding is Locale
0429                 resultData = ts.readAll();
0430                 resultFile.close();
0431                 NetAccess::del(tempPathUrl, window);
0432             }
0433         }
0434     } else {
0435         resultData = i18n("ERROR: Unknown protocol '%1'", url.scheme());
0436     }
0437     return resultData;
0438 }
0439 
0440 bool NetAccess::synchronousRunInternal(Job *job, QWidget *window, QByteArray *data,
0441                                        QUrl *finalURL, QMap<QString, QString> *metaData)
0442 {
0443     KJobWidgets::setWindow(job, window);
0444 
0445     d->m_metaData = metaData;
0446     if (d->m_metaData) {
0447         for (QMap<QString, QString>::iterator it = d->m_metaData->begin(); it != d->m_metaData->end(); ++it) {
0448             job->addMetaData(it.key(), it.value());
0449         }
0450     }
0451 
0452     if (finalURL) {
0453         SimpleJob *sj = qobject_cast<SimpleJob *>(job);
0454         if (sj) {
0455             d->m_url = sj->url();
0456         }
0457     }
0458 
0459     connect(job, SIGNAL(result(KJob*)),
0460             this, SLOT(slotResult(KJob*)));
0461 
0462     const QMetaObject *meta = job->metaObject();
0463 
0464     static const char dataSignal[] = "data(KIO::Job*,QByteArray)";
0465     if (meta->indexOfSignal(dataSignal) != -1) {
0466         connect(job, SIGNAL(data(KIO::Job*,QByteArray)),
0467                 this, SLOT(slotData(KIO::Job*,QByteArray)));
0468     }
0469 
0470     static const char redirSignal[] = "redirection(KIO::Job*,QUrl)";
0471     if (meta->indexOfSignal(redirSignal) != -1) {
0472         connect(job, SIGNAL(redirection(KIO::Job*,QUrl)),
0473                 this, SLOT(slotRedirection(KIO::Job*,QUrl)));
0474     }
0475 
0476     enter_loop();
0477 
0478     if (finalURL) {
0479         *finalURL = d->m_url;
0480     }
0481     if (data) {
0482         *data = d->m_data;
0483     }
0484 
0485     return d->bJobOK;
0486 }
0487 
0488 void NetAccess::enter_loop()
0489 {
0490     QEventLoop eventLoop;
0491     connect(this, SIGNAL(leaveModality()),
0492             &eventLoop, SLOT(quit()));
0493     eventLoop.exec(QEventLoop::ExcludeUserInputEvents);
0494 }
0495 
0496 void NetAccess::slotResult(KJob *job)
0497 {
0498     lastErrorCode = job->error();
0499     d->bJobOK = !job->error();
0500     if (!d->bJobOK) {
0501         if (!lastErrorMsg) {
0502             lastErrorMsg = new QString;
0503         }
0504         *lastErrorMsg = job->errorString();
0505     }
0506     KIO::StatJob *statJob = qobject_cast<KIO::StatJob *>(job);
0507     if (statJob) {
0508         d->m_entry = statJob->statResult();
0509     }
0510 
0511     KIO::Job *kioJob = qobject_cast<KIO::Job *>(job);
0512     if (kioJob && d->m_metaData) {
0513         *d->m_metaData = kioJob->metaData();
0514     }
0515 
0516     emit leaveModality();
0517 }
0518 
0519 void NetAccess::slotData(KIO::Job *, const QByteArray &data)
0520 {
0521     if (data.isEmpty()) {
0522         return;
0523     }
0524 
0525     unsigned offset = d->m_data.size();
0526     d->m_data.resize(offset + data.size());
0527     std::memcpy(d->m_data.data() + offset, data.data(), data.size());
0528 }
0529 
0530 void NetAccess::slotRedirection(KIO::Job *, const QUrl &url)
0531 {
0532     d->m_url = url;
0533 }
0534 
0535 #include "moc_netaccess.cpp"