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"