File indexing completed on 2024-05-05 16:13:51

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