From 2a8bbbd2c8923a7569914003fa191143339fc169 Mon Sep 17 00:00:00 2001 From: sbosse Date: Mon, 21 Jul 2025 23:22:41 +0200 Subject: [PATCH] Mon 21 Jul 22:43:21 CEST 2025 --- .../cordova-plugin-file/src/ubuntu/file.cpp | 912 ++++++++++++++++++ 1 file changed, 912 insertions(+) create mode 100644 js/ui/cordova/plugins/cordova-plugin-file/src/ubuntu/file.cpp diff --git a/js/ui/cordova/plugins/cordova-plugin-file/src/ubuntu/file.cpp b/js/ui/cordova/plugins/cordova-plugin-file/src/ubuntu/file.cpp new file mode 100644 index 0000000..395ab2d --- /dev/null +++ b/js/ui/cordova/plugins/cordova-plugin-file/src/ubuntu/file.cpp @@ -0,0 +1,912 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "file.h" + +#include + +namespace { + class FileError { + public: + static const QString kEncodingErr; + static const QString kTypeMismatchErr; + static const QString kNotFoundErr; + static const QString kSecurityErr; + static const QString kAbortErr; + static const QString kNotReadableErr; + static const QString kNoModificationAllowedErr; + static const QString kInvalidStateErr; + static const QString kSyntaxErr; + static const QString kInvalidModificationErr; + static const QString kQuotaExceededErr; + static const QString kPathExistsErr; + }; + + bool checkFileName(const QString &name) { + if (name.contains(":")){ + return false; + } + return true; + } +}; + +const QString FileError::kEncodingErr("FileError.ENCODING_ERR"); +const QString FileError::kTypeMismatchErr("FileError.TYPE_MISMATCH_ERR"); +const QString FileError::kNotFoundErr("FileError.NOT_FOUND_ERR"); +const QString FileError::kSecurityErr("FileError.SECURITY_ERR"); +const QString FileError::kAbortErr("FileError.ABORT_ERR"); +const QString FileError::kNotReadableErr("FileError.NOT_READABLE_ERR"); +const QString FileError::kNoModificationAllowedErr("FileError.NO_MODIFICATION_ALLOWED_ERR"); +const QString FileError::kInvalidStateErr("FileError.INVALID_STATE_ERR"); +const QString FileError::kSyntaxErr("FileError.SYNTAX_ERR"); +const QString FileError::kInvalidModificationErr("FileError.INVALID_MODIFICATION_ERR"); +const QString FileError::kQuotaExceededErr("FileError.QUOTA_EXCEEDED_ERR"); +const QString FileError::kPathExistsErr("FileError.PATH_EXISTS_ERR"); + +File::File(Cordova *cordova) : + CPlugin(cordova), + _persistentDir(QString("%1/.local/share/%2/persistent").arg(QDir::homePath()).arg(QCoreApplication::applicationName())) { + QDir::root().mkpath(_persistentDir.absolutePath()); +} + +QVariantMap File::file2map(const QFileInfo &fileInfo) { + QVariantMap res; + + res.insert("name", fileInfo.fileName()); + QPair r = GetRelativePath(fileInfo); + res.insert("fullPath", QString("/") + r.second); + res.insert("filesystemName", r.first); + + res.insert("nativeURL", QString("file://localhost") + fileInfo.absoluteFilePath()); + res.insert("isDirectory", (int)fileInfo.isDir()); + res.insert("isFile", (int)fileInfo.isFile()); + + return res; +} + +QVariantMap File::dir2map(const QDir &dir) { + return file2map(QFileInfo(dir.absolutePath())); +} + +QPair File::GetRelativePath(const QFileInfo &fileInfo) { + QString fullPath = fileInfo.isDir() ? QDir::cleanPath(fileInfo.absoluteFilePath()) : fileInfo.absoluteFilePath(); + + QString relativePath1 = _persistentDir.relativeFilePath(fullPath); + QString relativePath2 = QDir::temp().relativeFilePath(fullPath); + + if (!(relativePath1[0] != '.' || relativePath2[0] != '.')) { + if (relativePath1.size() > relativePath2.size()) { + return QPair("temporary", relativePath2); + } else { + return QPair("persistent", relativePath1); + } + } + + if (relativePath1[0] != '.') + return QPair("persistent", relativePath1); + return QPair("temporary", relativePath2); +} + +void File::requestFileSystem(int scId, int ecId, unsigned short type, unsigned long long size) { + QDir dir; + + if (size >= 1000485760){ + this->callback(ecId, FileError::kQuotaExceededErr); + return; + } + + if (type == 0) + dir = QDir::temp(); + else + dir = _persistentDir; + + if (type > 1) { + this->callback(ecId, FileError::kSyntaxErr); + return; + } else { + QVariantMap res; + res.insert("root", dir2map(dir)); + if (type == 0) + res.insert("name", "temporary"); + else + res.insert("name", "persistent"); + + this->cb(scId, res); + } +} + +QPair File::resolveURI(int ecId, const QString &uri) { + QPair result; + + result.first = false; + + QUrl url = QUrl::fromUserInput(uri); + + if (url.scheme() == "file" && url.isValid()) { + result.first = true; + result.second = QFileInfo(url.path()); + return result; + } + + if (url.scheme() != "cdvfile") { + if (ecId) + this->callback(ecId, FileError::kTypeMismatchErr); + return result; + } + + QString path = url.path().replace("//", "/"); + //NOTE: colon is not safe in url, it is not a valid path in Win and Mac, simple disable it here. + if (path.contains(":") || !url.isValid()){ + if (ecId) + this->callback(ecId, FileError::kEncodingErr); + return result; + } + if (!path.startsWith("/persistent/") && !path.startsWith("/temporary/")) { + if (ecId) + this->callback(ecId, FileError::kEncodingErr); + return result; + } + + result.first = true; + if (path.startsWith("/persistent/")) { + QString relativePath = path.mid(QString("/persistent/").size()); + result.second = QFileInfo(_persistentDir.filePath(relativePath)); + } else { + QString relativePath = path.mid(QString("/temporary/").size()); + result.second = QFileInfo(QDir::temp().filePath(relativePath)); + } + return result; +} + +QPair File::resolveURI(const QString &uri) { + return resolveURI(0, uri); +} + + +void File::_getLocalFilesystemPath(int scId, int ecId, const QString& uri) { + QPair f1 = resolveURI(ecId, uri); + + if (!f1.first) + return; + + this->cb(scId, f1.second.absoluteFilePath()); +} + +void File::resolveLocalFileSystemURI(int scId, int ecId, const QString &uri) { + if (uri[0] == '/' || uri[0] == '.') { + this->callback(ecId, FileError::kEncodingErr); + return; + } + + QPair f1 = resolveURI(ecId, uri); + + if (!f1.first) + return; + + QFileInfo fileInfo = f1.second; + if (!fileInfo.exists()) { + this->callback(ecId, FileError::kNotFoundErr); + return; + } + + this->cb(scId, file2map(fileInfo)); +} + +void File::getFile(int scId, int ecId, const QString &parentPath, const QString &rpath, const QVariantMap &options) { + QPair f1 = resolveURI(ecId, parentPath + "/" + rpath); + if (!f1.first) + return; + + bool create = options.value("create").toBool(); + bool exclusive = options.value("exclusive").toBool(); + QFile file(f1.second.absoluteFilePath()); + + // if create is false and the path represents a directory, return error + QFileInfo fileInfo = f1.second; + if ((!create) && fileInfo.isDir()) { + this->callback(ecId, FileError::kTypeMismatchErr); + return; + } + + // if file does exist, and create is true and exclusive is true, return error + if (file.exists()) { + if (create && exclusive) { + this->callback(ecId, FileError::kPathExistsErr); + return; + } + } + else { + // if file does not exist and create is false, return error + if (!create) { + this->callback(ecId, FileError::kNotFoundErr); + return; + } + + file.open(QIODevice::WriteOnly); + file.close(); + + // Check if creation was successfull + if (!file.exists()) { + this->callback(ecId, FileError::kNoModificationAllowedErr); + return; + } + } + + this->cb(scId, file2map(QFileInfo(file))); +} + +void File::getDirectory(int scId, int ecId, const QString &parentPath, const QString &rpath, const QVariantMap &options) { + QPair f1 = resolveURI(ecId, parentPath + "/" + rpath); + if (!f1.first) + return; + + bool create = options.value("create").toBool(); + bool exclusive = options.value("exclusive").toBool(); + QDir dir(f1.second.absoluteFilePath()); + + QFileInfo &fileInfo = f1.second; + if ((!create) && fileInfo.isFile()) { + this->callback(ecId, FileError::kTypeMismatchErr); + return; + } + + if (dir.exists()) { + if (create && exclusive) { + this->callback(ecId, FileError::kPathExistsErr); + return; + } + } + else { + if (!create) { + this->callback(ecId, FileError::kNotFoundErr); + return; + } + + QString folderName = dir.dirName(); + dir.cdUp(); + dir.mkdir(folderName); + dir.cd(folderName); + + if (!dir.exists()) { + this->callback(ecId, FileError::kNoModificationAllowedErr); + return; + } + } + + this->cb(scId, dir2map(dir)); +} + +void File::removeRecursively(int scId, int ecId, const QString &uri) { + QPair f1 = resolveURI(ecId, uri); + + if (!f1.first) + return; + + QDir dir(f1.second.absoluteFilePath()); + if (File::rmDir(dir)) + this->cb(scId); + else + this->callback(ecId, FileError::kNoModificationAllowedErr); +} + +void File::write(int scId, int ecId, const QString &uri, const QString &_data, unsigned long long position, bool binary) { + QPair f1 = resolveURI(ecId, uri); + + if (!f1.first) + return; + + QFile file(f1.second.absoluteFilePath()); + + file.open(QIODevice::WriteOnly); + file.close(); + + if (!file.exists()) { + this->callback(ecId, FileError::kNotFoundErr); + return; + } + + QFileInfo fileInfo(file); + if (!file.open(QIODevice::ReadWrite)) { + this->callback(ecId, FileError::kNoModificationAllowedErr); + return; + } + + if (!binary) { + QTextStream textStream(&file); + textStream.setCodec("UTF-8"); + textStream.setAutoDetectUnicode(true); + + if (!textStream.seek(position)) { + file.close(); + fileInfo.refresh(); + + this->callback(ecId, FileError::kInvalidModificationErr); + return; + } + + textStream << _data; + textStream.flush(); + } else { + QByteArray data(_data.toUtf8()); + if (!file.seek(position)) { + file.close(); + fileInfo.refresh(); + + this->callback(ecId, FileError::kInvalidModificationErr); + return; + } + + file.write(data.data(), data.length()); + } + + file.flush(); + file.close(); + fileInfo.refresh(); + + this->cb(scId, fileInfo.size() - position); +} + +void File::truncate(int scId, int ecId, const QString &uri, unsigned long long size) { + QPair f1 = resolveURI(ecId, uri); + + if (!f1.first) + return; + + QFile file(f1.second.absoluteFilePath()); + + if (!file.exists()) { + this->callback(ecId, FileError::kNotFoundErr); + return; + } + + if (!file.resize(size)) { + this->callback(ecId, FileError::kNoModificationAllowedErr); + return; + } + + this->cb(scId, size); +} + +void File::getParent(int scId, int ecId, const QString &uri) { + QPair f1 = resolveURI(ecId, uri); + + if (!f1.first) + return; + QDir dir(f1.second.absoluteFilePath()); + + //can't cdup more than app's root + // Try to change into upper directory + if (dir != _persistentDir && dir != QDir::temp()){ + if (!dir.cdUp()) { + this->callback(ecId, FileError::kNotFoundErr); + return; + } + + } + this->cb(scId, dir2map(dir)); +} + +void File::remove(int scId, int ecId, const QString &uri) { + QPair f1 = resolveURI(ecId, uri); + if (!f1.first) + return; + + QFileInfo &fileInfo = f1.second; + //TODO: fix + if (!fileInfo.exists() || (fileInfo.absoluteFilePath() == _persistentDir.absolutePath()) || (QDir::temp() == fileInfo.absoluteFilePath())) { + this->callback(ecId, FileError::kNoModificationAllowedErr); + return; + } + + if (fileInfo.isDir()) { + QDir dir(fileInfo.absoluteFilePath()); + if (dir.rmdir(dir.absolutePath())) { + this->cb(scId); + return; + } + } else { + QFile file(fileInfo.absoluteFilePath()); + if (file.remove()) { + this->cb(scId); + return; + } + } + + this->callback(ecId, FileError::kInvalidModificationErr); +} + +void File::getFileMetadata(int scId, int ecId, const QString &uri) { + QPair f1 = resolveURI(ecId, uri); + + if (!f1.first) + return; + QFileInfo &fileInfo = f1.second; + + if (!fileInfo.exists()) { + this->callback(ecId, FileError::kNotFoundErr); + } else { + QMimeType mime = _db.mimeTypeForFile(fileInfo.fileName()); + + QString args = QString("{name: %1, fullPath: %2, type: %3, lastModifiedDate: new Date(%4), size: %5}") + .arg(CordovaInternal::format(fileInfo.fileName())).arg(CordovaInternal::format(fileInfo.absoluteFilePath())) + .arg(CordovaInternal::format(mime.name())).arg(fileInfo.lastModified().toMSecsSinceEpoch()) + .arg(fileInfo.size()); + + this->callback(scId, args); + } +} + +void File::getMetadata(int scId, int ecId, const QString &uri) { + QPair f1 = resolveURI(ecId, uri); + + if (!f1.first) + return; + QFileInfo &fileInfo = f1.second; + + if (!fileInfo.exists()) + this->callback(ecId, FileError::kNotFoundErr); + else { + QVariantMap obj; + obj.insert("modificationTime", fileInfo.lastModified().toMSecsSinceEpoch()); + obj.insert("size", fileInfo.isDir() ? 0 : fileInfo.size()); + this->cb(scId, obj); + } +} + +void File::readEntries(int scId, int ecId, const QString &uri) { + QPair f1 = resolveURI(ecId, uri); + + if (!f1.first) + return; + QDir dir(f1.second.absoluteFilePath()); + QString entriesList; + + if (!dir.exists()) { + this->callback(ecId, FileError::kNotFoundErr); + return; + } + + for (const QFileInfo &fileInfo: dir.entryInfoList(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot)) { + entriesList += CordovaInternal::format(file2map(fileInfo)) + ","; + } + // Remove trailing comma + if (entriesList.size() > 0) + entriesList.remove(entriesList.size() - 1, 1); + + entriesList = "new Array(" + entriesList + ")"; + + this->callback(scId, entriesList); +} + +void File::readAsText(int scId, int ecId, const QString &uri, const QString &/*encoding*/, int sliceStart, int sliceEnd) { + QPair f1 = resolveURI(ecId, uri); + + if (!f1.first) + return; + + QFile file(f1.second.absoluteFilePath()); + + if (!file.exists()) { + this->callback(ecId, FileError::kNotFoundErr); + return; + } + + if (!file.open(QIODevice::ReadOnly)) { + this->callback(ecId, FileError::kNotReadableErr); + return; + } + + QByteArray content = file.readAll(); + + if (sliceEnd == -1) + sliceEnd = content.size(); + if (sliceEnd < 0) { + sliceEnd++; + sliceEnd = std::max(0, content.size() + sliceEnd); + } + if (sliceEnd > content.size()) + sliceEnd = content.size(); + + if (sliceStart < 0) + sliceStart = std::max(0, content.size() + sliceStart); + if (sliceStart > content.size()) + sliceStart = content.size(); + + if (sliceStart > sliceEnd) + sliceEnd = sliceStart; + + //FIXME: encoding + content = content.mid(sliceStart, sliceEnd - sliceStart); + + this->cb(scId, content); +} + +void File::readAsArrayBuffer(int scId, int ecId, const QString &uri, int sliceStart, int sliceEnd) { + const QString str2array("\ + (function strToArray(str) { \ + var res = new Uint8Array(str.length); \ + for (var i = 0; i < str.length; i++) { \ + res[i] = str.charCodeAt(i); \ + } \ + return res; \ + })(\"%1\")"); + + QPair f1 = resolveURI(ecId, uri); + + if (!f1.first) + return; + + QFile file(f1.second.absoluteFilePath()); + + if (!file.exists()) { + this->callback(ecId, FileError::kNotFoundErr); + return; + } + + if (!file.open(QIODevice::ReadOnly)) { + this->callback(ecId, FileError::kNotReadableErr); + return; + } + QString res; + QByteArray content = file.readAll(); + + if (sliceEnd == -1) + sliceEnd = content.size(); + if (sliceEnd < 0) { + sliceEnd++; + sliceEnd = std::max(0, content.size() + sliceEnd); + } + if (sliceEnd > content.size()) + sliceEnd = content.size(); + + if (sliceStart < 0) + sliceStart = std::max(0, content.size() + sliceStart); + if (sliceStart > content.size()) + sliceStart = content.size(); + + if (sliceStart > sliceEnd) + sliceEnd = sliceStart; + + content = content.mid(sliceStart, sliceEnd - sliceStart); + + res.reserve(content.length() * 6); + for (uchar c: content) { + res += "\\x"; + res += QString::number(c, 16).rightJustified(2, '0').toUpper(); + } + + this->callback(scId, str2array.arg(res)); +} + +void File::readAsBinaryString(int scId, int ecId, const QString &uri, int sliceStart, int sliceEnd) { + QPair f1 = resolveURI(ecId, uri); + + if (!f1.first) + return; + + QFile file(f1.second.absoluteFilePath()); + + if (!file.exists()) { + this->callback(ecId, FileError::kNotFoundErr); + return; + } + + if (!file.open(QIODevice::ReadOnly)) { + this->callback(ecId, FileError::kNotReadableErr); + return; + } + QString res; + QByteArray content = file.readAll(); + + if (sliceEnd == -1) + sliceEnd = content.size(); + if (sliceEnd < 0) { + sliceEnd++; + sliceEnd = std::max(0, content.size() + sliceEnd); + } + if (sliceEnd > content.size()) + sliceEnd = content.size(); + + if (sliceStart < 0) + sliceStart = std::max(0, content.size() + sliceStart); + if (sliceStart > content.size()) + sliceStart = content.size(); + + if (sliceStart > sliceEnd) + sliceEnd = sliceStart; + + content = content.mid(sliceStart, sliceEnd - sliceStart); + + res.reserve(content.length() * 6); + for (uchar c: content) { + res += "\\x"; + res += QString::number(c, 16).rightJustified(2, '0').toUpper(); + } + this->callback(scId, "\"" + res + "\""); +} + +void File::readAsDataURL(int scId, int ecId, const QString &uri, int sliceStart, int sliceEnd) { + QPair f1 = resolveURI(ecId, uri); + + if (!f1.first) + return; + + QFile file(f1.second.absoluteFilePath()); + QFileInfo &fileInfo = f1.second; + + if (!file.exists()) { + this->callback(ecId, FileError::kNotReadableErr); + return; + } + + if (!file.open(QIODevice::ReadOnly)) { + this->callback(ecId, FileError::kNotReadableErr); + return; + } + + QByteArray content = file.readAll(); + QString contentType(_db.mimeTypeForFile(fileInfo.fileName()).name()); + + if (sliceEnd == -1) + sliceEnd = content.size(); + if (sliceEnd < 0) { + sliceEnd++; + sliceEnd = std::max(0, content.size() + sliceEnd); + } + if (sliceEnd > content.size()) + sliceEnd = content.size(); + + if (sliceStart < 0) + sliceStart = std::max(0, content.size() + sliceStart); + if (sliceStart > content.size()) + sliceStart = content.size(); + + if (sliceStart > sliceEnd) + sliceEnd = sliceStart; + + content = content.mid(sliceStart, sliceEnd - sliceStart); + + this->cb(scId, QString("data:%1;base64,").arg(contentType) + content.toBase64()); +} + +bool File::rmDir(const QDir &dir) { + if (dir == _persistentDir || dir == QDir::temp()) {//can't remove root dir + return false; + } + bool result = true; + if (dir.exists()) { + // Iterate over entries and remove them + Q_FOREACH(const QFileInfo &fileInfo, dir.entryInfoList(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot)) { + if (fileInfo.isDir()) { + result = rmDir(fileInfo.absoluteFilePath()); + } + else { + result = QFile::remove(fileInfo.absoluteFilePath()); + } + + if (!result) { + return result; + } + } + + // Finally remove the current dir + return dir.rmdir(dir.absolutePath()); + } + return result; +} + +bool File::copyFile(int scId, int ecId,const QString& sourceUri, const QString& destinationUri, const QString& newName) { + QPair destDir = resolveURI(ecId, destinationUri); + QPair sourceFile = resolveURI(ecId, sourceUri); + + if (!destDir.first || !sourceFile.first) + return false; + + if (!checkFileName(newName)) { + this->callback(ecId, FileError::kEncodingErr); + return false; + } + + if (destDir.second.isFile()) { + this->callback(ecId, FileError::kInvalidModificationErr); + return false; + } + + if (!destDir.second.isDir()) { + this->callback(ecId, FileError::kNotFoundErr); + return false; + } + + QFileInfo &fileInfo = sourceFile.second; + QString fileName((newName.isEmpty()) ? fileInfo.fileName() : newName); + QString destinationFile(QDir(destDir.second.absoluteFilePath()).filePath(fileName)); + if (QFile::copy(fileInfo.absoluteFilePath(), destinationFile)){ + this->cb(scId, file2map(QFileInfo(destinationFile))); + return true; + } + this->callback(ecId, FileError::kInvalidModificationErr); + return false; +} + +void File::copyDir(int scId, int ecId,const QString& sourceUri, const QString& destinationUri, const QString& newName) { + QPair destDir = resolveURI(ecId, destinationUri); + QPair sourceDir = resolveURI(ecId, sourceUri); + + if (!destDir.first || !sourceDir.first) + return; + if (!checkFileName(newName)) { + this->callback(ecId, FileError::kEncodingErr); + return; + } + + QString targetName = ((newName.isEmpty()) ? sourceDir.second.fileName() : newName); + QString target(QDir(destDir.second.absoluteFilePath()).filePath(targetName)); + + if (QFileInfo(target).isFile()){ + this->callback(ecId, FileError::kInvalidModificationErr); + return; + } + + // check: copy directory into itself + if (QDir(sourceDir.second.absoluteFilePath()).relativeFilePath(target)[0] != '.'){ + this->callback(ecId, FileError::kInvalidModificationErr); + return; + } + + if (!QDir(target).exists()){ + QDir(destDir.second.absoluteFilePath()).mkdir(target);; + } else{ + this->callback(ecId, FileError::kInvalidModificationErr); + return; + } + + if (copyFolder(sourceDir.second.absoluteFilePath(), target)){ + this->cb(scId, dir2map(QDir(target))); + return; + } + this->callback(ecId, FileError::kInvalidModificationErr); + return; +} + +void File::copyTo(int scId, int ecId, const QString& source, const QString& destinationDir, const QString& newName) { + QPair f1 = resolveURI(ecId, source); + + if (!f1.first) + return; + + if (f1.second.isDir()) + copyDir(scId, ecId, source, destinationDir, newName); + else + copyFile(scId, ecId, source, destinationDir, newName); +} + +void File::moveFile(int scId, int ecId,const QString& sourceUri, const QString& destinationUri, const QString& newName) { + QPair sourceFile = resolveURI(ecId, sourceUri); + QPair destDir = resolveURI(ecId, destinationUri); + + if (!destDir.first || !sourceFile.first) + return; + if (!checkFileName(newName)) { + this->callback(ecId, FileError::kEncodingErr); + return; + } + + QString fileName = ((newName.isEmpty()) ? sourceFile.second.fileName() : newName); + QString target = QDir(destDir.second.absoluteFilePath()).filePath(fileName); + + if (sourceFile.second == QFileInfo(target)) { + this->callback(ecId, FileError::kInvalidModificationErr); + return; + } + + if (!destDir.second.exists()) { + this->callback(ecId, FileError::kNotFoundErr); + return; + } + if (!destDir.second.isDir()){ + this->callback(ecId, FileError::kInvalidModificationErr); + return; + } + + if (QFileInfo(target).exists()) { + if (!QFile::remove(target)) { + this->callback(ecId, FileError::kInvalidModificationErr); + return; + } + } + + QFile::rename(sourceFile.second.absoluteFilePath(), target); + this->cb(scId, file2map(QFileInfo(target))); +} + +void File::moveDir(int scId, int ecId,const QString& sourceUri, const QString& destinationUri, const QString& newName){ + QPair sourceDir = resolveURI(ecId, sourceUri); + QPair destDir = resolveURI(ecId, destinationUri); + + if (!destDir.first || !sourceDir.first) + return; + if (!checkFileName(newName)) { + this->callback(ecId, FileError::kEncodingErr); + return; + } + + QString fileName = ((newName.isEmpty()) ? sourceDir.second.fileName() : newName); + QString target = QDir(destDir.second.absoluteFilePath()).filePath(fileName); + + if (!destDir.second.exists()){ + this->callback(ecId, FileError::kNotFoundErr); + return; + } + + if (destDir.second.isFile()){ + this->callback(ecId, FileError::kInvalidModificationErr); + return; + } + + // check: copy directory into itself + if (QDir(sourceDir.second.absoluteFilePath()).relativeFilePath(target)[0] != '.'){ + this->callback(ecId, FileError::kInvalidModificationErr); + return; + } + + if (QFileInfo(target).exists() && !QDir(destDir.second.absoluteFilePath()).rmdir(fileName)) { + this->callback(ecId, FileError::kInvalidModificationErr); + return; + } + + if (copyFolder(sourceDir.second.absoluteFilePath(), target)) { + rmDir(sourceDir.second.absoluteFilePath()); + this->cb(scId, file2map(QFileInfo(target))); + } else { + this->callback(ecId, FileError::kNoModificationAllowedErr); + } +} + +void File::moveTo(int scId, int ecId, const QString& source, const QString& destinationDir, const QString& newName) { + QPair f1 = resolveURI(ecId, source); + + if (!f1.first) + return; + + if (f1.second.isDir()) + moveDir(scId, ecId, source, destinationDir, newName); + else + moveFile(scId, ecId, source, destinationDir, newName); +} + +bool File::copyFolder(const QString& sourceFolder, const QString& destFolder) { + QDir sourceDir(sourceFolder); + if (!sourceDir.exists()) + return false; + QDir destDir(destFolder); + if (!destDir.exists()){ + destDir.mkdir(destFolder); + } + QStringList files = sourceDir.entryList(QDir::Files); + for (int i = 0; i< files.count(); i++) + { + QString srcName = sourceFolder + "/" + files[i]; + QString destName = destFolder + "/" + files[i]; + QFile::copy(srcName, destName); + } + files.clear(); + files = sourceDir.entryList(QDir::AllDirs | QDir::NoDotAndDotDot); + for (int i = 0; i< files.count(); i++) + { + QString srcName = sourceFolder + "/" + files[i]; + QString destName = destFolder + "/" + files[i]; + copyFolder(srcName, destName); + } + return true; +}