File indexing completed on 2024-04-21 04:18:47

0001 // vim: set tabstop=4 shiftwidth=4 expandtab:
0002 /*
0003 Gwenview: an image viewer
0004 Copyright 2009 Aurélien Gâteau <agateau@kde.org>
0005 
0006 This program is free software; you can redistribute it and/or
0007 modify it under the terms of the GNU General Public License
0008 as published by the Free Software Foundation; either version 2
0009 of the License, or (at your option) any later version.
0010 
0011 This program is distributed in the hope that it will be useful,
0012 but WITHOUT ANY WARRANTY; without even the implied warranty of
0013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0014 GNU General Public License for more details.
0015 
0016 You should have received a copy of the GNU General Public License
0017 along with this program; if not, write to the Free Software
0018 Foundation, Inc., 51 Franklin Street, Fifth Floor, Cambridge, MA 02110-1301, USA.
0019 
0020 */
0021 // Self
0022 #include "fileutils.h"
0023 
0024 // Qt
0025 #include <QBuffer>
0026 #include <QFile>
0027 #include <QFileInfo>
0028 #include <QScopedPointer>
0029 #include <QUrl>
0030 
0031 // KF
0032 #include <KFileItem>
0033 #include <KIO/CopyJob>
0034 #include <KIO/Job>
0035 #include <KIO/JobUiDelegate>
0036 #include <KIO/StatJob>
0037 #include <KIO/StoredTransferJob>
0038 #include <KJobWidgets>
0039 
0040 // Local
0041 #include "gwenview_importer_debug.h"
0042 
0043 namespace Gwenview
0044 {
0045 namespace FileUtils
0046 {
0047 bool contentsAreIdentical(const QUrl &url1, const QUrl &url2, QWidget *authWindow)
0048 {
0049     KIO::StatJob *statJob = nullptr;
0050     KIO::StoredTransferJob *fileJob = nullptr;
0051     QScopedPointer<QIODevice> file1, file2;
0052     QByteArray file1Bytes, file2Bytes;
0053 
0054     if (url1.isLocalFile()) {
0055         statJob = KIO::mostLocalUrl(url1);
0056         KJobWidgets::setWindow(statJob, authWindow);
0057         if (!statJob->exec()) {
0058             qCWarning(GWENVIEW_IMPORTER_LOG) << "Unable to stat" << url1;
0059             return false;
0060         }
0061         file1.reset(new QFile(statJob->mostLocalUrl().toLocalFile()));
0062     } else { // file1 is remote
0063         fileJob = KIO::storedGet(url1, KIO::NoReload, KIO::HideProgressInfo);
0064         KJobWidgets::setWindow(fileJob, authWindow);
0065         if (!fileJob->exec()) {
0066             qCWarning(GWENVIEW_IMPORTER_LOG) << "Can't read" << url1;
0067             return false;
0068         }
0069         file1Bytes = QByteArray(fileJob->data());
0070         file1.reset(new QBuffer(&file1Bytes));
0071     }
0072     if (!file1->open(QIODevice::ReadOnly)) {
0073         // Can't read url1, assume it's different from url2
0074         qCWarning(GWENVIEW_IMPORTER_LOG) << "Can't read" << url1;
0075         return false;
0076     }
0077 
0078     if (url2.isLocalFile()) {
0079         statJob = KIO::mostLocalUrl(url2);
0080         KJobWidgets::setWindow(statJob, authWindow);
0081         if (!statJob->exec()) {
0082             qCWarning(GWENVIEW_IMPORTER_LOG) << "Unable to stat" << url2;
0083             return false;
0084         }
0085         file2.reset(new QFile(statJob->mostLocalUrl().toLocalFile()));
0086     } else { // file2 is remote
0087         fileJob = KIO::storedGet(url2, KIO::NoReload, KIO::HideProgressInfo);
0088         KJobWidgets::setWindow(fileJob, authWindow);
0089         if (!fileJob->exec()) {
0090             qCWarning(GWENVIEW_IMPORTER_LOG) << "Can't read" << url2;
0091             return false;
0092         }
0093         file2Bytes = QByteArray(fileJob->data());
0094         file2.reset(new QBuffer(&file2Bytes));
0095     }
0096     if (!file2->open(QIODevice::ReadOnly)) {
0097         // Can't read url2, assume it's different from url1
0098         qCWarning(GWENVIEW_IMPORTER_LOG) << "Can't read" << url2;
0099         return false;
0100     }
0101 
0102     const int CHUNK_SIZE = 4096;
0103     while (!file1->atEnd() && !file2->atEnd()) {
0104         QByteArray url1Array = file1->read(CHUNK_SIZE);
0105         QByteArray url2Array = file2->read(CHUNK_SIZE);
0106 
0107         if (url1Array != url2Array) {
0108             return false;
0109         }
0110     }
0111     if (file1->atEnd() && file2->atEnd()) {
0112         return true;
0113     } else {
0114         qCWarning(GWENVIEW_IMPORTER_LOG) << "One file ended before the other";
0115         return false;
0116     }
0117 }
0118 
0119 RenameResult rename(const QUrl &src, const QUrl &dst_, QWidget *authWindow)
0120 {
0121     QUrl dst = dst_;
0122     RenameResult result = RenamedOK;
0123     int count = 1;
0124 
0125     QFileInfo fileInfo(dst.fileName());
0126     QString prefix = fileInfo.completeBaseName() + QLatin1Char('_');
0127     QString suffix = '.' + fileInfo.suffix();
0128 
0129     // Get src size
0130     KIO::StatJob *sourceStat = KIO::stat(src);
0131     KJobWidgets::setWindow(sourceStat, authWindow);
0132     if (!sourceStat->exec()) {
0133         return RenameFailed;
0134     }
0135     KFileItem item(sourceStat->statResult(), src, true /* delayedMimeTypes */);
0136     KIO::filesize_t srcSize = item.size();
0137 
0138     // Find unique name
0139     KIO::StatJob *statJob = KIO::stat(dst);
0140     KJobWidgets::setWindow(statJob, authWindow);
0141     while (statJob->exec()) {
0142         // File exists. If it's not the same, try to create a new name
0143         item = KFileItem(statJob->statResult(), dst, true /* delayedMimeTypes */);
0144         KIO::filesize_t dstSize = item.size();
0145 
0146         if (srcSize == dstSize && contentsAreIdentical(src, dst, authWindow)) {
0147             // Already imported, skip it
0148             KIO::Job *job = KIO::file_delete(src, KIO::HideProgressInfo);
0149             KJobWidgets::setWindow(job, authWindow);
0150 
0151             return Skipped;
0152         }
0153         result = RenamedUnderNewName;
0154 
0155         dst.setPath(dst.adjusted(QUrl::RemoveFilename).path() + prefix + QString::number(count) + suffix);
0156         statJob = KIO::stat(dst);
0157         KJobWidgets::setWindow(statJob, authWindow);
0158 
0159         ++count;
0160     }
0161 
0162     // Rename
0163     KIO::Job *job = KIO::moveAs(src, dst, KIO::HideProgressInfo);
0164     KJobWidgets::setWindow(job, authWindow);
0165     if (!job->exec()) {
0166         result = RenameFailed;
0167     }
0168     return result;
0169 }
0170 
0171 } // namespace
0172 } // namespace