File indexing completed on 2024-11-24 04:31:15

0001 /*
0002     SPDX-FileCopyrightText: 2005 Joris Guisson <joris.guisson@gmail.com>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 #include "movedatafilesjob.h"
0007 #include <KIO/FileCopyJob>
0008 #include <QFileInfo>
0009 #include <QUrl>
0010 #include <interfaces/torrentfileinterface.h>
0011 #include <kio/jobuidelegate.h>
0012 #include <kjobtrackerinterface.h>
0013 #include <klocalizedstring.h>
0014 #include <util/fileops.h>
0015 #include <util/functions.h>
0016 #include <util/log.h>
0017 
0018 namespace bt
0019 {
0020 static ResourceManager move_data_files_slot(1);
0021 
0022 MoveDataFilesJob::MoveDataFilesJob()
0023     : Job(true, nullptr)
0024     , Resource(&move_data_files_slot, "MoveDataFilesJob")
0025     , err(false)
0026     , active_job(nullptr)
0027     , running_recovery_jobs(0)
0028     , bytes_moved(0)
0029     , total_bytes(0)
0030     , bytes_moved_current_file(0)
0031 {
0032 }
0033 
0034 MoveDataFilesJob::MoveDataFilesJob(const QMap<TorrentFileInterface *, QString> &fmap)
0035     : Job(true, nullptr)
0036     , Resource(&move_data_files_slot, "MoveDataFilesJob")
0037     , err(false)
0038     , active_job(nullptr)
0039     , running_recovery_jobs(0)
0040     , bytes_moved(0)
0041     , total_bytes(0)
0042     , bytes_moved_current_file(0)
0043 {
0044     file_map = fmap;
0045     QMap<TorrentFileInterface *, QString>::const_iterator i = file_map.constBegin();
0046     while (i != file_map.constEnd()) {
0047         TorrentFileInterface *tf = i.key();
0048         QString dest = i.value();
0049         if (QFileInfo(dest).isDir()) {
0050             QString path = tf->getUserModifiedPath();
0051             if (!dest.endsWith(bt::DirSeparator()))
0052                 dest += bt::DirSeparator();
0053 
0054             int last = path.lastIndexOf(bt::DirSeparator());
0055             QString dst = dest + path.mid(last + 1);
0056             if (QFileInfo(tf->getPathOnDisk()).canonicalPath() != QFileInfo(dst).canonicalPath())
0057                 addMove(tf->getPathOnDisk(), dst);
0058         } else if (QFileInfo(tf->getPathOnDisk()).canonicalPath() != QFileInfo(i.value()).canonicalPath()) {
0059             addMove(tf->getPathOnDisk(), i.value());
0060         }
0061         ++i;
0062     }
0063 }
0064 
0065 MoveDataFilesJob::~MoveDataFilesJob()
0066 {
0067 }
0068 
0069 void MoveDataFilesJob::addMove(const QString &src, const QString &dst)
0070 {
0071     todo.insert(src, dst);
0072 }
0073 
0074 void MoveDataFilesJob::onJobDone(KJob *j)
0075 {
0076     if (j->error() || err) {
0077         if (!err)
0078             setError(KIO::ERR_INTERNAL);
0079 
0080         active_job = nullptr;
0081         if (j->error())
0082             ((KIO::Job *)j)->uiDelegate()->showErrorMessage();
0083 
0084         // shit happened cancel all previous moves
0085         err = true;
0086         recover(j->error() != KIO::ERR_FILE_ALREADY_EXIST && j->error() != KIO::ERR_IDENTICAL_FILES);
0087     } else {
0088         bytes_moved += bytes_moved_current_file;
0089         bytes_moved_current_file = 0;
0090         success.insert(active_src, active_dst);
0091         active_src = active_dst = QString();
0092         active_job = nullptr;
0093         startMoving();
0094     }
0095 }
0096 
0097 void MoveDataFilesJob::start()
0098 {
0099     registerWithTracker();
0100     QMap<QString, QString>::iterator i = todo.begin();
0101     while (i != todo.end()) {
0102         QFileInfo fi(i.key());
0103         total_bytes += fi.size();
0104         ++i;
0105     }
0106     setTotalAmount(KJob::Bytes, total_bytes);
0107     move_data_files_slot.add(this);
0108     if (!active_job) {
0109         description(this,
0110                     i18n("Waiting for other move jobs to finish"),
0111                     qMakePair(i18nc("The source of a file operation", "Source"), active_src),
0112                     qMakePair(i18nc("The destination of a file operation", "Destination"), active_dst));
0113         emitSpeed(0);
0114     }
0115 }
0116 
0117 void MoveDataFilesJob::acquired()
0118 {
0119     startMoving();
0120 }
0121 
0122 void MoveDataFilesJob::kill(bool quietly)
0123 {
0124     Q_UNUSED(quietly);
0125     // don't do anything we cannot abort in the middle of this operation
0126 }
0127 
0128 void MoveDataFilesJob::startMoving()
0129 {
0130     if (todo.isEmpty()) {
0131         emitResult();
0132         return;
0133     }
0134 
0135     QMap<QString, QString>::iterator i = todo.begin();
0136     active_job = KIO::file_move(QUrl::fromLocalFile(i.key()), QUrl::fromLocalFile(i.value()), -1, KIO::HideProgressInfo);
0137     active_src = i.key();
0138     active_dst = i.value();
0139     Out(SYS_GEN | LOG_DEBUG) << "Moving " << active_src << " -> " << active_dst << endl;
0140     connect(active_job, &KIO::Job::result, this, &MoveDataFilesJob::onJobDone);
0141     connect(active_job, &KIO::Job::processedAmountChanged, this, &MoveDataFilesJob::onTransferred);
0142     connect(active_job, &KIO::Job::speed, this, &MoveDataFilesJob::onSpeed);
0143     todo.erase(i);
0144 
0145     description(this,
0146                 i18nc("@title job", "Moving"),
0147                 qMakePair(i18nc("The source of a file operation", "Source"), active_src),
0148                 qMakePair(i18nc("The destination of a file operation", "Destination"), active_dst));
0149     addSubjob(active_job);
0150 }
0151 
0152 void MoveDataFilesJob::recover(bool delete_active)
0153 {
0154     if (delete_active && bt::Exists(active_dst))
0155         bt::Delete(active_dst, true);
0156 
0157     if (success.isEmpty()) {
0158         emitResult();
0159         return;
0160     }
0161 
0162     running_recovery_jobs = 0;
0163     QMap<QString, QString>::iterator i = success.begin();
0164     while (i != success.end()) {
0165         KIO::Job *j = KIO::file_move(QUrl::fromLocalFile(i.value()), QUrl::fromLocalFile(i.key()), -1, KIO::HideProgressInfo);
0166         connect(j, &KIO::Job::result, this, &MoveDataFilesJob::onRecoveryJobDone);
0167         running_recovery_jobs++;
0168         ++i;
0169     }
0170     success.clear();
0171 }
0172 
0173 void MoveDataFilesJob::onRecoveryJobDone(KJob *j)
0174 {
0175     Q_UNUSED(j);
0176     running_recovery_jobs--;
0177     if (running_recovery_jobs <= 0)
0178         emitResult();
0179 }
0180 
0181 void MoveDataFilesJob::onTransferred(KJob *job, KJob::Unit unit, qulonglong amount)
0182 {
0183     Q_UNUSED(job);
0184     if (unit == KJob::Bytes) {
0185         bytes_moved_current_file = amount;
0186         setProcessedAmount(unit, bytes_moved + bytes_moved_current_file);
0187     }
0188 }
0189 
0190 void MoveDataFilesJob::onSpeed(KJob *job, unsigned long speed)
0191 {
0192     Q_UNUSED(job);
0193     emitSpeed(speed);
0194 }
0195 
0196 }