File indexing completed on 2025-01-19 03:55:51
0001 /* ============================================================ 0002 * 0003 * This file is a part of digiKam project 0004 * https://www.digikam.org 0005 * 0006 * Date : 2015-06-15 0007 * Description : IO Jobs for file systems jobs 0008 * 0009 * SPDX-FileCopyrightText: 2015 by Mohamed_Anwer <m_dot_anwer at gmx dot com> 0010 * SPDX-FileCopyrightText: 2018 by Maik Qualmann <metzpinguin at gmail dot com> 0011 * 0012 * SPDX-License-Identifier: GPL-2.0-or-later 0013 * 0014 * ============================================================ */ 0015 0016 #include "iojob.h" 0017 0018 // Qt includes 0019 0020 #include <QDir> 0021 #include <QFile> 0022 0023 // KDE includes 0024 0025 #include <klocalizedstring.h> 0026 0027 // Local includes 0028 0029 #include "digikam_debug.h" 0030 #include "iteminfo.h" 0031 #include "dtrash.h" 0032 #include "coredb.h" 0033 #include "coredbaccess.h" 0034 #include "albummanager.h" 0035 #include "dfileoperations.h" 0036 #include "collectionmanager.h" 0037 #include "coredboperationgroup.h" 0038 0039 namespace Digikam 0040 { 0041 0042 IOJob::IOJob() 0043 { 0044 } 0045 0046 // -------------------------------------------- 0047 0048 CopyOrMoveJob::CopyOrMoveJob(IOJobData* const data) 0049 : m_data(data) 0050 { 0051 } 0052 0053 void CopyOrMoveJob::run() 0054 { 0055 while (m_data && !m_cancel) 0056 { 0057 QUrl srcUrl = m_data->getNextUrl(); 0058 0059 if (srcUrl.isEmpty()) 0060 { 0061 break; 0062 } 0063 0064 QFileInfo srcInfo(srcUrl.adjusted(QUrl::StripTrailingSlash).toLocalFile()); 0065 QDir dstDir(m_data->destUrl().toLocalFile()); 0066 QString srcName = srcInfo.fileName(); 0067 0068 if (!srcInfo.exists()) 0069 { 0070 Q_EMIT signalError(i18n("File/Folder %1 does not exist anymore", srcName)); 0071 continue; 0072 } 0073 0074 if (!dstDir.exists()) 0075 { 0076 Q_EMIT signalError(i18n("Album %1 does not exist anymore", dstDir.dirName())); 0077 continue; 0078 } 0079 0080 // Checking if there is a file with the same name in destination folder 0081 0082 QString destenation = dstDir.path() + QLatin1Char('/') + srcName; 0083 0084 if (QFileInfo::exists(destenation)) 0085 { 0086 if (m_data->fileConflict() == IOJobData::Overwrite) 0087 { 0088 if (srcInfo.isDir()) 0089 { 0090 continue; 0091 } 0092 else 0093 { 0094 if (srcInfo.filePath() == destenation) 0095 { 0096 continue; 0097 } 0098 0099 if (m_data->operation() == IOJobData::CopyToExt) 0100 { 0101 if (!QFile::remove(destenation)) 0102 { 0103 Q_EMIT signalError(i18n("Could not overwrite image %1", 0104 srcName)); 0105 0106 continue; 0107 } 0108 } 0109 else if (!DTrash::deleteImage(destenation, m_data->jobTime())) 0110 { 0111 Q_EMIT signalError(i18n("Could not move image %1 to collection trash.\n" 0112 "Check the file permission on the trash folder " 0113 "\"%2\" in the image collection.", 0114 srcName, DTrash::TRASH_FOLDER)); 0115 0116 continue; 0117 } 0118 } 0119 } 0120 else if (m_data->fileConflict() == IOJobData::AutoRename) 0121 { 0122 QUrl destUrl = QUrl::fromLocalFile(destenation); 0123 0124 if (srcInfo.isDir()) 0125 { 0126 QUrl renamed = DFileOperations::getUniqueFolderUrl(destUrl); 0127 destenation = renamed.toLocalFile(); 0128 m_data->setDestUrl(srcUrl, renamed); 0129 } 0130 else 0131 { 0132 QUrl renamed = DFileOperations::getUniqueFileUrl(destUrl); 0133 destenation = renamed.toLocalFile(); 0134 m_data->setDestUrl(srcUrl, renamed); 0135 } 0136 } 0137 else 0138 { 0139 Q_EMIT signalError(i18n("A file or folder named %1 already exists in %2", 0140 srcName, QDir::toNativeSeparators(dstDir.path()))); 0141 0142 continue; 0143 } 0144 } 0145 0146 if ((m_data->operation() == IOJobData::MoveAlbum) || 0147 (m_data->operation() == IOJobData::MoveImage) || 0148 (m_data->operation() == IOJobData::MoveFiles)) 0149 { 0150 if (srcInfo.isDir()) 0151 { 0152 QDir srcDir(srcInfo.filePath()); 0153 0154 if (!srcDir.rename(srcDir.path(), destenation)) 0155 { 0156 // If QDir::rename fails, try copy and remove. 0157 0158 if (!DFileOperations::copyFolderRecursively(srcDir.path(), destenation, 0159 m_data->getProgressId(), 0160 &m_cancel, true)) 0161 { 0162 m_data->setErrorOrCancel(true); 0163 0164 if (m_cancel) 0165 { 0166 break; 0167 } 0168 0169 Q_EMIT signalError(i18n("Could not move folder %1 to album %2", 0170 srcName, QDir::toNativeSeparators(dstDir.path()))); 0171 0172 continue; 0173 } 0174 else if (!srcDir.removeRecursively()) 0175 { 0176 Q_EMIT signalError(i18n("Could not move folder %1 to album %2. " 0177 "The folder %1 was copied as well to album %2", 0178 srcName, QDir::toNativeSeparators(dstDir.path()))); 0179 } 0180 } 0181 } 0182 else 0183 { 0184 // move the possible sidecar files first 0185 0186 if (!DFileOperations::sidecarFiles(srcInfo.filePath(), destenation, DFileOperations::Rename)) 0187 { 0188 Q_EMIT signalError(i18n("Could not move sidecar from file %1 to album %2", 0189 srcName, QDir::toNativeSeparators(dstDir.path()))); 0190 } 0191 0192 if (!DFileOperations::renameFile(srcInfo.filePath(), destenation)) 0193 { 0194 Q_EMIT signalError(i18n("Could not move file %1 to album %2", 0195 srcName, QDir::toNativeSeparators(dstDir.path()))); 0196 0197 continue; 0198 } 0199 } 0200 } 0201 else 0202 { 0203 if (srcInfo.isDir()) 0204 { 0205 QDir srcDir(srcInfo.filePath()); 0206 0207 if (!DFileOperations::copyFolderRecursively(srcDir.path(), destenation, 0208 m_data->getProgressId(), 0209 &m_cancel, true)) 0210 { 0211 m_data->setErrorOrCancel(true); 0212 0213 if (m_cancel) 0214 { 0215 break; 0216 } 0217 0218 Q_EMIT signalError(i18n("Could not copy folder %1 to album %2", 0219 srcName, QDir::toNativeSeparators(dstDir.path()))); 0220 0221 continue; 0222 } 0223 } 0224 else 0225 { 0226 // copy the possible sidecar files first 0227 0228 if (!DFileOperations::sidecarFiles(srcInfo.filePath(), destenation, DFileOperations::Copy)) 0229 { 0230 Q_EMIT signalError(i18n("Could not copy sidecar from file %1 to folder %2", 0231 srcName, QDir::toNativeSeparators(dstDir.path()))); 0232 } 0233 0234 if (!DFileOperations::copyFile(srcInfo.filePath(), destenation)) 0235 { 0236 if (m_data->operation() == IOJobData::CopyToExt) 0237 { 0238 Q_EMIT signalError(i18n("Could not copy file %1 to folder %2", 0239 srcName, QDir::toNativeSeparators(dstDir.path()))); 0240 } 0241 else 0242 { 0243 Q_EMIT signalError(i18n("Could not copy file %1 to album %2", 0244 srcName, QDir::toNativeSeparators(dstDir.path()))); 0245 } 0246 0247 continue; 0248 } 0249 } 0250 } 0251 0252 Q_EMIT signalOneProccessed(srcUrl); 0253 } 0254 0255 m_data->setErrorOrCancel(m_cancel); 0256 0257 Q_EMIT signalDone(); 0258 } 0259 0260 // -------------------------------------------- 0261 0262 DeleteJob::DeleteJob(IOJobData* const data) 0263 : m_data(data) 0264 { 0265 } 0266 0267 void DeleteJob::run() 0268 { 0269 while (m_data && !m_cancel) 0270 { 0271 QUrl deleteUrl = m_data->getNextUrl(); 0272 0273 if (deleteUrl.isEmpty()) 0274 { 0275 break; 0276 } 0277 0278 bool useTrash = (m_data->operation() == IOJobData::Trash); 0279 0280 QFileInfo fileInfo(deleteUrl.toLocalFile()); 0281 qCDebug(DIGIKAM_IOJOB_LOG) << "Deleting: " << fileInfo.filePath(); 0282 qCDebug(DIGIKAM_IOJOB_LOG) << "File exists?" << fileInfo.exists(); 0283 qCDebug(DIGIKAM_IOJOB_LOG) << "Is to trash?" << useTrash; 0284 0285 if (!fileInfo.exists()) 0286 { 0287 Q_EMIT signalError(i18n("File/Folder %1 does not exist", 0288 QDir::toNativeSeparators(fileInfo.filePath()))); 0289 0290 continue; 0291 } 0292 0293 if (useTrash) 0294 { 0295 if (fileInfo.isDir()) 0296 { 0297 if (!DTrash::deleteDirRecursivley(deleteUrl.toLocalFile(), m_data->jobTime())) 0298 { 0299 Q_EMIT signalError(i18n("Could not move folder %1 to collection trash.\n" 0300 "Check the file permission on the trash folder " 0301 "\".dtrash\" in the image collection.", 0302 QDir::toNativeSeparators(fileInfo.path()))); 0303 0304 continue; 0305 } 0306 } 0307 else 0308 { 0309 if (!DTrash::deleteImage(deleteUrl.toLocalFile(), m_data->jobTime())) 0310 { 0311 Q_EMIT signalError(i18n("Could not move image %1 to collection trash.\n" 0312 "Check the file permission on the trash folder " 0313 "\".dtrash\" in the image collection.", 0314 QDir::toNativeSeparators(fileInfo.filePath()))); 0315 0316 continue; 0317 } 0318 } 0319 } 0320 else 0321 { 0322 if (fileInfo.isDir()) 0323 { 0324 QDir dir(fileInfo.filePath()); 0325 0326 if (!dir.removeRecursively()) 0327 { 0328 Q_EMIT signalError(i18n("Album %1 could not be removed", 0329 QDir::toNativeSeparators(fileInfo.path()))); 0330 0331 continue; 0332 } 0333 } 0334 else 0335 { 0336 QFile file(fileInfo.filePath()); 0337 0338 if (!file.remove()) 0339 { 0340 Q_EMIT signalError(i18n("Image %1 could not be removed", 0341 QDir::toNativeSeparators(fileInfo.filePath()))); 0342 0343 continue; 0344 } 0345 } 0346 } 0347 0348 Q_EMIT signalOneProccessed(deleteUrl); 0349 } 0350 0351 Q_EMIT signalDone(); 0352 } 0353 0354 // -------------------------------------------- 0355 0356 RenameFileJob::RenameFileJob(IOJobData* const data) 0357 : m_data(data) 0358 { 0359 } 0360 0361 void RenameFileJob::run() 0362 { 0363 while (m_data && !m_cancel) 0364 { 0365 QUrl renameUrl = m_data->getNextUrl(); 0366 0367 if (renameUrl.isEmpty()) 0368 { 0369 break; 0370 } 0371 0372 QUrl destUrl = m_data->destUrl(renameUrl); 0373 QFileInfo fileInfo(destUrl.toLocalFile()); 0374 0375 QDir dir(fileInfo.dir()); 0376 const QStringList& dirList = dir.entryList(QDir::Dirs | 0377 QDir::Files | 0378 QDir::NoDotAndDotDot); 0379 0380 if (dirList.contains(fileInfo.fileName())) 0381 { 0382 if (m_data->fileConflict() == IOJobData::Overwrite) 0383 { 0384 if (!DTrash::deleteImage(destUrl.toLocalFile(), m_data->jobTime())) 0385 { 0386 Q_EMIT signalError(i18n("Could not move image %1 to collection trash", 0387 QDir::toNativeSeparators(destUrl.toLocalFile()))); 0388 0389 Q_EMIT signalRenameFailed(renameUrl); 0390 continue; 0391 } 0392 } 0393 else 0394 { 0395 qCDebug(DIGIKAM_IOJOB_LOG) << "File with the same name exists!"; 0396 Q_EMIT signalError(i18n("Image with the same name %1 already there", 0397 QDir::toNativeSeparators(destUrl.toLocalFile()))); 0398 0399 Q_EMIT signalRenameFailed(renameUrl); 0400 continue; 0401 } 0402 } 0403 0404 qCDebug(DIGIKAM_IOJOB_LOG) << "Trying to rename" 0405 << renameUrl.toLocalFile() << "to" 0406 << destUrl.toLocalFile(); 0407 0408 if (!DFileOperations::renameFile(renameUrl.toLocalFile(), destUrl.toLocalFile())) 0409 { 0410 qCDebug(DIGIKAM_IOJOB_LOG) << "File could not be renamed!"; 0411 Q_EMIT signalError(i18n("Image %1 could not be renamed", 0412 QDir::toNativeSeparators(renameUrl.toLocalFile()))); 0413 0414 Q_EMIT signalRenameFailed(renameUrl); 0415 continue; 0416 } 0417 0418 Q_EMIT signalOneProccessed(renameUrl); 0419 } 0420 0421 Q_EMIT signalDone(); 0422 } 0423 0424 // ---------------------------------------------- 0425 0426 DTrashItemsListingJob::DTrashItemsListingJob(const QString& collectionPath) 0427 : m_collectionPath(collectionPath) 0428 { 0429 } 0430 0431 void DTrashItemsListingJob::run() 0432 { 0433 DTrashItemInfo itemInfo; 0434 0435 QString collectionTrashFilesPath = m_collectionPath + QLatin1Char('/') + 0436 DTrash::TRASH_FOLDER + QLatin1Char('/') + 0437 DTrash::FILES_FOLDER; 0438 0439 qCDebug(DIGIKAM_IOJOB_LOG) << "Collection trash files path:" << collectionTrashFilesPath; 0440 0441 QDir filesDir(collectionTrashFilesPath); 0442 0443 Q_FOREACH (const QFileInfo& fileInfo, filesDir.entryInfoList(QDir::Files)) 0444 { 0445 if (m_cancel) 0446 { 0447 break; 0448 } 0449 0450 qCDebug(DIGIKAM_IOJOB_LOG) << "File in trash:" << fileInfo.filePath(); 0451 itemInfo.trashPath = fileInfo.filePath(); 0452 0453 DTrash::extractJsonForItem(m_collectionPath, fileInfo.baseName(), itemInfo); 0454 0455 Q_EMIT trashItemInfo(itemInfo); 0456 } 0457 0458 Q_EMIT signalDone(); 0459 } 0460 0461 // ---------------------------------------------- 0462 0463 RestoreDTrashItemsJob::RestoreDTrashItemsJob(IOJobData* const data) 0464 : m_data(data) 0465 { 0466 } 0467 0468 void RestoreDTrashItemsJob::run() 0469 { 0470 if (!m_data) 0471 { 0472 return; 0473 } 0474 0475 Q_FOREACH (const DTrashItemInfo& item, m_data->trashItems()) 0476 { 0477 QUrl srcToRename = QUrl::fromLocalFile(item.collectionPath); 0478 QUrl newName = DFileOperations::getUniqueFileUrl(srcToRename); 0479 0480 QFileInfo fi(item.collectionPath); 0481 0482 if (!fi.dir().exists()) 0483 { 0484 fi.dir().mkpath(fi.dir().path()); 0485 } 0486 0487 if (!QFile::rename(item.trashPath, newName.toLocalFile())) 0488 { 0489 Q_EMIT signalError(i18n("Could not restore file %1 from trash", 0490 QDir::toNativeSeparators(newName.toLocalFile()))); 0491 } 0492 else 0493 { 0494 QFile::remove(item.jsonFilePath); 0495 } 0496 0497 Q_EMIT signalOneProccessed(newName); 0498 } 0499 0500 Q_EMIT signalDone(); 0501 } 0502 0503 // ---------------------------------------------- 0504 0505 EmptyDTrashItemsJob::EmptyDTrashItemsJob(IOJobData* const data) 0506 : m_data(data) 0507 { 0508 } 0509 0510 void EmptyDTrashItemsJob::run() 0511 { 0512 if (!m_data) 0513 { 0514 return; 0515 } 0516 0517 QList<int> albumsFromImages; 0518 QList<qlonglong> imagesToRemove; 0519 0520 Q_FOREACH (const DTrashItemInfo& item, m_data->trashItems()) 0521 { 0522 QFile::remove(item.trashPath); 0523 QFile::remove(item.jsonFilePath); 0524 0525 imagesToRemove << item.imageId; 0526 albumsFromImages << ItemInfo(item.imageId).albumId(); 0527 0528 Q_EMIT signalOneProccessed(QUrl()); 0529 } 0530 0531 { 0532 CoreDbOperationGroup group; 0533 group.setMaximumTime(200); 0534 0535 CoreDbAccess().db()->removeItemsPermanently(imagesToRemove, albumsFromImages); 0536 } 0537 0538 Q_EMIT signalDone(); 0539 } 0540 0541 // ---------------------------------------------- 0542 0543 BuildTrashCountersJob::BuildTrashCountersJob() 0544 { 0545 } 0546 0547 void BuildTrashCountersJob::run() 0548 { 0549 QMap<QString, int> trashCountersMap; 0550 QList<CollectionLocation> allLocations = CollectionManager::instance()->allAvailableLocations(); 0551 0552 Q_FOREACH (const CollectionLocation& location, allLocations) 0553 { 0554 QString path = location.albumRootPath() + QLatin1Char('/') + 0555 DTrash::TRASH_FOLDER + QLatin1Char('/') + 0556 DTrash::FILES_FOLDER; 0557 0558 QDir dir(path, QLatin1String(""), QDir::Unsorted, QDir::Files); 0559 0560 if (dir.exists() && dir.isReadable()) 0561 { 0562 trashCountersMap.insert(location.albumRootPath(), dir.count()); 0563 } 0564 else 0565 { 0566 trashCountersMap.insert(location.albumRootPath(), 0); 0567 } 0568 } 0569 0570 Q_EMIT signalTrashCountersMap(trashCountersMap); 0571 0572 Q_EMIT signalDone(); 0573 } 0574 0575 } // namespace Digikam 0576 0577 #include "moc_iojob.cpp"