Warning, file /utilities/krusader/app/Synchronizer/synchronizertask.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /*
0002     SPDX-FileCopyrightText: 2006 Csaba Karai <krusader@users.sourceforge.net>
0003     SPDX-FileCopyrightText: 2006-2022 Krusader Krew <https://krusader.org>
0004 
0005     SPDX-License-Identifier: GPL-2.0-or-later
0006 */
0007 
0008 #include "synchronizertask.h"
0009 
0010 // QtCore
0011 #include <QElapsedTimer>
0012 #include <QFile>
0013 #include <QTimer>
0014 
0015 #include <KI18n/KLocalizedString>
0016 #include <KWidgetsAddons/KMessageBox>
0017 
0018 #include "../FileSystem/filesystem.h"
0019 #include "synchronizer.h"
0020 #include "synchronizerdirlist.h"
0021 #include "synchronizerfileitem.h"
0022 
0023 CompareTask::CompareTask(SynchronizerFileItem *parentIn,
0024                          const QString &leftURL,
0025                          const QString &rightURL,
0026                          const QString &leftDir,
0027                          const QString &rightDir,
0028                          bool hidden)
0029     : m_parent(parentIn)
0030     , m_url(leftURL)
0031     , m_dir(leftDir)
0032     , m_otherUrl(rightURL)
0033     , m_otherDir(rightDir)
0034     , m_duplicate(true)
0035     , m_dirList(nullptr)
0036     , m_otherDirList(nullptr)
0037 {
0038     ignoreHidden = hidden;
0039 }
0040 
0041 CompareTask::CompareTask(SynchronizerFileItem *parentIn, const QString &urlIn, const QString &dirIn, bool isLeftIn, bool hidden)
0042     : m_parent(parentIn)
0043     , m_url(urlIn)
0044     , m_dir(dirIn)
0045     , m_isLeft(isLeftIn)
0046     , m_duplicate(false)
0047     , m_dirList(nullptr)
0048     , m_otherDirList(nullptr)
0049 {
0050     ignoreHidden = hidden;
0051 }
0052 
0053 CompareTask::~CompareTask()
0054 {
0055     if (m_dirList) {
0056         delete m_dirList;
0057         m_dirList = nullptr;
0058     }
0059     if (m_otherDirList) {
0060         delete m_otherDirList;
0061         m_otherDirList = nullptr;
0062     }
0063 }
0064 
0065 void CompareTask::start()
0066 {
0067     if (m_state == ST_STATE_NEW) {
0068         m_state = ST_STATE_PENDING;
0069         m_loadFinished = m_otherLoadFinished = false;
0070 
0071         m_dirList = new SynchronizerDirList(parentWidget, ignoreHidden);
0072         connect(m_dirList, &SynchronizerDirList::finished, this, &CompareTask::slotFinished);
0073         m_dirList->load(m_url, false);
0074 
0075         if (m_duplicate) {
0076             m_otherDirList = new SynchronizerDirList(parentWidget, ignoreHidden);
0077             connect(m_otherDirList, &SynchronizerDirList::finished, this, &CompareTask::slotOtherFinished);
0078             m_otherDirList->load(m_otherUrl, false);
0079         }
0080     }
0081 }
0082 
0083 void CompareTask::slotFinished(bool result)
0084 {
0085     if (!result) {
0086         m_state = ST_STATE_ERROR;
0087         return;
0088     }
0089     m_loadFinished = true;
0090 
0091     if (m_otherLoadFinished || !m_duplicate)
0092         m_state = ST_STATE_READY;
0093 }
0094 
0095 void CompareTask::slotOtherFinished(bool result)
0096 {
0097     if (!result) {
0098         m_state = ST_STATE_ERROR;
0099         return;
0100     }
0101     m_otherLoadFinished = true;
0102 
0103     if (m_loadFinished)
0104         m_state = ST_STATE_READY;
0105 }
0106 
0107 CompareContentTask::CompareContentTask(Synchronizer *syn, SynchronizerFileItem *itemIn, const QUrl &leftURLIn, const QUrl &rightURLIn, KIO::filesize_t sizeIn)
0108     : leftURL(leftURLIn)
0109     , rightURL(rightURLIn)
0110     , size(sizeIn)
0111     , errorPrinted(false)
0112     , leftReadJob(nullptr)
0113     , rightReadJob(nullptr)
0114     , compareArray()
0115     , owner(-1)
0116     , item(itemIn)
0117     , timer(nullptr)
0118     , leftFile(nullptr)
0119     , rightFile(nullptr)
0120     , received(0)
0121     , sync(syn)
0122 {
0123 }
0124 
0125 CompareContentTask::~CompareContentTask()
0126 {
0127     abortContentComparing();
0128 
0129     if (timer)
0130         delete timer;
0131     if (leftFile)
0132         delete leftFile;
0133     if (rightFile)
0134         delete rightFile;
0135 }
0136 
0137 void CompareContentTask::start()
0138 {
0139     m_state = ST_STATE_PENDING;
0140 
0141     if (leftURL.isLocalFile() && rightURL.isLocalFile()) {
0142         leftFile = new QFile(leftURL.path());
0143         if (!leftFile->open(QIODevice::ReadOnly)) {
0144             KMessageBox::error(parentWidget, i18n("Error at opening %1.", leftURL.path()));
0145             m_state = ST_STATE_ERROR;
0146             return;
0147         }
0148 
0149         rightFile = new QFile(rightURL.path());
0150         if (!rightFile->open(QIODevice::ReadOnly)) {
0151             KMessageBox::error(parentWidget, i18n("Error at opening %1.", rightURL.path()));
0152             m_state = ST_STATE_ERROR;
0153             return;
0154         }
0155 
0156         timer = new QTimer(this);
0157         connect(timer, &QTimer::timeout, this, &CompareContentTask::sendStatusMessage);
0158         timer->setSingleShot(true);
0159         timer->start(1000);
0160 
0161         localFileCompareCycle();
0162     } else {
0163         leftReadJob = KIO::get(leftURL, KIO::NoReload, KIO::HideProgressInfo);
0164         rightReadJob = KIO::get(rightURL, KIO::NoReload, KIO::HideProgressInfo);
0165 
0166         connect(leftReadJob, &KIO::TransferJob::data, this, &CompareContentTask::slotDataReceived);
0167         connect(rightReadJob, &KIO::TransferJob::data, this, &CompareContentTask::slotDataReceived);
0168         connect(leftReadJob, &KIO::TransferJob::result, this, &CompareContentTask::slotFinished);
0169         connect(rightReadJob, &KIO::TransferJob::result, this, &CompareContentTask::slotFinished);
0170 
0171         rightReadJob->suspend();
0172 
0173         timer = new QTimer(this);
0174         connect(timer, &QTimer::timeout, this, &CompareContentTask::sendStatusMessage);
0175         timer->setSingleShot(true);
0176         timer->start(1000);
0177     }
0178 }
0179 
0180 void CompareContentTask::localFileCompareCycle()
0181 {
0182     bool different = false;
0183 
0184     char leftBuffer[1440];
0185     char rightBuffer[1440];
0186 
0187     QElapsedTimer timer;
0188     timer.start();
0189 
0190     int cnt = 0;
0191 
0192     while (!leftFile->atEnd() && !rightFile->atEnd()) {
0193         int leftBytes = static_cast<int>(leftFile->read(leftBuffer, sizeof(leftBuffer)));
0194         int rightBytes = static_cast<int>(rightFile->read(rightBuffer, sizeof(rightBuffer)));
0195 
0196         if (leftBytes != rightBytes) {
0197             different = true;
0198             break;
0199         }
0200 
0201         if (leftBytes <= 0)
0202             break;
0203 
0204         received += leftBytes;
0205 
0206         if (memcmp(leftBuffer, rightBuffer, leftBytes)) {
0207             different = true;
0208             break;
0209         }
0210 
0211         if ((++cnt % 16) == 0 && timer.elapsed() >= 250)
0212             break;
0213     }
0214 
0215     if (different) {
0216         sync->compareContentResult(item, false);
0217         m_state = ST_STATE_READY;
0218         return;
0219     }
0220 
0221     if (leftFile->atEnd() && rightFile->atEnd()) {
0222         sync->compareContentResult(item, true);
0223         m_state = ST_STATE_READY;
0224         return;
0225     }
0226 
0227     QTimer::singleShot(0, this, &CompareContentTask::localFileCompareCycle);
0228 }
0229 
0230 void CompareContentTask::slotDataReceived(KIO::Job *job, const QByteArray &data)
0231 {
0232     int jobowner = (job == leftReadJob) ? 1 : 0;
0233     int bufferLen = compareArray.size();
0234     int dataLen = data.size();
0235 
0236     if (job == leftReadJob)
0237         received += dataLen;
0238 
0239     if (jobowner == owner) {
0240         compareArray.append(data);
0241         return;
0242     }
0243 
0244     do {
0245         if (bufferLen == 0) {
0246             compareArray = QByteArray(data.data(), dataLen);
0247             owner = jobowner;
0248             break;
0249         }
0250 
0251         int minSize = (dataLen < bufferLen) ? dataLen : bufferLen;
0252 
0253         for (int i = 0; i != minSize; i++)
0254             if (data[i] != compareArray[i]) {
0255                 abortContentComparing();
0256                 return;
0257             }
0258 
0259         if (minSize == bufferLen) {
0260             compareArray = QByteArray(data.data() + bufferLen, dataLen - bufferLen);
0261             if (dataLen == bufferLen) {
0262                 owner = -1;
0263                 return;
0264             }
0265             owner = jobowner;
0266             break;
0267         } else {
0268             compareArray = QByteArray(compareArray.data() + dataLen, bufferLen - dataLen);
0269             return;
0270         }
0271 
0272     } while (false);
0273 
0274     KIO::TransferJob *otherJob = (job == leftReadJob) ? rightReadJob : leftReadJob;
0275 
0276     if (otherJob == nullptr) {
0277         if (compareArray.size())
0278             abortContentComparing();
0279     } else {
0280         if (!(qobject_cast<KIO::TransferJob *>(job))->isSuspended()) {
0281             (qobject_cast<KIO::TransferJob *>(job))->suspend();
0282             otherJob->resume();
0283         }
0284     }
0285 }
0286 
0287 void CompareContentTask::slotFinished(KJob *job)
0288 {
0289     KIO::TransferJob *otherJob = (job == leftReadJob) ? rightReadJob : leftReadJob;
0290 
0291     if (job == leftReadJob)
0292         leftReadJob = nullptr;
0293     else
0294         rightReadJob = nullptr;
0295 
0296     if (otherJob)
0297         otherJob->resume();
0298 
0299     if (job->error()) {
0300         timer->stop();
0301         abortContentComparing();
0302     }
0303 
0304     if (job->error() && job->error() != KIO::ERR_USER_CANCELED && !errorPrinted) {
0305         errorPrinted = true;
0306         KMessageBox::error(parentWidget,
0307                            i18n("I/O error while comparing file %1 with %2.",
0308                                 leftURL.toDisplayString(QUrl::PreferLocalFile),
0309                                 rightURL.toDisplayString(QUrl::PreferLocalFile)));
0310     }
0311 
0312     if (leftReadJob == nullptr && rightReadJob == nullptr) {
0313         if (!compareArray.size())
0314             sync->compareContentResult(item, true);
0315         else
0316             sync->compareContentResult(item, false);
0317 
0318         m_state = ST_STATE_READY;
0319     }
0320 }
0321 
0322 void CompareContentTask::abortContentComparing()
0323 {
0324     if (timer)
0325         timer->stop();
0326 
0327     if (leftReadJob)
0328         leftReadJob->kill(KJob::EmitResult);
0329     if (rightReadJob)
0330         rightReadJob->kill(KJob::EmitResult);
0331 
0332     if (item->task() >= TT_UNKNOWN)
0333         sync->compareContentResult(item, false);
0334 
0335     m_state = ST_STATE_READY;
0336 }
0337 
0338 void CompareContentTask::sendStatusMessage()
0339 {
0340     double perc = (size == 0) ? 1. : (double)received / (double)size;
0341     auto percent = (int)(perc * 10000. + 0.5);
0342     QString statstr = QString("%1.%2%3").arg(percent / 100).arg((percent / 10) % 10).arg(percent % 10) + '%';
0343     setStatusMessage(i18n("Comparing file %1 (%2)...", leftURL.fileName(), statstr));
0344     timer->setSingleShot(true);
0345     timer->start(500);
0346 }