You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
292 lines
7.4 KiB
292 lines
7.4 KiB
/* SPDX-License-Identifier: LGPL-2.0-or-later |
|
*/ |
|
|
|
#include <stdio.h> |
|
#include <stdlib.h> |
|
#include <sys/stat.h> |
|
|
|
#include "kio_mits_debug.h" |
|
#include <QCoreApplication> |
|
#include <QMimeDatabase> |
|
#include <QMimeType> |
|
|
|
#include <QBitArray> |
|
#include <QDir> |
|
#include <QFile> |
|
#include <QVector> |
|
|
|
#include "libchmurlfactory.h" |
|
#include "msits.h" |
|
|
|
using namespace KIO; |
|
|
|
// Pseudo plugin class to embed meta data |
|
class KIOPluginForMetaData : public QObject |
|
{ |
|
Q_OBJECT |
|
Q_PLUGIN_METADATA(IID "org.kde.kio.slave.ms-its.json" FILE "ms-its.json") |
|
}; |
|
|
|
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<QString> *)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<QString> 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(); |
|
} |
|
|
|
#include "msits.moc"
|
|
|