File indexing completed on 2024-04-14 04:52:23

0001 /*
0002 This file is part of KDE
0003 
0004  SPDX-FileCopyrightText: 1999-2000 Waldo Bastian <bastian@kde.org>
0005  SPDX-FileCopyrightText: 2008 David Faure <faure@kde.org>
0006 
0007 SPDX-License-Identifier: MIT
0008 */
0009 
0010 #include "filter.h"
0011 
0012 #include <QCoreApplication>
0013 #include <QDebug>
0014 #include <QFile>
0015 #include <QFileInfo>
0016 #include <QMimeDatabase>
0017 #include <QMimeType>
0018 #include <QUrl>
0019 
0020 #include <KCompressionDevice>
0021 #include <KFilterBase>
0022 
0023 #include "loggingcategory.h"
0024 
0025 // Pseudo plugin class to embed meta data
0026 class KIOPluginForMetaData : public QObject
0027 {
0028     Q_OBJECT
0029     Q_PLUGIN_METADATA(IID "org.kde.kio.worker.filter" FILE "filter.json")
0030 };
0031 
0032 extern "C" {
0033 Q_DECL_EXPORT int kdemain(int argc, char **argv);
0034 }
0035 
0036 int kdemain(int argc, char **argv)
0037 {
0038     QCoreApplication app(argc, argv);
0039     app.setApplicationName("kio_filter");
0040 
0041     qDebug(KIO_FILTER_DEBUG) << "Starting";
0042 
0043     if (argc != 4) {
0044         fprintf(stderr, "Usage: kio_filter protocol domain-socket1 domain-socket2\n");
0045         exit(-1);
0046     }
0047 
0048     FilterProtocol worker(argv[1], argv[2], argv[3]);
0049     worker.dispatchLoop();
0050 
0051     qDebug(KIO_FILTER_DEBUG) << "Done";
0052     return 0;
0053 }
0054 
0055 FilterProtocol::FilterProtocol(const QByteArray &protocol, const QByteArray &pool, const QByteArray &app)
0056     : KIO::WorkerBase(protocol, pool, app)
0057     , m_protocol(QString::fromLatin1(protocol))
0058 {
0059     const QString mimetype = (protocol == "zstd") ? QStringLiteral("application/zstd") : QLatin1String("application/x-") + QLatin1String(protocol.constData());
0060 
0061     filter = KCompressionDevice::filterForCompressionType(KCompressionDevice::compressionTypeForMimeType(mimetype));
0062     Q_ASSERT(filter);
0063 }
0064 
0065 KIO::WorkerResult FilterProtocol::get(const QUrl &url)
0066 {
0067     // In the old solution, subURL would be set by setSubURL.
0068     // KDE4: now I simply assume bzip2:/localpath/file.bz2 and set subURL to the local path.
0069     QUrl subURL = url;
0070     subURL.setScheme("file");
0071 
0072     if (subURL.isEmpty()) {
0073         return KIO::WorkerResult::fail(KIO::ERR_NO_SOURCE_PROTOCOL, m_protocol);
0074     }
0075 
0076     QFile localFile(url.path());
0077     if (!localFile.open(QIODevice::ReadOnly)) {
0078         return KIO::WorkerResult::fail(KIO::ERR_CANNOT_READ, m_protocol);
0079     }
0080 
0081     if (!filter) {
0082         // TODO better error message
0083         return KIO::WorkerResult::fail(KIO::ERR_INTERNAL, m_protocol);
0084     }
0085 
0086     filter->init(QIODevice::ReadOnly);
0087 
0088     bool bNeedHeader = true;
0089     bool bNeedMimetype = true;
0090     bool bError = true;
0091     int result;
0092 
0093     QByteArray inputBuffer;
0094     inputBuffer.resize(8 * 1024);
0095     QByteArray outputBuffer;
0096     outputBuffer.resize(8 * 1024); // Start with a modest buffer
0097     filter->setOutBuffer(outputBuffer.data(), outputBuffer.size());
0098     while (true) {
0099         if (filter->inBufferEmpty()) {
0100             result = localFile.read(inputBuffer.data(), inputBuffer.size());
0101             qDebug(KIO_FILTER_DEBUG) << "requestData: got " << result;
0102             if (result <= 0) {
0103                 bError = true;
0104                 break; // Unexpected EOF.
0105             }
0106             filter->setInBuffer(inputBuffer.data(), inputBuffer.size());
0107         }
0108         if (bNeedHeader) {
0109             bError = !filter->readHeader();
0110             if (bError)
0111                 break;
0112             bNeedHeader = false;
0113         }
0114         result = filter->uncompress();
0115         if ((filter->outBufferAvailable() == 0) || (result == KFilterBase::End)) {
0116             qDebug(KIO_FILTER_DEBUG) << "avail_out = " << filter->outBufferAvailable();
0117             if (filter->outBufferAvailable() != 0) {
0118                 // Discard unused space :-)
0119                 outputBuffer.resize(outputBuffer.size() - filter->outBufferAvailable());
0120             }
0121             if (bNeedMimetype) {
0122                 // Can we use the "base" filename? E.g. foo.txt.bz2
0123                 const QString extension = QFileInfo(subURL.path()).suffix();
0124                 QMimeDatabase db;
0125                 QMimeType mime;
0126                 if (extension == "gz" || extension == "bz" || extension == "bz2" || extension == "zst") {
0127                     QString baseName = subURL.path();
0128                     baseName.truncate(baseName.length() - extension.length() - 1 /*the dot*/);
0129                     qDebug(KIO_FILTER_DEBUG) << "baseName=" << baseName;
0130                     mime = db.mimeTypeForFileNameAndData(baseName, outputBuffer);
0131                 } else {
0132                     mime = db.mimeTypeForData(outputBuffer);
0133                 }
0134                 qDebug(KIO_FILTER_DEBUG) << "Emitting mimetype " << mime.name();
0135                 mimeType(mime.name());
0136                 bNeedMimetype = false;
0137             }
0138             data(outputBuffer); // Send data
0139             filter->setOutBuffer(outputBuffer.data(), outputBuffer.size());
0140             if (result == KFilterBase::End)
0141                 break; // Finished.
0142         }
0143         if (result != KFilterBase::Ok) {
0144             bError = true;
0145             break; // Error
0146         }
0147     }
0148 
0149     if (!bError) {
0150         result = localFile.read(inputBuffer.data(), inputBuffer.size());
0151         qDebug(KIO_FILTER_DEBUG) << "requestData: got" << result << "(expecting 0)";
0152         data(QByteArray()); // Send EOF
0153     }
0154 
0155     filter->terminate();
0156 
0157     if (bError) {
0158         return KIO::WorkerResult::fail(KIO::ERR_CANNOT_READ, subURL.url());
0159     }
0160 
0161     return KIO::WorkerResult::pass();
0162 }
0163 
0164 #include "filter.moc"