This repository has been archived on 2026-08-16. You can view files and clone it, but cannot push or open issues or pull requests.
ollie-kde/kio/ollie_kio.cpp

340 lines
10 KiB
C++

#include "ollie_kio.h"
#include <QCoreApplication>
#include <QUrl>
#include <QDateTime>
#include <QMimeDatabase>
#include <QMimeType>
#include <KIO/UDSEntry>
#include <KIO/WorkerFactory>
using namespace KIO;
// Plugin factory — KIO loads this .so and calls the appropriate create method
#if QT_VERSION_MAJOR >= 6
class OllieWorkerFactory : public KIO::WorkerFactory
{
Q_OBJECT
Q_PLUGIN_METADATA(IID "org.kde.kio.worker.ollie" FILE "ollie.json")
public:
using WorkerFactory::WorkerFactory;
std::unique_ptr<WorkerBase> createWorker(const QByteArray &pool, const QByteArray &app) override
{
return std::make_unique<OllieWorker>(pool, app);
}
};
#else
class OllieWorkerFactory : public KIO::RealWorkerFactory
{
Q_OBJECT
Q_PLUGIN_METADATA(IID "org.kde.kio.worker.ollie" FILE "ollie.json")
public:
using RealWorkerFactory::RealWorkerFactory;
std::unique_ptr<SlaveBase> createWorker(const QByteArray &pool, const QByteArray &app) override
{
Q_UNUSED(pool); Q_UNUSED(app);
return nullptr; // unused — KIO calls createRealWorker for RealWorkerFactory
}
std::unique_ptr<WorkerBase> createRealWorker(const QByteArray &pool, const QByteArray &app) override
{
return std::make_unique<OllieWorker>(pool, app);
}
};
#endif
// kdemain entry point — KF5 KIO launches workers by calling this
extern "C" Q_DECL_EXPORT int kdemain(int argc, char **argv)
{
QCoreApplication app(argc, argv);
app.setApplicationName(QStringLiteral("kio_ollie"));
if (argc != 4) {
fprintf(stderr, "Usage: kio_ollie protocol pool_socket app_socket\n");
return -1;
}
OllieWorker worker(argv[2], argv[3]);
worker.dispatchLoop();
return 0;
}
OllieWorker::OllieWorker(const QByteArray &pool, const QByteArray &app)
: WorkerBase("ollie", pool, app)
{
}
OllieWorker::~OllieWorker()
{
m_client.disconnect();
}
bool OllieWorker::ensureConnected()
{
if (m_client.isConnected())
return true;
return m_client.connectDefault();
}
QString OllieWorker::ninepPath(const QUrl &url) const
{
QString path = url.path();
// Remove leading slash — 9P root is ""
if (path.startsWith(QLatin1Char('/')))
path = path.mid(1);
// Remove trailing slash
if (path.endsWith(QLatin1Char('/')) && path.length() > 1)
path.chop(1);
return path;
}
bool OllieWorker::isDirectory(const QString &mode) const
{
// 9P mode: starts with 'd' for directory,
// or numeric with Plan 9 DMDIR bit (0x80000000)
if (mode.startsWith(QLatin1Char('d')))
return true;
bool ok;
uint modeVal = mode.toUInt(&ok, 0);
if (ok && (modeVal & 0x80000000))
return true;
return false;
}
mode_t OllieWorker::parseMode(const QString &modeStr) const
{
// Try numeric first
bool ok;
uint modeVal = modeStr.toUInt(&ok, 0);
if (ok) {
return static_cast<mode_t>(modeVal & 0777);
}
// Parse rwx string (e.g. "drwxr-xr-x")
QString perms = modeStr;
if (perms.startsWith(QLatin1Char('d')))
perms = perms.mid(1);
mode_t m = 0;
if (perms.length() >= 9) {
if (perms[0] == QLatin1Char('r')) m |= 0400;
if (perms[1] == QLatin1Char('w')) m |= 0200;
if (perms[2] == QLatin1Char('x')) m |= 0100;
if (perms[3] == QLatin1Char('r')) m |= 0040;
if (perms[4] == QLatin1Char('w')) m |= 0020;
if (perms[5] == QLatin1Char('x')) m |= 0010;
if (perms[6] == QLatin1Char('r')) m |= 0004;
if (perms[7] == QLatin1Char('w')) m |= 0002;
if (perms[8] == QLatin1Char('x')) m |= 0001;
} else {
m = 0644;
}
return m;
}
KIO::UDSEntry OllieWorker::entryFromStat(const QString &path, const QString &name)
{
UDSEntry entry;
auto st = m_client.stat(path);
if (st.valid) {
bool dir = isDirectory(st.mode);
entry.fastInsert(UDSEntry::UDS_NAME, name.isEmpty() ? st.name : name);
entry.fastInsert(UDSEntry::UDS_SIZE, dir ? 0 : st.size);
entry.fastInsert(UDSEntry::UDS_MODIFICATION_TIME, st.mtime);
entry.fastInsert(UDSEntry::UDS_ACCESS_TIME, st.mtime);
mode_t perm = parseMode(st.mode);
if (dir) {
entry.fastInsert(UDSEntry::UDS_FILE_TYPE, S_IFDIR);
entry.fastInsert(UDSEntry::UDS_ACCESS, perm | 0111);
} else {
entry.fastInsert(UDSEntry::UDS_FILE_TYPE, S_IFREG);
entry.fastInsert(UDSEntry::UDS_ACCESS, perm);
}
if (!dir) {
QMimeDatabase db;
QMimeType mime = db.mimeTypeForFile(name.isEmpty() ? st.name : name,
QMimeDatabase::MatchExtension);
if (mime.isValid() && !mime.isDefault()) {
entry.fastInsert(UDSEntry::UDS_MIME_TYPE, mime.name());
} else {
entry.fastInsert(UDSEntry::UDS_MIME_TYPE,
QStringLiteral("text/plain"));
}
}
} else {
entry.fastInsert(UDSEntry::UDS_NAME, name);
}
return entry;
}
WorkerResult OllieWorker::listDir(const QUrl &url)
{
if (!ensureConnected()) {
return WorkerResult::fail(ERR_CANNOT_CONNECT,
QStringLiteral("Cannot connect to Ollie 9P server: ")
+ m_client.lastError());
}
QString path = ninepPath(url);
QStringList entries = m_client.ls(path);
if (entries.isEmpty() && !m_client.lastError().isEmpty()) {
return WorkerResult::fail(ERR_CANNOT_ENTER_DIRECTORY,
m_client.lastError());
}
// Emit "." entry for the directory itself
UDSEntry dot;
dot.fastInsert(UDSEntry::UDS_NAME, QStringLiteral("."));
dot.fastInsert(UDSEntry::UDS_FILE_TYPE, S_IFDIR);
dot.fastInsert(UDSEntry::UDS_ACCESS, 0755);
listEntry(dot);
for (const QString &name : entries) {
QString childPath = path.isEmpty() ? name : (path + QLatin1Char('/') + name);
UDSEntry entry = entryFromStat(childPath, name);
listEntry(entry);
}
return WorkerResult::pass();
}
WorkerResult OllieWorker::stat(const QUrl &url)
{
if (!ensureConnected()) {
return WorkerResult::fail(ERR_CANNOT_CONNECT,
QStringLiteral("Cannot connect to Ollie 9P server: ")
+ m_client.lastError());
}
QString path = ninepPath(url);
QString name = url.fileName();
if (name.isEmpty())
name = QStringLiteral(".");
UDSEntry entry = entryFromStat(path, name);
if (!entry.contains(UDSEntry::UDS_FILE_TYPE)) {
if (path.isEmpty()) {
// Root directory
entry.fastInsert(UDSEntry::UDS_NAME, QStringLiteral("."));
entry.fastInsert(UDSEntry::UDS_FILE_TYPE, S_IFDIR);
entry.fastInsert(UDSEntry::UDS_ACCESS, 0755);
entry.fastInsert(UDSEntry::UDS_MIME_TYPE,
QStringLiteral("inode/directory"));
} else {
return WorkerResult::fail(ERR_DOES_NOT_EXIST,
m_client.lastError());
}
}
statEntry(entry);
return WorkerResult::pass();
}
WorkerResult OllieWorker::get(const QUrl &url)
{
if (!ensureConnected()) {
return WorkerResult::fail(ERR_CANNOT_CONNECT,
QStringLiteral("Cannot connect to Ollie 9P server: ")
+ m_client.lastError());
}
QString path = ninepPath(url);
QByteArray fileData = m_client.read(path);
if (fileData.isNull() && !m_client.lastError().isEmpty()) {
return WorkerResult::fail(ERR_CANNOT_READ,
m_client.lastError());
}
QMimeDatabase db;
QMimeType mime = db.mimeTypeForFileNameAndData(url.fileName(), fileData);
mimeType(mime.name());
totalSize(fileData.size());
data(fileData);
return WorkerResult::pass();
}
WorkerResult OllieWorker::put(const QUrl &url, int /*permissions*/, KIO::JobFlags /*flags*/)
{
if (!ensureConnected()) {
return WorkerResult::fail(ERR_CANNOT_CONNECT,
QStringLiteral("Cannot connect to Ollie 9P server: ")
+ m_client.lastError());
}
// Read incoming data from the job
QByteArray allData;
int result;
do {
QByteArray buffer;
dataReq();
result = readData(buffer);
if (result > 0)
allData.append(buffer);
} while (result > 0);
QString path = ninepPath(url);
if (!m_client.write(path, allData)) {
return WorkerResult::fail(ERR_CANNOT_WRITE,
m_client.lastError());
}
return WorkerResult::pass();
}
WorkerResult OllieWorker::mkdir(const QUrl &url, int /*permissions*/)
{
if (!ensureConnected()) {
return WorkerResult::fail(ERR_CANNOT_CONNECT,
QStringLiteral("Cannot connect to Ollie 9P server: ")
+ m_client.lastError());
}
QString path = ninepPath(url);
if (!m_client.mkdir(path)) {
return WorkerResult::fail(ERR_CANNOT_MKDIR,
m_client.lastError());
}
return WorkerResult::pass();
}
WorkerResult OllieWorker::del(const QUrl &url, bool /*isFile*/)
{
if (!ensureConnected()) {
return WorkerResult::fail(ERR_CANNOT_CONNECT,
QStringLiteral("Cannot connect to Ollie 9P server: ")
+ m_client.lastError());
}
QString path = ninepPath(url);
if (!m_client.remove(path)) {
return WorkerResult::fail(ERR_CANNOT_DELETE,
m_client.lastError());
}
return WorkerResult::pass();
}
WorkerResult OllieWorker::rename(const QUrl &src, const QUrl &dest, KIO::JobFlags /*flags*/)
{
if (!ensureConnected()) {
return WorkerResult::fail(ERR_CANNOT_CONNECT,
QStringLiteral("Cannot connect to Ollie 9P server: ")
+ m_client.lastError());
}
QString srcPath = ninepPath(src);
QString newName = dest.fileName();
if (!m_client.rename(srcPath, newName)) {
return WorkerResult::fail(ERR_CANNOT_RENAME,
m_client.lastError());
}
return WorkerResult::pass();
}
#include "ollie_kio.moc"