File indexing completed on 2024-05-12 03:56:44

0001 /*
0002     SPDX-FileCopyrightText: 2000-2002 Stephan Kulow <coolo@kde.org>
0003     SPDX-FileCopyrightText: 2000-2002 David Faure <faure@kde.org>
0004     SPDX-FileCopyrightText: 2000-2002 Waldo Bastian <bastian@kde.org>
0005     SPDX-FileCopyrightText: 2006 Allan Sandfeld Jensen <sandfeld@kde.org>
0006     SPDX-FileCopyrightText: 2007 Thiago Macieira <thiago@kde.org>
0007     SPDX-FileCopyrightText: 2007 Christian Ehrlicher <ch.ehrlicher@gmx.de>
0008 
0009     SPDX-License-Identifier: LGPL-2.0-or-later
0010 */
0011 
0012 #include "file.h"
0013 
0014 #include <qt_windows.h>
0015 
0016 #include <QDir>
0017 #include <QDirIterator>
0018 #include <QFileInfo>
0019 #include <QUrl>
0020 
0021 #include <KConfigGroup>
0022 #include <QDebug>
0023 
0024 #include "kioglobal_p.h"
0025 
0026 using namespace KIO;
0027 
0028 static DWORD CALLBACK CopyProgressRoutine(LARGE_INTEGER TotalFileSize,
0029                                           LARGE_INTEGER TotalBytesTransferred,
0030                                           LARGE_INTEGER StreamSize,
0031                                           LARGE_INTEGER StreamBytesTransferred,
0032                                           DWORD dwStreamNumber,
0033                                           DWORD dwCallbackReason,
0034                                           HANDLE hSourceFile,
0035                                           HANDLE hDestinationFile,
0036                                           LPVOID lpData)
0037 {
0038     FileProtocol *f = reinterpret_cast<FileProtocol *>(lpData);
0039     f->processedSize(TotalBytesTransferred.QuadPart);
0040     return PROGRESS_CONTINUE;
0041 }
0042 
0043 static UDSEntry createUDSEntryWin(const QFileInfo &fileInfo)
0044 {
0045     UDSEntry entry;
0046 
0047     entry.fastInsert(KIO::UDSEntry::UDS_NAME, fileInfo.fileName());
0048     if (fileInfo.isSymLink()) {
0049         entry.fastInsert(KIO::UDSEntry::UDS_TARGET_URL, fileInfo.symLinkTarget());
0050         /* TODO - or not useful on windows?
0051                 if ( details > 1 ) {
0052                     // It is a link pointing to nowhere
0053                     type = S_IFMT - 1;
0054                     access = S_IRWXU | S_IRWXG | S_IRWXO;
0055 
0056                     entry.insert( KIO::UDSEntry::UDS_FILE_TYPE, type );
0057                     entry.insert( KIO::UDSEntry::UDS_ACCESS, access );
0058                     entry.insert( KIO::UDSEntry::UDS_SIZE, 0LL );
0059                     goto notype;
0060 
0061                 }
0062         */
0063     }
0064     int type = S_IFREG;
0065     int access = 0;
0066     if (fileInfo.isDir()) {
0067         type = S_IFDIR;
0068     } else if (fileInfo.isSymLink()) {
0069         type = QT_STAT_LNK;
0070     }
0071     if (fileInfo.isReadable()) {
0072         access |= S_IRUSR;
0073     }
0074     if (fileInfo.isWritable()) {
0075         access |= S_IWUSR;
0076     }
0077     if (fileInfo.isExecutable()) {
0078         access |= S_IXUSR;
0079     }
0080 
0081     entry.fastInsert(KIO::UDSEntry::UDS_FILE_TYPE, type);
0082     entry.fastInsert(KIO::UDSEntry::UDS_ACCESS, access);
0083     entry.fastInsert(KIO::UDSEntry::UDS_SIZE, fileInfo.size());
0084     if (fileInfo.isHidden()) {
0085         entry.fastInsert(KIO::UDSEntry::UDS_HIDDEN, true);
0086     }
0087 
0088     entry.fastInsert(KIO::UDSEntry::UDS_MODIFICATION_TIME, fileInfo.lastModified().toSecsSinceEpoch());
0089     entry.fastInsert(KIO::UDSEntry::UDS_USER, fileInfo.owner());
0090     entry.fastInsert(KIO::UDSEntry::UDS_GROUP, fileInfo.group());
0091     entry.fastInsert(KIO::UDSEntry::UDS_ACCESS_TIME, fileInfo.lastRead().toSecsSinceEpoch());
0092     entry.fastInsert(KIO::UDSEntry::UDS_CREATION_TIME, fileInfo.birthTime().toSecsSinceEpoch());
0093 
0094     QT_STATBUF buf;
0095     QUrl url(fileInfo.absoluteFilePath());
0096     const QString path = url.toString(QUrl::StripTrailingSlash | QUrl::PreferLocalFile);
0097     const QByteArray pathBA = QFile::encodeName(path);
0098 
0099     if (QT_LSTAT(pathBA.constData(), &buf) == 0) {
0100         entry.fastInsert(KIO::UDSEntry::UDS_DEVICE_ID, buf.st_dev);
0101         entry.fastInsert(KIO::UDSEntry::UDS_INODE, buf.st_ino);
0102     }
0103 
0104     return entry;
0105 }
0106 
0107 WorkerResult FileProtocol::copy(const QUrl &src, const QUrl &dest, int _mode, JobFlags _flags)
0108 {
0109     // qDebug() << "copy(): " << src << " -> " << dest << ", mode=" << _mode;
0110 
0111     QFileInfo _src(src.toLocalFile());
0112     QFileInfo _dest(dest.toLocalFile());
0113     DWORD dwFlags = COPY_FILE_FAIL_IF_EXISTS;
0114 
0115     if (_src == _dest) {
0116         return WorkerResult::fail(KIO::ERR_IDENTICAL_FILES, _dest.filePath());
0117     }
0118 
0119     if (!_src.exists()) {
0120         return WorkerResult::fail(KIO::ERR_DOES_NOT_EXIST, _src.filePath());
0121     }
0122 
0123     if (_src.isDir()) {
0124         return WorkerResult::fail(KIO::ERR_IS_DIRECTORY, _src.filePath());
0125     }
0126 
0127     if (_dest.exists()) {
0128         if (_dest.isDir()) {
0129             return WorkerResult::fail(KIO::ERR_DIR_ALREADY_EXIST, _dest.filePath());
0130         }
0131 
0132         if (!(_flags & KIO::Overwrite)) {
0133             return WorkerResult::fail(KIO::ERR_FILE_ALREADY_EXIST, _dest.filePath());
0134         }
0135 
0136         dwFlags = 0;
0137     }
0138 
0139     if (!QFileInfo(_dest.dir().absolutePath()).exists()) {
0140         _dest.dir().mkdir(_dest.dir().absolutePath());
0141     }
0142 
0143     if (CopyFileExW((LPCWSTR)_src.filePath().utf16(), (LPCWSTR)_dest.filePath().utf16(), CopyProgressRoutine, (LPVOID)this, FALSE, dwFlags) == 0) {
0144         DWORD dwLastErr = GetLastError();
0145         if (dwLastErr == ERROR_FILE_NOT_FOUND) {
0146             return WorkerResult::fail(KIO::ERR_DOES_NOT_EXIST, _src.filePath());
0147         } else if (dwLastErr == ERROR_ACCESS_DENIED) {
0148             return WorkerResult::fail(KIO::ERR_ACCESS_DENIED, _dest.filePath());
0149         } else {
0150             // qDebug() <<  "Copying file " << _src.filePath() << " failed (" << dwLastErr << ")";
0151             return WorkerResult::fail(KIO::ERR_CANNOT_RENAME, _src.filePath());
0152         }
0153     }
0154 
0155     return WorkerResult::pass();
0156 }
0157 
0158 WorkerResult FileProtocol::listDir(const QUrl &url)
0159 {
0160     // qDebug() << "========= LIST " << url << " =========";
0161 
0162     if (!url.isLocalFile()) {
0163         QUrl redir(url);
0164         redir.setScheme(configValue(QStringLiteral("DefaultRemoteProtocol"), QStringLiteral("smb")));
0165         redirection(redir);
0166         // qDebug() << "redirecting to " << redir;
0167         return WorkerResult::pass();
0168     }
0169 
0170     QString path = url.toLocalFile();
0171     // C: means current directory, a concept which makes no sense in a GUI
0172     // KCoreDireLister strips trailing slashes, let's put it back again here for C:/
0173     if (path.length() == 2 && path.at(1) == QLatin1Char(':'))
0174         path += QLatin1Char('/');
0175     const QFileInfo info(path);
0176     if (info.isFile()) {
0177         return WorkerResult::fail(KIO::ERR_IS_FILE, path);
0178     }
0179 
0180     QDir dir(path);
0181     dir.setFilter(QDir::AllEntries | QDir::Hidden);
0182 
0183     if (!dir.exists()) {
0184         // qDebug() << "========= ERR_DOES_NOT_EXIST  =========";
0185         return WorkerResult::fail(KIO::ERR_DOES_NOT_EXIST, path);
0186     }
0187 
0188     if (!dir.isReadable()) {
0189         // qDebug() << "========= ERR_CANNOT_ENTER_DIRECTORY =========";
0190         return WorkerResult::fail(KIO::ERR_CANNOT_ENTER_DIRECTORY, path);
0191     }
0192     QDirIterator it(dir);
0193     UDSEntry entry;
0194     while (it.hasNext()) {
0195         it.next();
0196         UDSEntry entry = createUDSEntryWin(it.fileInfo());
0197 
0198         listEntry(entry);
0199         entry.clear();
0200     }
0201 
0202     // qDebug() << "============= COMPLETED LIST ============";
0203 
0204     return WorkerResult::pass();
0205 }
0206 
0207 WorkerResult FileProtocol::rename(const QUrl &src, const QUrl &dest, KIO::JobFlags _flags)
0208 {
0209     // qDebug() << "rename(): " << src << " -> " << dest;
0210 
0211     QFileInfo _src(src.toLocalFile());
0212     QFileInfo _dest(dest.toLocalFile());
0213     DWORD dwFlags = 0;
0214 
0215     if (_src == _dest) {
0216         return WorkerResult::fail(KIO::ERR_IDENTICAL_FILES, _dest.filePath());
0217     }
0218 
0219     if (!_src.exists()) {
0220         return WorkerResult::fail(KIO::ERR_DOES_NOT_EXIST, _src.filePath());
0221     }
0222 
0223     if (_dest.exists()) {
0224         if (_dest.isDir()) {
0225             return WorkerResult::fail(KIO::ERR_DIR_ALREADY_EXIST, _dest.filePath());
0226         }
0227 
0228         if (!(_flags & KIO::Overwrite)) {
0229             return WorkerResult::fail(KIO::ERR_FILE_ALREADY_EXIST, _dest.filePath());
0230         }
0231 
0232         dwFlags = MOVEFILE_REPLACE_EXISTING;
0233     }
0234     // To avoid error 17 - The system cannot move the file to a different disk drive.
0235     dwFlags |= MOVEFILE_COPY_ALLOWED;
0236 
0237     if (MoveFileExW((LPCWSTR)_src.filePath().utf16(), (LPCWSTR)_dest.filePath().utf16(), dwFlags) == 0) {
0238         DWORD dwLastErr = GetLastError();
0239         if (dwLastErr == ERROR_FILE_NOT_FOUND) {
0240             return WorkerResult::fail(KIO::ERR_DOES_NOT_EXIST, _src.filePath());
0241         } else if (dwLastErr == ERROR_ACCESS_DENIED) {
0242             return WorkerResult::fail(KIO::ERR_ACCESS_DENIED, _dest.filePath());
0243         } else {
0244             qCDebug(KIO_FILE) << "Renaming file " << _src.filePath() << " failed (" << dwLastErr << ")";
0245             return WorkerResult::fail(KIO::ERR_CANNOT_RENAME, _src.filePath());
0246         }
0247     }
0248 
0249     return WorkerResult::pass();
0250 }
0251 
0252 WorkerResult FileProtocol::symlink(const QString &target, const QUrl &dest, KIO::JobFlags flags)
0253 {
0254     QString localDest = dest.toLocalFile();
0255     // TODO handle overwrite, etc
0256     if (!KIOPrivate::createSymlink(target, localDest)) {
0257         return WorkerResult::fail(KIO::ERR_UNKNOWN, localDest);
0258     }
0259 
0260     return WorkerResult::pass();
0261 }
0262 
0263 WorkerResult FileProtocol::del(const QUrl &url, bool isfile)
0264 {
0265     QString _path(url.toLocalFile());
0266     /*****
0267      * Delete files
0268      *****/
0269 
0270     if (isfile) {
0271         // qDebug() << "Deleting file " << _path;
0272 
0273         if (DeleteFileW((LPCWSTR)_path.utf16()) == 0) {
0274             DWORD dwLastErr = GetLastError();
0275             if (dwLastErr == ERROR_PATH_NOT_FOUND) {
0276                 return WorkerResult::fail(KIO::ERR_DOES_NOT_EXIST, _path);
0277             } else if (dwLastErr == ERROR_ACCESS_DENIED) {
0278                 return WorkerResult::fail(KIO::ERR_ACCESS_DENIED, _path);
0279             } else {
0280                 return WorkerResult::fail(KIO::ERR_CANNOT_DELETE, _path);
0281                 // qDebug() <<  "Deleting file " << _path << " failed (" << dwLastErr << ")";
0282             }
0283         }
0284     } else {
0285         // qDebug() << "Deleting directory " << _path;
0286         auto deleteResult = deleteRecursive(_path);
0287         if (!deleteResult.success()) {
0288             return deleteResult;
0289         }
0290         if (RemoveDirectoryW((LPCWSTR)_path.utf16()) == 0) {
0291             DWORD dwLastErr = GetLastError();
0292             if (dwLastErr == ERROR_FILE_NOT_FOUND) {
0293                 return WorkerResult::fail(KIO::ERR_DOES_NOT_EXIST, _path);
0294             } else if (dwLastErr == ERROR_ACCESS_DENIED) {
0295                 return WorkerResult::fail(KIO::ERR_ACCESS_DENIED, _path);
0296             } else {
0297                 return WorkerResult::fail(KIO::ERR_CANNOT_DELETE, _path);
0298                 // qDebug() <<  "Deleting directory " << _path << " failed (" << dwLastErr << ")";
0299             }
0300         }
0301     }
0302     return WorkerResult::pass();
0303 }
0304 
0305 WorkerResult FileProtocol::chown(const QUrl &url, const QString &, const QString &)
0306 {
0307     return WorkerResult::fail(KIO::ERR_CANNOT_CHOWN, url.toLocalFile());
0308 }
0309 
0310 WorkerResult FileProtocol::stat(const QUrl &url)
0311 {
0312     if (!url.isLocalFile()) {
0313         return redirect(url);
0314     }
0315 
0316     const QString sDetails = metaData(QLatin1String("details"));
0317     int details = sDetails.isEmpty() ? 2 : sDetails.toInt();
0318     // qDebug() << "FileProtocol::stat details=" << details;
0319 
0320     const QString localFile = url.toLocalFile();
0321     QFileInfo fileInfo(localFile);
0322     if (!fileInfo.exists()) {
0323         return WorkerResult::fail(KIO::ERR_DOES_NOT_EXIST, localFile);
0324     }
0325 
0326     UDSEntry entry = createUDSEntryWin(fileInfo);
0327 
0328     statEntry(entry);
0329 
0330     return WorkerResult::pass();
0331 }
0332 
0333 bool FileProtocol::privilegeOperationUnitTestMode()
0334 {
0335     return false;
0336 }
0337 
0338 WorkerResult FileProtocol::execWithElevatedPrivilege(ActionType, const QVariantList &, int err)
0339 {
0340     return WorkerResult::fail(err);
0341 }
0342 WorkerResult FileProtocol::tryOpen(QFile &f, const QByteArray &, int, int, int err)
0343 {
0344     return WorkerResult::fail(err);
0345 }
0346 
0347 WorkerResult FileProtocol::tryChangeFileAttr(ActionType, const QVariantList &, int err)
0348 {
0349     return WorkerResult::fail(err);
0350 }
0351 
0352 int FileProtocol::setACL(const char *path, mode_t perm, bool directoryDefault)
0353 {
0354     Q_UNUSED(path);
0355     Q_UNUSED(perm);
0356     Q_UNUSED(directoryDefault);
0357     return 0;
0358 }