File indexing completed on 2024-05-26 04:37:15

0001 /*  SPDX-License-Identifier: LGPL-2.0-or-later
0002  */
0003 
0004 #include <stdio.h>
0005 #include <stdlib.h>
0006 #include <sys/stat.h>
0007 
0008 #include "kio_mits_debug.h"
0009 #include <QCoreApplication>
0010 #include <QMimeDatabase>
0011 #include <QMimeType>
0012 
0013 #include <QBitArray>
0014 #include <QDir>
0015 #include <QFile>
0016 #include <QVector>
0017 
0018 #include "libchmurlfactory.h"
0019 #include "msits.h"
0020 
0021 using namespace KIO;
0022 
0023 // Pseudo plugin class to embed meta data
0024 class KIOPluginForMetaData : public QObject
0025 {
0026     Q_OBJECT
0027     Q_PLUGIN_METADATA(IID "org.kde.kio.slave.ms-its.json" FILE "ms-its.json")
0028 };
0029 
0030 extern "C" {
0031 int Q_DECL_EXPORT kdemain(int argc, char **argv)
0032 {
0033     qCDebug(KIO_MITS_LOG) << "*** kio_msits Init";
0034 
0035     QCoreApplication app(argc, argv);
0036     app.setApplicationName(QStringLiteral("kio_msits"));
0037 
0038     if (argc != 4) {
0039         qCDebug(KIO_MITS_LOG) << "Usage: kio_msits protocol domain-socket1 domain-socket2";
0040         exit(-1);
0041     }
0042 
0043     ProtocolMSITS slave(argv[2], argv[3]);
0044     slave.dispatchLoop();
0045 
0046     qCDebug(KIO_MITS_LOG) << "*** kio_msits Done";
0047     return 0;
0048 }
0049 }
0050 
0051 ProtocolMSITS::ProtocolMSITS(const QByteArray &pool_socket, const QByteArray &app_socket)
0052     : SlaveBase("kio_msits", pool_socket, app_socket)
0053 {
0054     m_chmFile = nullptr;
0055 }
0056 
0057 ProtocolMSITS::~ProtocolMSITS()
0058 {
0059     if (!m_chmFile) {
0060         return;
0061     }
0062 
0063     chm_close(m_chmFile);
0064     m_chmFile = nullptr;
0065 }
0066 
0067 // A simple stat() wrapper
0068 static bool isDirectory(const QString &filename)
0069 {
0070     return filename.endsWith(QLatin1Char('/'));
0071 }
0072 
0073 void ProtocolMSITS::get(const QUrl &url)
0074 {
0075     QString htmdata, fileName;
0076     chmUnitInfo ui;
0077     QByteArray buf;
0078 
0079     qCDebug(KIO_MITS_LOG) << "kio_msits::get() " << url.path();
0080 
0081     if (!parseLoadAndLookup(url, fileName)) {
0082         return; // error() has been called by parseLoadAndLookup
0083     }
0084 
0085     qCDebug(KIO_MITS_LOG) << "kio_msits::get: parseLoadAndLookup returned " << fileName;
0086 
0087     if (LCHMUrlFactory::handleFileType(url.path(), htmdata)) {
0088         buf = htmdata.toUtf8();
0089         qCDebug(KIO_MITS_LOG) << "Using special handling for image pages: " << htmdata;
0090     } else {
0091         if (isDirectory(fileName)) {
0092             error(KIO::ERR_IS_DIRECTORY, url.toString());
0093             return;
0094         }
0095 
0096         if (!ResolveObject(fileName, &ui)) {
0097             qCDebug(KIO_MITS_LOG) << "kio_msits::get: could not resolve filename " << fileName;
0098             error(KIO::ERR_DOES_NOT_EXIST, url.toString());
0099             return;
0100         }
0101 
0102         buf.resize(ui.length);
0103 
0104         if (RetrieveObject(&ui, (unsigned char *)buf.data(), 0, ui.length) == 0) {
0105             qCDebug(KIO_MITS_LOG) << "kio_msits::get: could not retrieve filename " << fileName;
0106             error(KIO::ERR_NO_CONTENT, url.toString());
0107             return;
0108         }
0109     }
0110 
0111     totalSize(buf.size());
0112 
0113     QMimeDatabase db;
0114     QMimeType result = db.mimeTypeForFileNameAndData(fileName, buf);
0115     qCDebug(KIO_MITS_LOG) << "Emitting mimetype " << result.name();
0116 
0117     mimeType(result.name());
0118 
0119     data(buf);
0120     processedSize(buf.size());
0121 
0122     finished();
0123 }
0124 
0125 bool ProtocolMSITS::parseLoadAndLookup(const QUrl &url, QString &abspath)
0126 {
0127     qCDebug(KIO_MITS_LOG) << "ProtocolMSITS::parseLoadAndLookup (const KUrl&) " << url.path();
0128 
0129     int pos = url.path().indexOf(QStringLiteral("::"));
0130 
0131     if (pos == -1) {
0132         error(KIO::ERR_MALFORMED_URL, url.toString());
0133         return false;
0134     }
0135 
0136     QString filename = url.path().left(pos);
0137     abspath = url.path().mid(pos + 2); // skip ::
0138 
0139     // Some buggy apps add ms-its:/ to the path as well
0140     if (abspath.startsWith(QLatin1String("ms-its:"))) {
0141         abspath = abspath.mid(7);
0142     }
0143 
0144     qCDebug(KIO_MITS_LOG) << "ProtocolMSITS::parseLoadAndLookup: filename " << filename << ", path " << abspath;
0145 
0146     if (filename.isEmpty()) {
0147         error(KIO::ERR_MALFORMED_URL, url.toString());
0148         return false;
0149     }
0150 
0151     // If the file has been already loaded, nothing to do.
0152     if (m_chmFile && filename == m_openedFile) {
0153         return true;
0154     }
0155 
0156     qCDebug(KIO_MITS_LOG) << "Opening a new CHM file " << QFile::encodeName(QDir::toNativeSeparators(filename));
0157 
0158     // First try to open a temporary file
0159     chmFile *tmpchm;
0160 
0161     if ((tmpchm = chm_open(QFile::encodeName(QDir::toNativeSeparators(filename)).constData())) == nullptr) {
0162         error(KIO::ERR_CANNOT_READ, url.toString());
0163         return false;
0164     }
0165 
0166     // Replace an existing file by a new one
0167     if (m_chmFile) {
0168         chm_close(m_chmFile);
0169     }
0170 
0171     m_chmFile = tmpchm;
0172     m_openedFile = filename;
0173 
0174     qCDebug(KIO_MITS_LOG) << "A CHM file " << filename << " has beed opened successfully";
0175     return true;
0176 }
0177 
0178 /*
0179  * Shamelessly stolen from a KDE KIO tutorial
0180  */
0181 static void app_entry(UDSEntry &e, unsigned int uds, const QString &str)
0182 {
0183     e.fastInsert(uds, str);
0184 }
0185 
0186 // appends an int with the UDS-ID uds
0187 static void app_entry(UDSEntry &e, unsigned int uds, long l)
0188 {
0189     e.fastInsert(uds, l);
0190 }
0191 
0192 // internal function
0193 // fills a directory item with its name and size
0194 static void app_dir(UDSEntry &e, const QString &name)
0195 {
0196     e.clear();
0197     app_entry(e, KIO::UDSEntry::UDS_NAME, name);
0198     app_entry(e, KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR);
0199     app_entry(e, KIO::UDSEntry::UDS_SIZE, 1);
0200 }
0201 
0202 // internal function
0203 // fills a file item with its name and size
0204 static void app_file(UDSEntry &e, const QString &name, size_t size)
0205 {
0206     e.clear();
0207     app_entry(e, KIO::UDSEntry::UDS_NAME, name);
0208     app_entry(e, KIO::UDSEntry::UDS_FILE_TYPE, S_IFREG);
0209     app_entry(e, KIO::UDSEntry::UDS_SIZE, size);
0210 }
0211 
0212 void ProtocolMSITS::stat(const QUrl &url)
0213 {
0214     QString fileName;
0215     chmUnitInfo ui;
0216 
0217     qCDebug(KIO_MITS_LOG) << "kio_msits::stat (const KUrl& url) " << url.path();
0218 
0219     if (!parseLoadAndLookup(url, fileName)) {
0220         return; // error() has been called by parseLoadAndLookup
0221     }
0222 
0223     if (!ResolveObject(fileName, &ui)) {
0224         error(KIO::ERR_DOES_NOT_EXIST, url.toString());
0225         return;
0226     }
0227 
0228     qCDebug(KIO_MITS_LOG) << "kio_msits::stat: adding an entry for " << fileName;
0229     UDSEntry entry;
0230 
0231     if (isDirectory(fileName)) {
0232         app_dir(entry, fileName);
0233     } else {
0234         app_file(entry, fileName, ui.length);
0235     }
0236 
0237     statEntry(entry);
0238 
0239     finished();
0240 }
0241 
0242 // A local CHMLIB enumerator
0243 static int chmlib_enumerator(struct chmFile *, struct chmUnitInfo *ui, void *context)
0244 {
0245     ((QVector<QString> *)context)->push_back(QString::fromLocal8Bit(ui->path));
0246     return CHM_ENUMERATOR_CONTINUE;
0247 }
0248 
0249 void ProtocolMSITS::listDir(const QUrl &url)
0250 {
0251     QString filepath;
0252 
0253     qCDebug(KIO_MITS_LOG) << "kio_msits::listDir (const KUrl& url) " << url.path();
0254 
0255     if (!parseLoadAndLookup(url, filepath)) {
0256         return; // error() has been called by parseLoadAndLookup
0257     }
0258 
0259     filepath += QLatin1Char('/');
0260 
0261     if (!isDirectory(filepath)) {
0262         error(KIO::ERR_CANNOT_ENTER_DIRECTORY, url.path());
0263         return;
0264     }
0265 
0266     qCDebug(KIO_MITS_LOG) << "kio_msits::listDir: enumerating directory " << filepath;
0267 
0268     QVector<QString> listing;
0269 
0270     if (chm_enumerate_dir(m_chmFile, filepath.toLocal8Bit().constData(), CHM_ENUMERATE_NORMAL | CHM_ENUMERATE_FILES | CHM_ENUMERATE_DIRS, chmlib_enumerator, &listing) != 1) {
0271         error(KIO::ERR_CANNOT_ENTER_DIRECTORY, url.path());
0272         return;
0273     }
0274 
0275     UDSEntry entry;
0276     int striplength = filepath.length();
0277 
0278     for (const QString &iListing : std::as_const(listing)) {
0279         // Strip the directory name
0280         const QString ename = iListing.mid(striplength);
0281 
0282         if (isDirectory(ename)) {
0283             app_dir(entry, ename);
0284         } else {
0285             app_file(entry, ename, 0);
0286         }
0287     }
0288 
0289     finished();
0290 }
0291 
0292 #include "msits.moc"