File indexing completed on 2025-01-12 12:26:26
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"