diff --git a/activities/KioActivities.cpp b/activities/KioActivities.cpp index d03cb30a..8fa6c6e5 100644 --- a/activities/KioActivities.cpp +++ b/activities/KioActivities.cpp @@ -1,363 +1,362 @@ /* * Copyright (C) 2012 - 2016 by Ivan Cukic * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "KioActivities.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include class ActivitiesProtocol::Private { public: Private() { } enum PathType { RootItem, ActivityRootItem, ActivityPathItem }; PathType pathType(const QUrl &url, QString *activity = Q_NULLPTR, QString *filePath = Q_NULLPTR) const { const auto fullPath = url.adjusted(QUrl::StripTrailingSlash).path(); const auto path = fullPath.midRef(fullPath.startsWith('/') ? 1 : 0); if (activity) { *activity = path.mid(0, path.indexOf("/") - 1).toString(); } if (filePath) { auto strippedPath = path.mid(path.indexOf("/") + 1); auto splitPosition = strippedPath.indexOf("/"); if (splitPosition == -1) { // if we have only one path segment *filePath = demangledPath(strippedPath.toString()); } else { // if we have sub-paths auto head = strippedPath.mid(0, splitPosition); auto tail = strippedPath.mid(splitPosition); *filePath = demangledPath(head.toString()) + tail.toString(); } } return path.length() == 0 ? RootItem : path.contains("/") ? ActivityPathItem : ActivityRootItem; } void syncActivities(KActivities::Consumer &activities) { // We need to use the consumer in a synchronized way while (activities.serviceStatus() == KActivities::Consumer::Unknown) { QCoreApplication::processEvents(); } } KIO::UDSEntry activityEntry(const QString &activity) { KIO::UDSEntry uds; KActivities::Info activityInfo(activity); uds.insert(KIO::UDSEntry::UDS_NAME, activity); uds.insert(KIO::UDSEntry::UDS_DISPLAY_NAME, activityInfo.name()); uds.insert(KIO::UDSEntry::UDS_DISPLAY_TYPE, i18n("Activity")); uds.insert(KIO::UDSEntry::UDS_ICON_NAME, activityInfo.icon()); uds.insert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR); uds.insert(KIO::UDSEntry::UDS_MIME_TYPE, QStringLiteral("inode/directory")); uds.insert(KIO::UDSEntry::UDS_ACCESS, 0500); uds.insert(KIO::UDSEntry::UDS_USER, KUser().loginName()); return uds; } KIO::UDSEntry filesystemEntry(const QString &path) { KIO::UDSEntry uds; auto url = QUrl::fromLocalFile(path); if (KIO::StatJob* job = KIO::stat(url, KIO::HideProgressInfo)) { QScopedPointer sp(job); job->setAutoDelete(false); if (job->exec()) { uds = job->statResult(); } } auto mangled = mangledPath(path); // QProcess::execute("kdialog", { "--passivepopup", mangled }); uds.insert(KIO::UDSEntry::UDS_NAME, mangled); uds.insert(KIO::UDSEntry::UDS_DISPLAY_NAME, url.fileName()); uds.insert(KIO::UDSEntry::UDS_TARGET_URL, url.url()); uds.insert(KIO::UDSEntry::UDS_LOCAL_PATH, path); return uds; } QString mangledPath(const QString &path) const { // return QString::fromUtf8(QUrl::toPercentEncoding(path)); return QString::fromLatin1(path.toUtf8().toBase64( QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals)); } QString demangledPath(const QString &mangled) const { // return QUrl::fromPercentEncoding(mangled.toUtf8()); return QString::fromUtf8(QByteArray::fromBase64(mangled.toLatin1(), QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals)); } // KActivities::Consumer activities; }; extern "C" int Q_DECL_EXPORT kdemain(int argc, char **argv) { // necessary to use other kio slaves QCoreApplication app(argc, argv); if (argc != 4) { fprintf(stderr, "Usage: kio_activities protocol domain-socket1 domain-socket2\n"); exit(-1); } // start the slave ActivitiesProtocol slave(argv[2], argv[3]); slave.dispatchLoop(); return 0; } ActivitiesProtocol::ActivitiesProtocol(const QByteArray &poolSocket, const QByteArray &appSocket) : KIO::ForwardingSlaveBase("activities", poolSocket, appSocket) { } ActivitiesProtocol::~ActivitiesProtocol() { } bool ActivitiesProtocol::rewriteUrl(const QUrl &url, QUrl &newUrl) { QString activity, path; switch (d->pathType(url, &activity, &path)) { case Private::RootItem: case Private::ActivityRootItem: if (activity == "current") { KActivities::Consumer activities; d->syncActivities(activities); newUrl = QUrl(QStringLiteral("activities:/") + activities.currentActivity()); return true; } return false; case Private::ActivityPathItem: { // auto demangled = d->demangledPath(path); // QProcess::execute("kdialog", // { "--passivepopup", // path.midRef(1).toString() + "\n" + demangled }); newUrl = QUrl::fromLocalFile(path); return true; } default: return true; } } void ActivitiesProtocol::listDir(const QUrl &url) { KActivities::Consumer activities; d->syncActivities(activities); QString activity, path; switch (d->pathType(url, &activity, &path)) { case Private::RootItem: { KIO::UDSEntryList udslist; KIO::UDSEntry uds; uds.insert(KIO::UDSEntry::UDS_NAME, QStringLiteral("current")); uds.insert(KIO::UDSEntry::UDS_DISPLAY_NAME, i18n("Current activity")); uds.insert(KIO::UDSEntry::UDS_DISPLAY_TYPE, i18n("Activity")); uds.insert(KIO::UDSEntry::UDS_ICON_NAME, QStringLiteral("preferences-activities")); uds.insert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR); uds.insert(KIO::UDSEntry::UDS_MIME_TYPE, QStringLiteral("inode/directory")); uds.insert(KIO::UDSEntry::UDS_ACCESS, 0500); uds.insert(KIO::UDSEntry::UDS_USER, KUser().loginName()); uds.insert(KIO::UDSEntry::UDS_TARGET_URL, QStringLiteral("activities:/") + activities.currentActivity()); udslist << uds; for (const auto activity: activities.activities()) { udslist << d->activityEntry(activity); } listEntries(udslist); finished(); break; } case Private::ActivityRootItem: { KIO::UDSEntryList udslist; auto database = Common::Database::instance( Common::Database::ResourcesDatabase, Common::Database::ReadOnly); if (!database) { finished(); break; } if (activity == "current") { activity = activities.currentActivity(); } static const auto queryString = QStringLiteral( "SELECT targettedResource " "FROM ResourceLink " "WHERE usedActivity = '%1' " "AND initiatingAgent = \":global\" " ); auto query = database->execQuery(queryString.arg(activity)); for (const auto& result: query) { auto path = result[0].toString(); if (!QFile(path).exists()) continue; KIO::UDSEntry uds; udslist << d->filesystemEntry(path); } listEntries(udslist); finished(); break; } case Private::ActivityPathItem: ForwardingSlaveBase::listDir(QUrl::fromLocalFile(path)); break; } } void ActivitiesProtocol::prepareUDSEntry(KIO::UDSEntry &entry, bool listing) const { ForwardingSlaveBase::prepareUDSEntry(entry, listing); } void ActivitiesProtocol::stat(const QUrl& url) { QString activity; switch (d->pathType(url, &activity)) { case Private::RootItem: { QString dirName = i18n("Activities"); KIO::UDSEntry uds; uds.insert(KIO::UDSEntry::UDS_NAME, dirName); uds.insert(KIO::UDSEntry::UDS_DISPLAY_NAME, dirName); uds.insert(KIO::UDSEntry::UDS_DISPLAY_TYPE, dirName); uds.insert(KIO::UDSEntry::UDS_ICON_NAME, QStringLiteral("preferences-activities")); uds.insert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR); uds.insert(KIO::UDSEntry::UDS_MIME_TYPE, QStringLiteral("inode/directory")); statEntry(uds); finished(); break; } case Private::ActivityRootItem: { KActivities::Consumer activities; d->syncActivities(activities); if (activity == "current") { activity = activities.currentActivity(); } statEntry(d->activityEntry(activity)); finished(); break; } case Private::ActivityPathItem: ForwardingSlaveBase::stat(url); break; } } void ActivitiesProtocol::mimetype(const QUrl& url) { switch (d->pathType(url)) { case Private::RootItem: case Private::ActivityRootItem: mimeType(QStringLiteral("inode/directory")); finished(); break; case Private::ActivityPathItem: ForwardingSlaveBase::mimetype(url); break; } } void ActivitiesProtocol::del(const QUrl& url, bool isfile) { Q_UNUSED(url); Q_UNUSED(isfile); } -// #include "KioActivities.moc" diff --git a/mtp/devicecache.cpp b/mtp/devicecache.cpp index 48be7267..c96d818b 100644 --- a/mtp/devicecache.cpp +++ b/mtp/devicecache.cpp @@ -1,230 +1,229 @@ /* Cache for recently used devices. Copyright (C) 2012 Philipp Schmidt This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "devicecache.h" #include "kio_mtp_helpers.h" // #include // #include #include #include #include /** * Creates a Cached Device that has a predefined lifetime (default: 10000 msec)s * The lifetime is reset every time the device is accessed. After it expires it * will be released. * * @param device The LIBMTP_mtpdevice_t pointer to cache * @param udi The UDI of the new device to cache */ CachedDevice::CachedDevice(LIBMTP_mtpdevice_t *device, LIBMTP_raw_device_t *rawdevice, const QString udi, qint32 timeout) { this->timeout = timeout; this->mtpdevice = device; this->rawdevice = *rawdevice; this->udi = udi; char *deviceName = LIBMTP_Get_Friendlyname(device); char *deviceModel = LIBMTP_Get_Modelname(device); // prefer friendly devicename over model if (!deviceName) { name = QString::fromUtf8(deviceModel); } else { name = QString::fromUtf8(deviceName); } qCDebug(LOG_KIO_MTP) << "Created device " << name << " with udi=" << udi << " and timeout " << timeout; } CachedDevice::~CachedDevice() { LIBMTP_Release_Device(mtpdevice); } LIBMTP_mtpdevice_t *CachedDevice::getDevice() { LIBMTP_mtpdevice_t *device = mtpdevice; if (!device->storage) { qCDebug(LOG_KIO_MTP) << "reopen mtpdevice if we have no storage found"; LIBMTP_Release_Device(mtpdevice); mtpdevice = LIBMTP_Open_Raw_Device_Uncached(&rawdevice); } return mtpdevice; } const QString CachedDevice::getName() { return name; } const QString CachedDevice::getUdi() { return udi; } DeviceCache::DeviceCache(qint32 timeout, QObject *parent) : QEventLoop(parent) { this->timeout = timeout; notifier = Solid::DeviceNotifier::instance(); connect(notifier, SIGNAL(deviceAdded(QString)), this, SLOT(deviceAdded(QString))); connect(notifier, SIGNAL(deviceRemoved(QString)), this, SLOT(deviceRemoved(QString))); foreach(Solid::Device solidDevice, Solid::Device::listFromType(Solid::DeviceInterface::PortableMediaPlayer, QString())) { checkDevice(solidDevice); } } DeviceCache::~DeviceCache() { processEvents(); // Release devices foreach(QString udi, udiCache.keys()) { deviceRemoved(udi); } } void DeviceCache::checkDevice(Solid::Device solidDevice) { if (!udiCache.contains(solidDevice.udi())) { qCDebug(LOG_KIO_MTP) << "new device, getting raw devices"; Solid::GenericInterface *iface = solidDevice.as(); if (!iface) { qCDebug( LOG_KIO_MTP ) << "Solid device " << solidDevice.udi() << " has NOT a Solid::GenericInterface"; return; } const QMap &properties = iface->allProperties(); const uint32_t solidBusNum = properties.value ( QLatin1String ( "BUSNUM" ) ).toUInt(); const uint32_t solidDevNum = properties.value ( QLatin1String ( "DEVNUM" ) ).toUInt(); LIBMTP_raw_device_t *rawdevices = 0; int numrawdevices; LIBMTP_error_number_t err; err = LIBMTP_Detect_Raw_Devices(&rawdevices, &numrawdevices); switch (err) { case LIBMTP_ERROR_CONNECTING: qCWarning(LOG_KIO_MTP) << "There has been an error connecting to the devices"; break; case LIBMTP_ERROR_MEMORY_ALLOCATION: qCWarning(LOG_KIO_MTP) << "Encountered a Memory Allocation Error"; break; case LIBMTP_ERROR_NONE: { qCDebug(LOG_KIO_MTP) << "No Error, continuing"; for (int i = 0; i < numrawdevices; i++) { LIBMTP_raw_device_t *rawDevice = &rawdevices[i]; uint32_t rawBusNum = rawDevice->bus_location; uint32_t rawDevNum = rawDevice->devnum; if (rawBusNum == solidBusNum && rawDevNum == solidDevNum) { qCDebug(LOG_KIO_MTP) << "Found device matching the Solid description"; LIBMTP_mtpdevice_t *mtpDevice = LIBMTP_Open_Raw_Device_Uncached(rawDevice); if (udiCache.find(solidDevice.udi()) == udiCache.end()) { CachedDevice *cDev = new CachedDevice(mtpDevice, rawDevice, solidDevice.udi(), timeout); udiCache.insert(solidDevice.udi(), cDev); nameCache.insert(cDev->getName(), cDev); } } } } break; case LIBMTP_ERROR_GENERAL: default: qCWarning(LOG_KIO_MTP) << "Unknown connection error"; break; } free(rawdevices); } } void DeviceCache::deviceAdded(const QString &udi) { qCDebug(LOG_KIO_MTP) << "New device attached with udi=" << udi << ". Checking if PortableMediaPlayer..."; Solid::Device device(udi); if (device.isDeviceInterface(Solid::DeviceInterface::PortableMediaPlayer)) { qCDebug(LOG_KIO_MTP) << "SOLID: New Device with udi=" << udi << "||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||"; checkDevice(device); } } void DeviceCache::deviceRemoved(const QString &udi) { if (udiCache.contains(udi)) { qCDebug(LOG_KIO_MTP) << "SOLID: Device with udi=" << udi << " removed. ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||"; CachedDevice *cDev = udiCache.value(udi); udiCache.remove(cDev->getUdi()); nameCache.remove(cDev->getName()); delete cDev; } } QHash DeviceCache::getAll() { qCDebug(LOG_KIO_MTP) << "getAll()"; processEvents(); return nameCache; } bool DeviceCache::contains(QString string, bool isUdi) { processEvents(); if (isUdi) { return udiCache.find(string) != udiCache.end(); } else { return nameCache.find(string) != nameCache.end(); } } CachedDevice *DeviceCache::get(const QString &string, bool isUdi) { processEvents(); if (isUdi) { return udiCache.value(string); } else { return nameCache.value(string); } } int DeviceCache::size() { processEvents(); return nameCache.size(); } -#include "devicecache.moc" diff --git a/mtp/filecache.cpp b/mtp/filecache.cpp index e60e3d4f..86d68b1a 100644 --- a/mtp/filecache.cpp +++ b/mtp/filecache.cpp @@ -1,74 +1,73 @@ /* Cache for recent files accessed. Copyright (C) 2012 Philipp Schmidt This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "filecache.h" #include FileCache::FileCache(QObject *parent) : QObject(parent) { } uint32_t FileCache::queryPath(const QString &path, int timeToLive) { qCDebug(LOG_KIO_MTP) << "Querying" << path; QPair< QDateTime, uint32_t > item = cache.value(path); if (item.second != 0) { QDateTime dateTime = QDateTime::currentDateTime(); if (item.first > dateTime) { qCDebug(LOG_KIO_MTP) << "Found item with ttl:" << item.first << "- now:" << dateTime; item.first = dateTime.addSecs(timeToLive); qCDebug(LOG_KIO_MTP) << "Reset item ttl:" << item.first; cache.insert(path, item); return item.second; } else { qCDebug(LOG_KIO_MTP) << "Item too old (" << item.first << "), removed. Current Time: " << dateTime; cache.remove(path); return 0; } } return 0; } void FileCache::addPath(const QString &path, uint32_t id, int timeToLive) { QDateTime dateTime = QDateTime::currentDateTime(); dateTime = dateTime.addSecs(timeToLive); QPair< QDateTime, uint32_t > item(dateTime, id); cache.insert(path, item); } void FileCache::removePath(const QString &path) { cache.remove(path); } -#include "filecache.moc" diff --git a/mtp/kio_mtp.cpp b/mtp/kio_mtp.cpp index 541268a1..81210e7a 100644 --- a/mtp/kio_mtp.cpp +++ b/mtp/kio_mtp.cpp @@ -1,960 +1,959 @@ /* * Main implementation for KIO-MTP * Copyright (C) 2012 Philipp Schmidt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "kio_mtp.h" #include "kio_mtp_helpers.h" // #include #include #include #include #include #include #include #include #include #include #include ////////////////////////////////////////////////////////////////////////////// ///////////////////////////// Slave Implementation /////////////////////////// ////////////////////////////////////////////////////////////////////////////// Q_LOGGING_CATEGORY(LOG_KIO_MTP, "kde.kio-mtp") extern "C" int Q_DECL_EXPORT kdemain(int argc, char **argv) { QCoreApplication app(argc, argv); app.setApplicationName(QLatin1String("kio_mtp")); if (argc != 4) { fprintf(stderr, "Usage: kio_mtp protocol domain-socket1 domain-socket2\n"); exit(-1); } MTPSlave slave(argv[2], argv[3]); slave.dispatchLoop(); qCDebug(LOG_KIO_MTP) << "Slave EventLoop ended"; return 0; } MTPSlave::MTPSlave(const QByteArray &pool, const QByteArray &app) : SlaveBase("mtp", pool, app) { LIBMTP_Init(); qCDebug(LOG_KIO_MTP) << "Slave started"; deviceCache = new DeviceCache(60000); fileCache = new FileCache(this); qCDebug(LOG_KIO_MTP) << "Caches created"; } MTPSlave::~MTPSlave() { qCDebug(LOG_KIO_MTP) << "Slave destroyed"; delete fileCache; delete deviceCache; } /** * @brief Get's the correct object from the device. * @param pathItems A QStringList containing the items of the filepath * @return QPair with the object and its device. pair.first is a nullpointer if the object doesn't exist or for root or, depending on the pathItems size device (1), storage (2) or file (>=3) */ QPair MTPSlave::getPath(const QString &path) { QStringList pathItems = path.split(QLatin1Char('/'), QString::SkipEmptyParts); qCDebug(LOG_KIO_MTP) << path << pathItems.size(); QPair ret; // Don' handle the root directory if (pathItems.size() <= 0) { return ret; } if (deviceCache->contains(pathItems.at(0))) { LIBMTP_mtpdevice_t *device = deviceCache->get(pathItems.at(0))->getDevice(); // return specific device if (pathItems.size() == 1) { ret.first = device; ret.second = device; qCDebug(LOG_KIO_MTP) << "returning LIBMTP_mtpdevice_t"; } if (pathItems.size() > 2) { // Query Cache after we have the device uint32_t c_fileID = fileCache->queryPath(path); if (c_fileID != 0) { qCDebug(LOG_KIO_MTP) << "Match found in cache, checking device"; LIBMTP_file_t *file = LIBMTP_Get_Filemetadata(device, c_fileID); if (file) { qCDebug(LOG_KIO_MTP) << "Found file in cache"; ret.first = file; ret.second = device; qCDebug(LOG_KIO_MTP) << "returning LIBMTP_file_t from cache"; return ret; } } // Query cache for parent else if (pathItems.size() > 3) { QString parentPath = convertToPath(pathItems, pathItems.size() - 1); uint32_t c_parentID = fileCache->queryPath(parentPath); qCDebug(LOG_KIO_MTP) << "Match for parent found in cache, checking device. Parent id = " << c_parentID; LIBMTP_file_t *parent = LIBMTP_Get_Filemetadata(device, c_parentID); if (parent) { qCDebug(LOG_KIO_MTP) << "Found parent in cache"; // fileCache->addPath( parentPath, c_parentID ); QMap files = getFiles(device, parent->storage_id, c_parentID); for (QMap::iterator it = files.begin(); it != files.end(); ++it) { QString filePath = parentPath; filePath.append(QString::fromUtf8("/")).append(it.key()); fileCache->addPath(filePath, it.value()->item_id); } if (files.contains(pathItems.last())) { LIBMTP_file_t *file = files.value(pathItems.last()); ret.first = file; ret.second = device; qCDebug(LOG_KIO_MTP) << "returning LIBMTP_file_t from cached parent" ; fileCache->addPath(path, file->item_id); } return ret; } } } QMap storages = getDevicestorages(device); if (pathItems.size() > 1 && storages.contains(pathItems.at(1))) { LIBMTP_devicestorage_t *storage = storages.value(pathItems.at(1)); if (pathItems.size() == 2) { ret.first = storage; ret.second = device; qCDebug(LOG_KIO_MTP) << "returning LIBMTP_devicestorage_t"; return ret; } int currentLevel = 2, currentParent = 0xFFFFFFFF; QMap files; // traverse further while depth not reached while (currentLevel < pathItems.size()) { files = getFiles(device, storage->id, currentParent); if (files.contains(pathItems.at(currentLevel))) { currentParent = files.value(pathItems.at(currentLevel))->item_id; } else { qCDebug(LOG_KIO_MTP) << "returning LIBMTP_file_t using tree walk"; return ret; } currentLevel++; } ret.first = LIBMTP_Get_Filemetadata(device, currentParent); ret.second = device; fileCache->addPath(path, currentParent); } } return ret; } int MTPSlave::checkUrl(const QUrl &url, bool redirect) { qCDebug(LOG_KIO_MTP) << url; if (url.path().startsWith(QLatin1String("udi=")) && redirect) { QString udi = url.adjusted(QUrl::StripTrailingSlash).path().remove(0, 4); qCDebug(LOG_KIO_MTP) << "udi = " << udi; if (deviceCache->contains(udi, true)) { QUrl newUrl; newUrl.setScheme(QLatin1String("mtp")); newUrl.setPath(QLatin1Char('/') + deviceCache->get(udi, true)->getName()); redirection(newUrl); return 1; } else { return 2; } } else if (url.path().startsWith(QLatin1Char('/'))) { return 0; } return -1; } QString MTPSlave::urlDirectory(const QUrl &url, bool appendTrailingSlash) { if (!appendTrailingSlash) { return url.adjusted(QUrl::StripTrailingSlash | QUrl::RemoveFilename).path(); } return url.adjusted(QUrl::RemoveFilename).path(); } QString MTPSlave::urlFileName(const QUrl &url) { return url.fileName(); } void MTPSlave::listDir(const QUrl &url) { qCDebug(LOG_KIO_MTP) << url.path(); int check = checkUrl(url); switch (check) { case 0: break; case 1: finished(); return; case 2: error(ERR_DOES_NOT_EXIST, url.path()); return; default: error(ERR_MALFORMED_URL, url.path()); return; } QStringList pathItems = url.path().split(QLatin1Char('/'), QString::SkipEmptyParts); UDSEntry entry; // list devices if (pathItems.size() == 0) { qCDebug(LOG_KIO_MTP) << "Root directory, listing devices"; totalSize(deviceCache->size()); foreach(CachedDevice * cachedDevice, deviceCache->getAll().values()) { LIBMTP_mtpdevice_t *device = cachedDevice->getDevice(); getEntry(entry, device); listEntry(entry); entry.clear(); } qCDebug(LOG_KIO_MTP) << "[SUCCESS] :: Devices"; finished(); } // traverse into device else if (deviceCache->contains(pathItems.at(0))) { QPair pair = getPath(url.path()); UDSEntry entry; if (pair.first) { LIBMTP_mtpdevice_t *device = pair.second; // Device, list storages if (pathItems.size() == 1) { QMap storages = getDevicestorages(device); qCDebug(LOG_KIO_MTP) << "Listing storages for device " << pathItems.at(0); totalSize(storages.size()); if (storages.size() > 0) { foreach(const QString & storageName, storages.keys()) { getEntry(entry, storages.value(storageName)); listEntry(entry); entry.clear(); } finished(); qCDebug(LOG_KIO_MTP) << "[SUCCESS] :: Storages"; } else { warning(i18n("No Storages found. Make sure your device is unlocked and has MTP enabled in its USB connection settings.")); } } // Storage, list files and folders of storage root else { QMap files; if (pathItems.size() == 2) { qCDebug(LOG_KIO_MTP) << "Getting storage root listing"; LIBMTP_devicestorage_t *storage = (LIBMTP_devicestorage_t *)pair.first; qCDebug(LOG_KIO_MTP) << "We have a storage:" << (storage == NULL); files = getFiles(device, storage->id); } else { LIBMTP_file_t *parent = (LIBMTP_file_t *)pair.first; files = getFiles(device, parent->storage_id, parent->item_id); } for (QMap::iterator it = files.begin(); it != files.end(); ++it) { LIBMTP_file_t *file = it.value(); QString filePath; if (url.path().endsWith(QLatin1Char('/'))) { filePath = url.path().append(it.key()); } else { filePath = url.path().append(QLatin1Char('/')).append(it.key()); } fileCache->addPath(filePath, file->item_id); getEntry(entry, file); listEntry(entry); entry.clear(); } finished(); qCDebug(LOG_KIO_MTP) << "[SUCCESS] Files"; } } else { error(ERR_CANNOT_ENTER_DIRECTORY, url.path()); qCDebug(LOG_KIO_MTP) << "[ERROR]"; } } else { error(ERR_CANNOT_ENTER_DIRECTORY, url.path()); qCDebug(LOG_KIO_MTP) << "[ERROR]"; } } void MTPSlave::stat(const QUrl &url) { qCDebug(LOG_KIO_MTP) << url.path(); int check = checkUrl(url); switch (check) { case 0: break; case 1: finished(); return; case 2: error(ERR_DOES_NOT_EXIST, url.path()); return; default: error(ERR_MALFORMED_URL, url.path()); return; } QStringList pathItems = url.path().split(QLatin1Char('/'), QString::SkipEmptyParts); QPair pair = getPath(url.path()); UDSEntry entry; if (pair.first) { // Root if (pathItems.size() < 1) { entry.insert(UDSEntry::UDS_NAME, QLatin1String("mtp:///")); entry.insert(UDSEntry::UDS_FILE_TYPE, S_IFDIR); entry.insert(UDSEntry::UDS_ACCESS, S_IRUSR | S_IRGRP | S_IROTH | S_IXUSR | S_IXGRP | S_IXOTH); entry.insert(UDSEntry::UDS_MIME_TYPE, QLatin1String("inode/directory")); } // Device else if (pathItems.size() < 2) { getEntry(entry, pair.second); } // Storage else if (pathItems.size() < 3) { getEntry(entry, (LIBMTP_devicestorage_t *) pair.first); } // Folder/File else { getEntry(entry, (LIBMTP_file_t *) pair.first); } } statEntry(entry); finished(); } void MTPSlave::mimetype(const QUrl &url) { int check = checkUrl(url); switch (check) { case 0: break; case 1: finished(); return; case 2: error(ERR_DOES_NOT_EXIST, url.path()); return; default: error(ERR_MALFORMED_URL, url.path()); return; } qCDebug(LOG_KIO_MTP) << url.path(); QStringList pathItems = url.path().split(QLatin1Char('/'), QString::SkipEmptyParts); QPair pair = getPath(url.path()); if (pair.first) { // NOTE the difference between calling mimetype and mimeType if (pathItems.size() > 2) { mimeType(getMimetype(((LIBMTP_file_t *) pair.first)->filetype)); } else { mimeType(QString::fromLatin1("inode/directory")); } } else { error(ERR_DOES_NOT_EXIST, url.path()); return; } } void MTPSlave::put(const QUrl &url, int, JobFlags flags) { int check = checkUrl(url); switch (check) { case 0: break; default: error(ERR_MALFORMED_URL, url.path()); return; } qCDebug(LOG_KIO_MTP) << url.path(); QStringList destItems = url.path().split(QLatin1Char('/'), QString::SkipEmptyParts); // Can't copy to root or device, needs storage if (destItems.size() < 2) { error(ERR_UNSUPPORTED_ACTION, url.path()); return; } if (!(flags & KIO::Overwrite) && getPath(url.path()).first) { error(ERR_FILE_ALREADY_EXIST, url.path()); return; } destItems.takeLast(); QPair pair = getPath(urlDirectory(url)); if (!pair.first) { error(ERR_DOES_NOT_EXIST, url.path()); return; } LIBMTP_mtpdevice_t *device = pair.second; LIBMTP_file_t *parent = (LIBMTP_file_t *) pair.first; if (parent->filetype != LIBMTP_FILETYPE_FOLDER) { error(ERR_IS_FILE, urlDirectory(url)); return; } // We did get a total size from the application if (hasMetaData(QLatin1String("sourceSize"))) { qCDebug(LOG_KIO_MTP) << "direct put"; LIBMTP_file_t *file = LIBMTP_new_file_t(); file->parent_id = parent->item_id; file->filename = strdup(urlFileName(url).toUtf8().data()); file->filetype = getFiletype(urlFileName(url)); file->filesize = metaData(QLatin1String("sourceSize")).toULongLong(); file->modificationdate = QDateTime::currentDateTime().toTime_t(); file->storage_id = parent->storage_id; qCDebug(LOG_KIO_MTP) << "Sending file" << file->filename; int ret = LIBMTP_Send_File_From_Handler(device, &dataGet, this, file, &dataProgress, this); LIBMTP_destroy_file_t(file); if (ret != 0) { error(KIO::ERR_COULD_NOT_WRITE, urlFileName(url)); LIBMTP_Dump_Errorstack(device); LIBMTP_Clear_Errorstack(device); return; } } // We need to get the entire file first, then we can upload else { qCDebug(LOG_KIO_MTP) << "use temp file"; QTemporaryFile temp; QByteArray buffer; int len = 0; do { dataReq(); len = readData(buffer); temp.write(buffer); } while (len > 0); QFileInfo info(temp); LIBMTP_file_t *file = LIBMTP_new_file_t(); file->parent_id = parent->item_id; file->filename = strdup(urlFileName(url).toUtf8().data()); file->filetype = getFiletype(urlFileName(url)); file->filesize = info.size(); file->modificationdate = QDateTime::currentDateTime().toTime_t(); file->storage_id = parent->storage_id; int ret = LIBMTP_Send_File_From_File_Descriptor(device, temp.handle(), file, NULL, NULL); LIBMTP_destroy_file_t(file); if (ret != 0) { error(KIO::ERR_COULD_NOT_WRITE, urlFileName(url)); return; } finished(); } } void MTPSlave::get(const QUrl &url) { int check = checkUrl(url); switch (check) { case 0: break; default: error(ERR_MALFORMED_URL, url.path()); return; } qCDebug(LOG_KIO_MTP) << url.path(); QStringList pathItems = url.path().split(QLatin1Char('/'), QString::SkipEmptyParts); // File if (pathItems.size() > 2) { QPair pair = getPath(url.path()); if (pair.first) { LIBMTP_file_t *file = (LIBMTP_file_t *) pair.first; mimeType(getMimetype(file->filetype)); totalSize(file->filesize); LIBMTP_mtpdevice_t *device = pair.second; int ret = LIBMTP_Get_File_To_Handler(device, file->item_id, &dataPut, this, &dataProgress, this); if (ret != 0) { error(ERR_COULD_NOT_READ, url.path()); return; } data(QByteArray()); finished(); } else { error(ERR_DOES_NOT_EXIST, url.path()); } } else { error(ERR_UNSUPPORTED_ACTION, url.path()); } } void MTPSlave::copy(const QUrl &src, const QUrl &dest, int, JobFlags flags) { qCDebug(LOG_KIO_MTP) << src.path() << dest.path(); if (src.scheme() == QLatin1String("mtp") && dest.scheme() == QLatin1String("mtp")) { qCDebug(LOG_KIO_MTP) << "Copy on device: Not supported"; // MTP doesn't support moving files directly on the device, so we have to download and then upload... error(ERR_UNSUPPORTED_ACTION, i18n("Cannot copy/move files on the device itself")); } else if (src.scheme() == QLatin1String("file") && dest.scheme() == QLatin1String("mtp")) { int check = checkUrl(dest); switch (check) { case 0: break; default: error(ERR_MALFORMED_URL, dest.path()); return; } QStringList destItems = dest.path().split(QLatin1Char('/') , QString::SkipEmptyParts); // Can't copy to root or device, needs storage if (destItems.size() < 2) { error(ERR_UNSUPPORTED_ACTION, dest.path()); return; } qCDebug(LOG_KIO_MTP) << "Copy file " << urlFileName(src) << "from filesystem to device" << urlDirectory(src, true) << urlDirectory(dest, true); if (!(flags & KIO::Overwrite) && getPath(dest.path()).first) { error(ERR_FILE_ALREADY_EXIST, dest.path()); return; } destItems.takeLast(); QPair pair = getPath(urlDirectory(dest)); if (!pair.first) { error(ERR_DOES_NOT_EXIST, urlDirectory(dest, true)); return; } LIBMTP_mtpdevice_t *device = pair.second; uint32_t parent_id = 0xFFFFFFFF, storage_id = 0; if (destItems.size() == 2) { LIBMTP_devicestorage_t *storage = (LIBMTP_devicestorage_t *) pair.first; storage_id = storage->id; } else { LIBMTP_file_t *parent = (LIBMTP_file_t *) pair.first; storage_id = parent->storage_id; parent_id = parent->item_id; if (parent->filetype != LIBMTP_FILETYPE_FOLDER) { error(ERR_IS_FILE, urlDirectory(dest)); return; } } QFileInfo source(src.path()); LIBMTP_file_t *file = LIBMTP_new_file_t(); file->parent_id = parent_id; file->filename = strdup(urlFileName(dest).toUtf8().data()); file->filetype = getFiletype(urlFileName(dest)); file->filesize = source.size(); file->modificationdate = source.lastModified().toTime_t(); file->storage_id = storage_id; qCDebug(LOG_KIO_MTP) << "Sending file" << file->filename << "with size" << file->filesize; totalSize(source.size()); int ret = LIBMTP_Send_File_From_File(device, src.path().toUtf8().data(), file, (LIBMTP_progressfunc_t) &dataProgress, this); LIBMTP_destroy_file_t(file); if (ret != 0) { error(KIO::ERR_COULD_NOT_WRITE, urlFileName(dest)); LIBMTP_Dump_Errorstack(device); LIBMTP_Clear_Errorstack(device); return; } qCDebug(LOG_KIO_MTP) << "Sent file"; } else if (src.scheme() == QLatin1String("mtp") && dest.scheme() == QLatin1String("file")) { int check = checkUrl(src); switch (check) { case 0: break; default: error(ERR_MALFORMED_URL, src.path()); return; } qCDebug(LOG_KIO_MTP) << "Copy file " << urlFileName(src) << "from device to filesystem" << urlDirectory(src, true) << urlDirectory(dest, true); QFileInfo destination(dest.path()); if (!(flags & KIO::Overwrite) && destination.exists()) { error(ERR_FILE_ALREADY_EXIST, dest.path()); return; } QStringList srcItems = src.path().split(QLatin1Char('/'), QString::SkipEmptyParts); // Can't copy to root or device, needs storage if (srcItems.size() < 2) { error(ERR_UNSUPPORTED_ACTION, src.path()); return; } QPair pair = getPath(src.path()); if (!pair.first) { error(ERR_COULD_NOT_READ, src.path()); return; } LIBMTP_mtpdevice_t *device = pair.second; LIBMTP_file_t *source = (LIBMTP_file_t *) pair.first; if (source->filetype == LIBMTP_FILETYPE_FOLDER) { error(ERR_IS_DIRECTORY, urlDirectory(src)); return; } qCDebug(LOG_KIO_MTP) << "Getting file" << source->filename << urlFileName(dest) << source->filesize; totalSize(source->filesize); int ret = LIBMTP_Get_File_To_File(device, source->item_id, dest.path().toUtf8().data(), (LIBMTP_progressfunc_t) &dataProgress, this); if (ret != 0) { error(KIO::ERR_COULD_NOT_WRITE, urlFileName(dest)); LIBMTP_Dump_Errorstack(device); LIBMTP_Clear_Errorstack(device); return; } struct utimbuf *times = (utimbuf *) malloc(sizeof(utimbuf)); times->actime = QDateTime::currentDateTime().toTime_t(); times->modtime = source->modificationdate; int result = utime(dest.path().toUtf8().data(), times); free(times); qCDebug(LOG_KIO_MTP) << "Sent file"; } finished(); } void MTPSlave::mkdir(const QUrl &url, int) { int check = checkUrl(url); switch (check) { case 0: break; default: error(ERR_MALFORMED_URL, url.path()); return; } qCDebug(LOG_KIO_MTP) << url.path(); QStringList pathItems = url.path().split(QLatin1Char('/') , QString::SkipEmptyParts); int pathDepth = pathItems.size(); if (pathItems.size() > 2 && !getPath(url.path()).first) { char *dirName = strdup(pathItems.takeLast().toUtf8().data()); LIBMTP_mtpdevice_t *device; LIBMTP_file_t *file; LIBMTP_devicestorage_t *storage; int ret = 0; QPair pair = getPath(urlDirectory(url)); if (pathDepth == 3) { //the folder need to be created straight to a storage device storage = (LIBMTP_devicestorage_t *) pair.first; device = pair.second; ret = LIBMTP_Create_Folder(device, dirName, 0xFFFFFFFF, storage->id); } else if (pair.first) { file = (LIBMTP_file_t *) pair.first; device = pair.second; if (file && file->filetype == LIBMTP_FILETYPE_FOLDER) { qCDebug(LOG_KIO_MTP) << "Found parent" << file->item_id << file->filename; qCDebug(LOG_KIO_MTP) << "Attempting to create folder" << dirName; ret = LIBMTP_Create_Folder(device, dirName, file->item_id, file->storage_id); } } if (ret != 0) { fileCache->addPath(url.path(), ret); finished(); return; } else { LIBMTP_Dump_Errorstack(device); LIBMTP_Clear_Errorstack(device); } } else { error(ERR_DIR_ALREADY_EXIST, url.path()); return; } error(ERR_COULD_NOT_MKDIR, url.path()); } void MTPSlave::del(const QUrl &url, bool) { int check = checkUrl(url); switch (check) { case 0: break; default: error(ERR_MALFORMED_URL, url.path()); return; } qCDebug(LOG_KIO_MTP) << url.path(); QStringList pathItems = url.path().split(QLatin1Char('/'), QString::SkipEmptyParts); if (pathItems.size() < 2) { error(ERR_CANNOT_DELETE, url.path()); return; } QPair pair = getPath(url.path()); LIBMTP_file_t *file = (LIBMTP_file_t *) pair.first; int ret = LIBMTP_Delete_Object(pair.second, file->item_id); LIBMTP_destroy_file_t (file); if (ret != 0) { error(ERR_CANNOT_DELETE, url.path()); return; } fileCache->removePath(url.path()); finished(); } void MTPSlave::rename(const QUrl &src, const QUrl &dest, JobFlags flags) { int check = checkUrl(src); switch (check) { case 0: break; default: error(ERR_MALFORMED_URL, src.path()); return; } check = checkUrl(dest); switch (check) { case 0: break; default: error(ERR_MALFORMED_URL, dest.path()); return; } qCDebug(LOG_KIO_MTP) << src.path(); QStringList srcItems = src.path().split(QLatin1Char('/'), QString::SkipEmptyParts); QPair pair = getPath(src.path()); if (pair.first) { // Rename Device if (srcItems.size() == 1) { LIBMTP_Set_Friendlyname(pair.second, urlFileName(dest).toUtf8().data()); } // Rename Storage else if (srcItems.size() == 2) { error(ERR_CANNOT_RENAME, src.path()); return; } else { LIBMTP_file_t *destination = (LIBMTP_file_t *) getPath(dest.path()).first; LIBMTP_file_t *source = (LIBMTP_file_t *) pair.first; if (!(flags & KIO::Overwrite) && destination) { if (destination->filetype == LIBMTP_FILETYPE_FOLDER) { error(ERR_DIR_ALREADY_EXIST, dest.path()); } else { error(ERR_FILE_ALREADY_EXIST, dest.path()); } return; } int ret = LIBMTP_Set_File_Name(pair.second, source, urlFileName(dest).toUtf8().data()); if (ret != 0) { error(ERR_CANNOT_RENAME, src.path()); return; } else { fileCache->addPath(dest.path(), source->item_id); fileCache->removePath(src.path()); } LIBMTP_destroy_file_t (source); } finished(); } else { error(ERR_DOES_NOT_EXIST, src.path()); } } void MTPSlave::virtual_hook(int id, void *data) { switch(id) { case SlaveBase::GetFileSystemFreeSpace: { QUrl *url = static_cast(data); fileSystemFreeSpace(*url); } break; default: SlaveBase::virtual_hook(id, data); } } void MTPSlave::fileSystemFreeSpace(const QUrl &url) { qCDebug(LOG_KIO_MTP) << "fileSystemFreeSpace:" << url; const int check = checkUrl(url); switch (check) { case 0: break; case 1: finished(); return; case 2: error(ERR_DOES_NOT_EXIST, url.toDisplayString()); return; default: error(ERR_MALFORMED_URL, url.toDisplayString()); return; } const auto path = url.path(); const auto storagePath = path.section(QLatin1Char('/'), 0, 2, QString::SectionIncludeLeadingSep); if (storagePath.count(QLatin1Char('/')) != 2) { // /{device}/{storage} error(KIO::ERR_COULD_NOT_STAT, url.toDisplayString()); return; } const auto pair = getPath(storagePath); auto storage = (LIBMTP_devicestorage_t *)pair.first; if (!storage) { error(KIO::ERR_COULD_NOT_STAT, url.toDisplayString()); return; } setMetaData(QStringLiteral("total"), QString::number(storage->MaxCapacity)); setMetaData(QStringLiteral("available"), QString::number(storage->FreeSpaceInBytes)); finished(); } -#include "kio_mtp.moc" diff --git a/thumbnail/htmlcreator.cpp b/thumbnail/htmlcreator.cpp index 0083f9c5..2f9d51f0 100644 --- a/thumbnail/htmlcreator.cpp +++ b/thumbnail/htmlcreator.cpp @@ -1,109 +1,108 @@ /* This file is part of the KDE libraries Copyright (C) 2000 Malte Starostik Copyright (C) 2006 Roberto Cappuccio Copyright (C) 2011 Dawit Alemayehu 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 "htmlcreator.h" #include #include #include #include #include extern "C" { Q_DECL_EXPORT ThumbCreator *new_creator() { return new HTMLCreator; } } HTMLCreator::HTMLCreator() : m_loadedOk(true), m_page(0) { } HTMLCreator::~HTMLCreator() { delete m_page; } bool HTMLCreator::create(const QString &path, int width, int height, QImage &img) { if (!m_page) { m_page = new QWebEnginePage; connect(m_page, &QWebEnginePage::loadFinished, this, &HTMLCreator::slotFinished); m_page->settings()->setAttribute(QWebEngineSettings::JavascriptEnabled, false); m_page->settings()->setAttribute(QWebEngineSettings::PluginsEnabled, false); m_page->settings()->setAttribute(QWebEngineSettings::LocalContentCanAccessRemoteUrls, false); m_page->settings()->setAttribute(QWebEngineSettings::LocalContentCanAccessFileUrls, true); } QUrl url = QUrl::fromUserInput(path); // the argument should be a QUrl! m_loadedOk = false; m_page->load(url); const int t = startTimer((url.isLocalFile()?5000:30000)); m_eventLoop.exec(QEventLoop::ExcludeUserInputEvents); killTimer(t); if (m_page->contentsSize().isEmpty()) { m_loadedOk = false; } if (!m_loadedOk) { return false; } QPixmap pix; if (width > 400 || height > 600) { if (height * 3 > width * 4) { pix = QPixmap(width, width * 4 / 3); } else { pix = QPixmap(height * 3 / 4, height); } } else { pix = QPixmap(400, 600); } pix.fill(Qt::transparent); m_page->view()->render(&pix); img = pix.toImage(); return true; } void HTMLCreator::timerEvent(QTimerEvent *) { m_eventLoop.quit(); } void HTMLCreator::slotFinished(bool ok) { m_loadedOk = ok; m_eventLoop.quit(); } ThumbCreator::Flags HTMLCreator::flags() const { return None; } -#include "htmlcreator.moc"