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