/* This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include "kio_mits_debug.h" #include #include #include #include #include #include #include #include "libchmurlfactory.h" #include "msits.h" using namespace KIO; extern "C" { int Q_DECL_EXPORT kdemain(int argc, char **argv) { qCDebug(KIO_MITS_LOG) << "*** kio_msits Init"; QCoreApplication app(argc, argv); app.setApplicationName(QStringLiteral("kio_msits")); if (argc != 4) { qCDebug(KIO_MITS_LOG) << "Usage: kio_msits protocol domain-socket1 domain-socket2"; exit(-1); } ProtocolMSITS slave(argv[2], argv[3]); slave.dispatchLoop(); qCDebug(KIO_MITS_LOG) << "*** kio_msits Done"; return 0; } } ProtocolMSITS::ProtocolMSITS(const QByteArray &pool_socket, const QByteArray &app_socket) : SlaveBase("kio_msits", pool_socket, app_socket) { m_chmFile = nullptr; } ProtocolMSITS::~ProtocolMSITS() { if (!m_chmFile) return; chm_close(m_chmFile); m_chmFile = nullptr; } // A simple stat() wrapper static bool isDirectory(const QString &filename) { return filename.endsWith(QLatin1Char('/')); } void ProtocolMSITS::get(const QUrl &url) { QString htmdata, fileName; chmUnitInfo ui; QByteArray buf; qCDebug(KIO_MITS_LOG) << "kio_msits::get() " << url.path(); if (!parseLoadAndLookup(url, fileName)) return; // error() has been called by parseLoadAndLookup qCDebug(KIO_MITS_LOG) << "kio_msits::get: parseLoadAndLookup returned " << fileName; if (LCHMUrlFactory::handleFileType(url.path(), htmdata)) { buf = htmdata.toUtf8(); qCDebug(KIO_MITS_LOG) << "Using special handling for image pages: " << htmdata; } else { if (isDirectory(fileName)) { error(KIO::ERR_IS_DIRECTORY, url.toString()); return; } if (!ResolveObject(fileName, &ui)) { qCDebug(KIO_MITS_LOG) << "kio_msits::get: could not resolve filename " << fileName; error(KIO::ERR_DOES_NOT_EXIST, url.toString()); return; } buf.resize(ui.length); if (RetrieveObject(&ui, (unsigned char *)buf.data(), 0, ui.length) == 0) { qCDebug(KIO_MITS_LOG) << "kio_msits::get: could not retrieve filename " << fileName; error(KIO::ERR_NO_CONTENT, url.toString()); return; } } totalSize(buf.size()); QMimeDatabase db; QMimeType result = db.mimeTypeForFileNameAndData(fileName, buf); qCDebug(KIO_MITS_LOG) << "Emitting mimetype " << result.name(); mimeType(result.name()); data(buf); processedSize(buf.size()); finished(); } bool ProtocolMSITS::parseLoadAndLookup(const QUrl &url, QString &abspath) { qCDebug(KIO_MITS_LOG) << "ProtocolMSITS::parseLoadAndLookup (const KUrl&) " << url.path(); int pos = url.path().indexOf(QStringLiteral("::")); if (pos == -1) { error(KIO::ERR_MALFORMED_URL, url.toString()); return false; } QString filename = url.path().left(pos); abspath = url.path().mid(pos + 2); // skip :: // Some buggy apps add ms-its:/ to the path as well if (abspath.startsWith(QLatin1String("ms-its:"))) abspath = abspath.mid(7); qCDebug(KIO_MITS_LOG) << "ProtocolMSITS::parseLoadAndLookup: filename " << filename << ", path " << abspath; if (filename.isEmpty()) { error(KIO::ERR_MALFORMED_URL, url.toString()); return false; } // If the file has been already loaded, nothing to do. if (m_chmFile && filename == m_openedFile) return true; qCDebug(KIO_MITS_LOG) << "Opening a new CHM file " << QFile::encodeName(QDir::toNativeSeparators(filename)); // First try to open a temporary file chmFile *tmpchm; if ((tmpchm = chm_open(QFile::encodeName(QDir::toNativeSeparators(filename)).constData())) == nullptr) { error(KIO::ERR_COULD_NOT_READ, url.toString()); return false; } // Replace an existing file by a new one if (m_chmFile) chm_close(m_chmFile); m_chmFile = tmpchm; m_openedFile = filename; qCDebug(KIO_MITS_LOG) << "A CHM file " << filename << " has beed opened successfully"; return true; } /* * Shamelessly stolen from a KDE KIO tutorial */ static void app_entry(UDSEntry &e, unsigned int uds, const QString &str) { e.fastInsert(uds, str); } // appends an int with the UDS-ID uds static void app_entry(UDSEntry &e, unsigned int uds, long l) { e.fastInsert(uds, l); } // internal function // fills a directory item with its name and size static void app_dir(UDSEntry &e, const QString &name) { e.clear(); app_entry(e, KIO::UDSEntry::UDS_NAME, name); app_entry(e, KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR); app_entry(e, KIO::UDSEntry::UDS_SIZE, 1); } // internal function // fills a file item with its name and size static void app_file(UDSEntry &e, const QString &name, size_t size) { e.clear(); app_entry(e, KIO::UDSEntry::UDS_NAME, name); app_entry(e, KIO::UDSEntry::UDS_FILE_TYPE, S_IFREG); app_entry(e, KIO::UDSEntry::UDS_SIZE, size); } void ProtocolMSITS::stat(const QUrl &url) { QString fileName; chmUnitInfo ui; qCDebug(KIO_MITS_LOG) << "kio_msits::stat (const KUrl& url) " << url.path(); if (!parseLoadAndLookup(url, fileName)) return; // error() has been called by parseLoadAndLookup if (!ResolveObject(fileName, &ui)) { error(KIO::ERR_DOES_NOT_EXIST, url.toString()); return; } qCDebug(KIO_MITS_LOG) << "kio_msits::stat: adding an entry for " << fileName; UDSEntry entry; if (isDirectory(fileName)) app_dir(entry, fileName); else app_file(entry, fileName, ui.length); statEntry(entry); finished(); } // A local CHMLIB enumerator static int chmlib_enumerator(struct chmFile *, struct chmUnitInfo *ui, void *context) { ((QVector *)context)->push_back(QString::fromLocal8Bit(ui->path)); return CHM_ENUMERATOR_CONTINUE; } void ProtocolMSITS::listDir(const QUrl &url) { QString filepath; qCDebug(KIO_MITS_LOG) << "kio_msits::listDir (const KUrl& url) " << url.path(); if (!parseLoadAndLookup(url, filepath)) return; // error() has been called by parseLoadAndLookup filepath += QLatin1Char('/'); if (!isDirectory(filepath)) { error(KIO::ERR_CANNOT_ENTER_DIRECTORY, url.path()); return; } qCDebug(KIO_MITS_LOG) << "kio_msits::listDir: enumerating directory " << filepath; QVector listing; if (chm_enumerate_dir(m_chmFile, filepath.toLocal8Bit().constData(), CHM_ENUMERATE_NORMAL | CHM_ENUMERATE_FILES | CHM_ENUMERATE_DIRS, chmlib_enumerator, &listing) != 1) { error(KIO::ERR_CANNOT_ENTER_DIRECTORY, url.path()); return; } UDSEntry entry; int striplength = filepath.length(); for (const QString &iListing : qAsConst(listing)) { // Strip the directory name const QString ename = iListing.mid(striplength); if (isDirectory(ename)) app_dir(entry, ename); else app_file(entry, ename, 0); } finished(); }