File indexing completed on 2024-05-12 04:59:45

0001 /*
0002     This file is part of the MTP KIOD module, part of the KDE project.
0003 
0004     SPDX-FileCopyrightText: 2018 Andreas Krutzler <andreas.krutzler@gmx.net>
0005     SPDX-FileCopyrightText: 2022-2023 Harald Sitter <sitter@kde.org>
0006 
0007     SPDX-License-Identifier: GPL-2.0-or-later
0008 */
0009 
0010 #include "mtpstorage.h"
0011 
0012 #include <memory>
0013 #include <span>
0014 
0015 #include <QDateTime>
0016 #include <qplatformdefs.h>
0017 
0018 #include "config-mtp.h"
0019 #include "kiod_kmtpd_debug.h"
0020 #include "memory.h"
0021 #include "mtpdevice.h"
0022 #include "mtpfile.h"
0023 #include "mtplister.h"
0024 #include "storageadaptor.h"
0025 
0026 #if defined(HAVE_LIBMTP_Get_Children)
0027 static QString dbusErrorNOENT()
0028 {
0029     return QStringLiteral("org.kde.kmtp.Error.NoEntry");
0030 }
0031 
0032 static QString dbusErrorNOTDIR()
0033 {
0034     return QStringLiteral("org.kde.kmtp.Error.NotDirectory");
0035 }
0036 #else
0037 static QString dbusErrorENOSYS()
0038 {
0039     return QStringLiteral("org.kde.kmtp.Error.NotImplemented");
0040 }
0041 #endif
0042 
0043 /**
0044  * @brief MTPDataPutFunc callback function, "puts" data from the device somewhere else
0045  */
0046 static uint16_t onDataPut(void *, void *priv, uint32_t sendlen, unsigned char *data, uint32_t *putlen)
0047 {
0048     MTPStorage *storage = static_cast<MTPStorage *>(priv);
0049     Q_EMIT storage->dataReady(QByteArray(reinterpret_cast<char *>(data), int(sendlen)));
0050     *putlen = sendlen;
0051 
0052     return LIBMTP_HANDLER_RETURN_OK;
0053 }
0054 
0055 static int onDataProgress(const uint64_t sent, const uint64_t total, const void *const priv)
0056 {
0057     MTPStorage *storage = const_cast<MTPStorage *>(static_cast<const MTPStorage *>(priv));
0058     Q_EMIT storage->copyProgress(sent, total);
0059     return LIBMTP_HANDLER_RETURN_OK;
0060 }
0061 
0062 static QString convertToPath(const QStringList &pathItems, const int elements)
0063 {
0064     QString path;
0065 
0066     for (int i = 0; i < elements && elements <= pathItems.size(); i++) {
0067         path.append(QLatin1Char('/'));
0068         path.append(pathItems.at(i));
0069     }
0070 
0071     return path;
0072 }
0073 
0074 /**
0075  * @brief Converts a mime-type to a LIBMTP_filetype_t
0076  */
0077 static LIBMTP_filetype_t getFiletype(const QString &filename)
0078 {
0079     LIBMTP_filetype_t filetype;
0080 
0081     const QString ptype = filename.split(QLatin1Char('.')).last();
0082 
0083     // TODO: use QMimeDatabase and query the mimetype from there (still from the extension presumably)
0084     // TODO: merge the mime mapping tables from this function and the reverse function
0085     // TODO: map video/* and text/* and audio/* to the generic types (e.g. LIBMTP_FILETYPE_UNDEF_VIDEO) when not otherwise mapped
0086     //   (NOTE: from glancing at the libmtp code mapping a file type isn't actually all that useful TBH, it only appears used when
0087     //    no destination folder was given?)
0088     /* This need to be kept constantly updated as new file types arrive. */
0089     if (ptype == QLatin1String("wav")) {
0090         filetype = LIBMTP_FILETYPE_WAV;
0091     } else if (ptype == QLatin1String("mp3")) {
0092         filetype = LIBMTP_FILETYPE_MP3;
0093     } else if (ptype == QLatin1String("wma")) {
0094         filetype = LIBMTP_FILETYPE_WMA;
0095     } else if (ptype == QLatin1String("ogg")) {
0096         filetype = LIBMTP_FILETYPE_OGG;
0097     } else if (ptype == QLatin1String("mp4")) {
0098         filetype = LIBMTP_FILETYPE_MP4;
0099     } else if (ptype == QLatin1String("wmv")) {
0100         filetype = LIBMTP_FILETYPE_WMV;
0101     } else if (ptype == QLatin1String("avi")) {
0102         filetype = LIBMTP_FILETYPE_AVI;
0103     } else if (ptype == QLatin1String("mpeg") || ptype == QLatin1String("mpg")) {
0104         filetype = LIBMTP_FILETYPE_MPEG;
0105     } else if (ptype == QLatin1String("asf")) {
0106         filetype = LIBMTP_FILETYPE_ASF;
0107     } else if (ptype == QLatin1String("qt") || ptype == QLatin1String("mov")) {
0108         filetype = LIBMTP_FILETYPE_QT;
0109     } else if (ptype == QLatin1String("wma")) {
0110         filetype = LIBMTP_FILETYPE_WMA;
0111     } else if (ptype == QLatin1String("jpg") || ptype == QLatin1String("jpeg")) {
0112         filetype = LIBMTP_FILETYPE_JPEG;
0113     } else if (ptype == QLatin1String("jfif")) {
0114         filetype = LIBMTP_FILETYPE_JFIF;
0115     } else if (ptype == QLatin1String("tif") || ptype == QLatin1String("tiff")) {
0116         filetype = LIBMTP_FILETYPE_TIFF;
0117     } else if (ptype == QLatin1String("bmp")) {
0118         filetype = LIBMTP_FILETYPE_BMP;
0119     } else if (ptype == QLatin1String("gif")) {
0120         filetype = LIBMTP_FILETYPE_GIF;
0121     } else if (ptype == QLatin1String("pic") || ptype == QLatin1String("pict")) {
0122         filetype = LIBMTP_FILETYPE_PICT;
0123     } else if (ptype == QLatin1String("png")) {
0124         filetype = LIBMTP_FILETYPE_PNG;
0125     } else if (ptype == QLatin1String("wmf")) {
0126         filetype = LIBMTP_FILETYPE_WINDOWSIMAGEFORMAT;
0127     } else if (ptype == QLatin1String("ics")) {
0128         filetype = LIBMTP_FILETYPE_VCALENDAR2;
0129     } else if (ptype == QLatin1String("exe") || ptype == QLatin1String("com") || ptype == QLatin1String("bat") || ptype == QLatin1String("dll")
0130                || ptype == QLatin1String("sys")) {
0131         filetype = LIBMTP_FILETYPE_WINEXEC;
0132     } else if (ptype == QLatin1String("aac")) {
0133         filetype = LIBMTP_FILETYPE_AAC;
0134     } else if (ptype == QLatin1String("mp2")) {
0135         filetype = LIBMTP_FILETYPE_MP2;
0136     } else if (ptype == QLatin1String("flac")) {
0137         filetype = LIBMTP_FILETYPE_FLAC;
0138     } else if (ptype == QLatin1String("m4a")) {
0139         filetype = LIBMTP_FILETYPE_M4A;
0140     } else if (ptype == QLatin1String("doc")) {
0141         filetype = LIBMTP_FILETYPE_DOC;
0142     } else if (ptype == QLatin1String("xml")) {
0143         filetype = LIBMTP_FILETYPE_XML;
0144     } else if (ptype == QLatin1String("xls")) {
0145         filetype = LIBMTP_FILETYPE_XLS;
0146     } else if (ptype == QLatin1String("ppt")) {
0147         filetype = LIBMTP_FILETYPE_PPT;
0148     } else if (ptype == QLatin1String("mht")) {
0149         filetype = LIBMTP_FILETYPE_MHT;
0150     } else if (ptype == QLatin1String("jp2")) {
0151         filetype = LIBMTP_FILETYPE_JP2;
0152     } else if (ptype == QLatin1String("jpx")) {
0153         filetype = LIBMTP_FILETYPE_JPX;
0154     } else if (ptype == QLatin1String("bin")) {
0155         filetype = LIBMTP_FILETYPE_FIRMWARE;
0156     } else if (ptype == QLatin1String("vcf")) {
0157         filetype = LIBMTP_FILETYPE_VCARD3;
0158     } else {
0159         /* Tagging as unknown file type */
0160         filetype = LIBMTP_FILETYPE_UNKNOWN;
0161     }
0162 
0163     return filetype;
0164 }
0165 
0166 MTPStorage::MTPStorage(const QString &dbusObjectPath, const LIBMTP_devicestorage_t *mtpStorage, MTPDevice *parent)
0167     : QObject(parent)
0168     , m_dbusObjectPath(dbusObjectPath)
0169 {
0170     setStorageProperties(mtpStorage);
0171 
0172     qDBusRegisterMetaType<KMTPFile>();
0173     qDBusRegisterMetaType<KMTPFileList>();
0174 
0175     new StorageAdaptor(this);
0176     QDBusConnection::sessionBus().registerObject(m_dbusObjectPath, this);
0177 }
0178 
0179 QString MTPStorage::dbusObjectPath() const
0180 {
0181     return m_dbusObjectPath;
0182 }
0183 
0184 QString MTPStorage::description() const
0185 {
0186     return m_description;
0187 }
0188 
0189 quint64 MTPStorage::maxCapacity() const
0190 {
0191     return m_maxCapacity;
0192 }
0193 
0194 quint64 MTPStorage::freeSpaceInBytes()
0195 {
0196     updateStorageInfo();
0197     return m_freeSpaceInBytes;
0198 }
0199 
0200 void MTPStorage::setStorageProperties(const LIBMTP_devicestorage_t *storage)
0201 {
0202     m_id = storage->id;
0203     m_maxCapacity = storage->MaxCapacity;
0204     m_freeSpaceInBytes = storage->FreeSpaceInBytes;
0205     m_description = QString::fromUtf8(storage->StorageDescription);
0206 }
0207 
0208 void MTPStorage::updateStorageInfo()
0209 {
0210     if (!LIBMTP_Get_Storage(getDevice(), LIBMTP_STORAGE_SORTBY_NOTSORTED)) {
0211         for (const LIBMTP_devicestorage_t *storage = getDevice()->storage; storage != nullptr; storage = storage->next) {
0212             if (m_id == storage->id) {
0213                 qCDebug(LOG_KIOD_KMTPD) << "storage info updated";
0214                 setStorageProperties(storage);
0215                 break;
0216             }
0217         }
0218     }
0219 }
0220 
0221 LIBMTP_mtpdevice_t *MTPStorage::getDevice() const
0222 {
0223     return qobject_cast<MTPDevice *>(parent())->getDevice();
0224 }
0225 
0226 KMTPFile MTPStorage::getFileFromPath(const QString &path)
0227 {
0228     const QStringList pathItems = path.split(QLatin1Char('/'), Qt::SkipEmptyParts);
0229 
0230     // don't handle the root directory
0231     if (!pathItems.isEmpty()) {
0232         // 1. check if the file is in the cache
0233         const auto itemId = queryPath(path);
0234         if (itemId.has_value()) {
0235             qCDebug(LOG_KIOD_KMTPD) << "Match found in cache, checking device";
0236 
0237             std::unique_ptr<LIBMTP_file_t> file(LIBMTP_Get_Filemetadata(getDevice(), itemId.value()));
0238             if (file) {
0239                 qCDebug(LOG_KIOD_KMTPD) << "Found file in cache";
0240                 return createKMTPFile(file);
0241             }
0242         }
0243 
0244         // 2. query cache for parent
0245         else if (pathItems.size() > 1) {
0246             QString parentPath = convertToPath(pathItems, pathItems.size() - 1);
0247             const auto parentId = queryPath(parentPath);
0248 
0249             if (parentId.has_value()) {
0250                 qCDebug(LOG_KIOD_KMTPD) << "Match for parent found in cache, checking device. Parent id = " << parentId.value();
0251 
0252                 std::unique_ptr<LIBMTP_file_t> parent(LIBMTP_Get_Filemetadata(getDevice(), parentId.value()));
0253                 if (parent) {
0254                     qCDebug(LOG_KIOD_KMTPD) << "Found parent in cache";
0255 
0256                     const KMTPFileList list = getFilesAndFoldersCached(parentPath, parentId.value());
0257                     const auto it = std::find_if(list.constBegin(), list.constEnd(), [pathItems](const KMTPFile &file) {
0258                         return file.filename() == pathItems.last();
0259                     });
0260 
0261                     if (it != list.constEnd()) {
0262                         qCDebug(LOG_KIOD_KMTPD) << "Found file from cached parent";
0263                         return *it;
0264                     }
0265                 }
0266             }
0267         }
0268     }
0269 
0270     // 3. traverse further while depth not reached
0271     QString currentPath;
0272     quint32 currentParent = LIBMTP_FILES_AND_FOLDERS_ROOT;
0273 
0274     for (const QString &element : pathItems) {
0275         std::optional<KMTPFile> optionalFile = findEntry(element, currentPath, currentParent);
0276         if (!optionalFile.has_value()) {
0277             qCDebug(LOG_KIOD_KMTPD) << "File not found!";
0278             return {};
0279         }
0280 
0281         currentParent = optionalFile->itemId();
0282         currentPath.append(QLatin1Char('/') + element);
0283     }
0284 
0285     std::unique_ptr<LIBMTP_file_t> file(LIBMTP_Get_Filemetadata(getDevice(), currentParent));
0286     if (file) {
0287         qCDebug(LOG_KIOD_KMTPD) << "Found file using tree walk";
0288 
0289         return createKMTPFile(file);
0290     }
0291     return {};
0292 }
0293 
0294 KMTPFileList MTPStorage::getFilesAndFoldersCached(const QString &path, quint32 parentId)
0295 {
0296     KMTPFileList mtpFiles;
0297 
0298     std::unique_ptr<LIBMTP_file_t> file(LIBMTP_Get_Files_And_Folders(getDevice(), m_id, parentId));
0299     while (file != nullptr) {
0300         const KMTPFile mtpFile = createKMTPFile(file);
0301         addPath(path + QLatin1Char('/') + mtpFile.filename(), mtpFile.itemId());
0302         mtpFiles.append(mtpFile);
0303 
0304         file.reset(file->next);
0305     }
0306     return mtpFiles;
0307 }
0308 
0309 #if defined(HAVE_LIBMTP_Get_Children)
0310 std::optional<KMTPFile> MTPStorage::findEntry(const QString &fileNeedle, const QString &parentPath, quint32 parentId)
0311 {
0312     // Optimized version of finding. Using LIBMTP_Get_Children to get a list of all ids relatively quickly and then
0313     // iterate them for the needle - leading to an early return when the needle is found, and partial caching!
0314     uint32_t *children = nullptr;
0315     const auto childrenSize = LIBMTP_Get_Children(getDevice(), m_id, parentId, &children);
0316     if (childrenSize == -1) {
0317         return std::nullopt;
0318     }
0319     const std::unique_ptr<uint32_t> childrenOwner(children);
0320 
0321     for (const auto &child : std::span<uint32_t>(children, childrenSize)) {
0322         const KMTPFile mtpFile = createKMTPFile(std::unique_ptr<LIBMTP_file_t>(LIBMTP_Get_Filemetadata(getDevice(), child)));
0323         addPath(parentPath + QLatin1Char('/') + mtpFile.filename(), mtpFile.itemId());
0324         if (mtpFile.filename() == fileNeedle) {
0325             return mtpFile;
0326         }
0327     }
0328 
0329     return std::nullopt;
0330 }
0331 #else
0332 std::optional<KMTPFile> MTPStorage::findEntry(const QString &fileNeedle, const QString &parentPath, quint32 parentId)
0333 {
0334     // Poor man's search function. This gets all metadata in advance meaning even if the needle is the first of N
0335     // entries we'll have gotten all N entries.
0336     const KMTPFileList list = getFilesAndFoldersCached(parentPath, parentId);
0337     auto it = std::find_if(list.constBegin(), list.constEnd(), [fileNeedle](const KMTPFile &file) {
0338         return file.filename() == fileNeedle;
0339     });
0340     if (it == list.cend()) {
0341         return std::nullopt;
0342     }
0343     return *it;
0344 }
0345 #endif
0346 
0347 std::optional<quint32> MTPStorage::queryPath(const QString &path, int timeToLive)
0348 {
0349     QPair<QDateTime, uint32_t> item = m_cache.value(path);
0350 
0351     if (item.second != 0) {
0352         QDateTime dateTime = QDateTime::currentDateTimeUtc();
0353 
0354         if (item.first > dateTime) {
0355             item.first = dateTime.addSecs(timeToLive);
0356             m_cache.insert(path, item);
0357             return item.second;
0358         }
0359         m_cache.remove(path);
0360         return std::nullopt;
0361     }
0362 
0363     return std::nullopt;
0364 }
0365 
0366 void MTPStorage::addPath(const QString &path, quint32 id, int timeToLive)
0367 {
0368     QDateTime dateTime = QDateTime::currentDateTimeUtc();
0369     dateTime = dateTime.addSecs(timeToLive);
0370 
0371     QPair<QDateTime, uint32_t> item(dateTime, id);
0372 
0373     m_cache.insert(path, item);
0374 }
0375 
0376 void MTPStorage::removePath(const QString &path)
0377 {
0378     m_cache.remove(path);
0379 }
0380 
0381 KMTPFileList MTPStorage::getFilesAndFolders(const QString &path, int &result)
0382 {
0383     result = 0;
0384     if (path.isEmpty() || path == QLatin1String("/")) {
0385         // list root directory
0386         return getFilesAndFoldersCached(path, LIBMTP_FILES_AND_FOLDERS_ROOT);
0387     }
0388     const KMTPFile file = getFileFromPath(path);
0389     if (!file.isValid()) {
0390         result = 1; // not existing
0391         return {};
0392     }
0393     if (!file.isFolder()) {
0394         result = 2; // is file
0395         return {};
0396     }
0397     return getFilesAndFoldersCached(path, file.itemId());
0398 }
0399 
0400 KMTPFile MTPStorage::getFileMetadata(const QString &path)
0401 {
0402     qCDebug(LOG_KIOD_KMTPD) << "getFileMetadata:" << path;
0403     return getFileFromPath(path);
0404 }
0405 
0406 int MTPStorage::getFileToHandler(const QString &path)
0407 {
0408     qCDebug(LOG_KIOD_KMTPD) << "getFileToHandler:" << path;
0409 
0410     const KMTPFile source = getFileMetadata(path);
0411     if (source.isValid()) {
0412         const quint32 itemId = source.itemId();
0413         QTimer::singleShot(0, this, [this, itemId] {
0414             const int result = LIBMTP_Get_File_To_Handler(getDevice(), itemId, onDataPut, this, onDataProgress, this);
0415             if (result) {
0416                 LIBMTP_Dump_Errorstack(getDevice());
0417                 LIBMTP_Clear_Errorstack(getDevice());
0418             }
0419             Q_EMIT copyFinished(result);
0420         });
0421         return 0;
0422     }
0423     return 1;
0424 }
0425 
0426 int MTPStorage::getFileToFileDescriptor(const QDBusUnixFileDescriptor &descriptor, const QString &sourcePath)
0427 {
0428     qCDebug(LOG_KIOD_KMTPD) << "getFileToFileDescriptor:" << sourcePath;
0429 
0430     const KMTPFile source = getFileMetadata(sourcePath);
0431     if (!source.isValid()) {
0432         return 1;
0433     }
0434 
0435     const quint32 itemId = source.itemId();
0436 
0437     // big files take some time to copy, and this may lead into D-Bus timeouts.
0438     // therefore the actual copying is not done within the D-Bus method itself but right after we return to the event loop
0439     QTimer::singleShot(0, this, [this, itemId, descriptor] {
0440         const int result = LIBMTP_Get_File_To_File_Descriptor(getDevice(), itemId, descriptor.fileDescriptor(), onDataProgress, this);
0441         if (result) {
0442             LIBMTP_Dump_Errorstack(getDevice());
0443             LIBMTP_Clear_Errorstack(getDevice());
0444         }
0445         Q_EMIT copyFinished(result);
0446     });
0447     return 0;
0448 }
0449 
0450 int MTPStorage::sendFileFromFileDescriptor(const QDBusUnixFileDescriptor &descriptor, const QString &destinationPath)
0451 {
0452     qCDebug(LOG_KIOD_KMTPD) << "sendFileFromFileDescriptor:" << destinationPath;
0453 
0454     QStringList destItems = destinationPath.split(QLatin1Char('/'), Qt::SkipEmptyParts);
0455     if (destItems.isEmpty()) {
0456         return 1;
0457     }
0458 
0459     const QString filename = destItems.takeLast();
0460     uint32_t parentId = LIBMTP_FILES_AND_FOLDERS_ROOT;
0461 
0462     if (!destItems.isEmpty()) {
0463         // not root folder, search for parent folder
0464         const KMTPFile parent = getFileMetadata(convertToPath(destItems, destItems.size()));
0465         parentId = parent.itemId();
0466         if (!parent.isFolder()) {
0467             return 2;
0468         }
0469     }
0470 
0471     QTimer::singleShot(0, this, [this, parentId, descriptor, filename] {
0472         int result = 1;
0473         QT_STATBUF srcBuf{};
0474         if (QT_FSTAT(descriptor.fileDescriptor(), &srcBuf) != -1) {
0475             const QDateTime lastModified = QDateTime::fromSecsSinceEpoch(srcBuf.st_mtim.tv_sec);
0476 
0477             std::unique_ptr<LIBMTP_file_t> file(LIBMTP_new_file_t());
0478             file->parent_id = parentId;
0479             file->filename = qstrdup(filename.toUtf8().data());
0480             file->filetype = getFiletype(filename);
0481             file->filesize = quint64(srcBuf.st_size);
0482             file->modificationdate = lastModified.toSecsSinceEpoch(); // no matter what to set here, current time is taken
0483             file->storage_id = m_id;
0484 
0485             QT_LSEEK(descriptor.fileDescriptor(), 0, SEEK_SET); // make sure we are at the beginning of the file, the worker may leave it sitting at the end
0486             result = LIBMTP_Send_File_From_File_Descriptor(getDevice(), descriptor.fileDescriptor(), file.get(), onDataProgress, this);
0487 
0488             if (result) {
0489                 LIBMTP_Dump_Errorstack(getDevice());
0490                 LIBMTP_Clear_Errorstack(getDevice());
0491             }
0492         }
0493         Q_EMIT copyFinished(result);
0494     });
0495 
0496     return 0;
0497 }
0498 
0499 int MTPStorage::setFileName(const QString &path, const QString &newName)
0500 {
0501     qCDebug(LOG_KIOD_KMTPD) << "setFileName:" << path << newName;
0502 
0503     const KMTPFile file = getFileFromPath(path);
0504     if (!file.isValid()) {
0505         return 1;
0506     }
0507 
0508     std::unique_ptr<LIBMTP_file_t> source(LIBMTP_Get_Filemetadata(getDevice(), file.itemId()));
0509     if (!source) {
0510         return 1;
0511     }
0512 
0513     const int result = LIBMTP_Set_File_Name(getDevice(), source.get(), newName.toUtf8().constData());
0514     if (result == 0) {
0515         removePath(path);
0516     }
0517     return result;
0518 }
0519 
0520 quint32 MTPStorage::createFolder(const QString &path)
0521 {
0522     qCDebug(LOG_KIOD_KMTPD) << "createFolder:" << path;
0523 
0524     quint32 folderId = 0;
0525     const QStringList pathItems = path.split(QLatin1Char('/'), Qt::SkipEmptyParts);
0526     const auto destinationId = queryPath(path);
0527 
0528     if (!pathItems.isEmpty() && !destinationId.has_value()) {
0529         QByteArray dirName = pathItems.last().toUtf8();
0530 
0531         if (pathItems.size() == 1) {
0532             // create folder in device root
0533             folderId = LIBMTP_Create_Folder(getDevice(), dirName.data(), LIBMTP_FILES_AND_FOLDERS_ROOT, m_id);
0534 
0535         } else {
0536             const KMTPFile parentFolder = getFileMetadata(path.section(QLatin1Char('/'), 0, -2, QString::SectionIncludeLeadingSep));
0537             if (parentFolder.isFolder()) {
0538                 folderId = LIBMTP_Create_Folder(getDevice(), dirName.data(), parentFolder.itemId(), m_id);
0539             }
0540         }
0541 
0542         if (folderId) {
0543             LIBMTP_Dump_Errorstack(getDevice());
0544             LIBMTP_Clear_Errorstack(getDevice());
0545         } else {
0546             addPath(path, folderId);
0547         }
0548     }
0549     return folderId;
0550 }
0551 
0552 int MTPStorage::deleteObject(const QString &path)
0553 {
0554     qCDebug(LOG_KIOD_KMTPD) << "deleteObject:" << path;
0555 
0556     const KMTPFile file = getFileMetadata(path);
0557     const int ret = LIBMTP_Delete_Object(getDevice(), file.itemId());
0558     if (!ret) {
0559         removePath(path);
0560     }
0561     return ret;
0562 }
0563 
0564 QDBusObjectPath MTPStorage::getFilesAndFolders2(const QString &path)
0565 {
0566 #if defined(HAVE_LIBMTP_Get_Children)
0567     qint64 idToList = LIBMTP_FILES_AND_FOLDERS_ROOT;
0568     if (!path.isEmpty() && path != QLatin1String("/")) {
0569         const KMTPFile file = getFileFromPath(path);
0570         if (!file.isValid()) {
0571             sendErrorReply(dbusErrorNOENT(), path);
0572             return {};
0573         }
0574         if (!file.isFolder()) {
0575             sendErrorReply(dbusErrorNOTDIR(), path);
0576             return {};
0577         }
0578         idToList = file.itemId();
0579     }
0580 
0581     uint32_t *children = nullptr;
0582     const auto childrenSize = LIBMTP_Get_Children(getDevice(), m_id, idToList, &children);
0583     if (childrenSize == -1) {
0584         sendErrorReply(dbusErrorNOENT(), path);
0585         return {};
0586     }
0587 
0588     auto lister = new MTPLister(std::unique_ptr<uint32_t>(children), childrenSize, getDevice(), path, this);
0589     connect(lister, &MTPLister::entry, this, [this, path](const KMTPFile &file) {
0590         addPath(path + QLatin1Char('/') + file.filename(), file.itemId()); // add to cache
0591     });
0592     static quint64 id = 0;
0593     const QDBusObjectPath objectPath(QStringLiteral("/modules/kmtpd/Lister/%1").arg(id++));
0594     connection().registerObject(objectPath.path(), lister);
0595     return objectPath;
0596 #else
0597     sendErrorReply(dbusErrorENOSYS(), QString::fromLatin1(Q_FUNC_INFO));
0598     return {};
0599 #endif
0600 }
0601 
0602 #include "moc_mtpstorage.cpp"