diff --git a/debian/changelog b/debian/changelog index e019bf1..d1ef476 100644 --- a/debian/changelog +++ b/debian/changelog @@ -2,6 +2,7 @@ libfm-qt (0.13.1-5ubuntu1) UNRELEASED; urgency=medium * Update Vcs-* and maintainer for maintaining by Lubuntu. * Add LXQt Archiver integration. + * Fix failure to open smb:// caused by incorrect file info handling. -- Simon Quigley Fri, 13 Jul 2018 23:59:59 -0500 diff --git a/debian/patches/fix-incorrect-file-info-handling-1.patch b/debian/patches/fix-incorrect-file-info-handling-1.patch new file mode 100644 index 0000000..c4a8ea8 --- /dev/null +++ b/debian/patches/fix-incorrect-file-info-handling-1.patch @@ -0,0 +1,251 @@ +Description: Fix failure to open smb:// caused by incorrect file info handling +Author: "Hong Jen Yee (PCMan)" +Origin: upstream +Applied-Upstream: commit:1a6fa26 +Last-Update: 2018-07-14 +--- a/src/core/basicfilelauncher.cpp ++++ b/src/core/basicfilelauncher.cpp +@@ -27,11 +27,13 @@ bool BasicFileLauncher::launchFiles(cons + FilePathList pathsToLaunch; + // classify files according to different mimetypes + for(auto& fileInfo : fileInfos) { +- // qDebug("path: %s, target: %s", fileInfo->path().toString().get(), fileInfo->target().c_str()); +- if(fileInfo->isDir()) { +- folderInfos.emplace_back(fileInfo); +- } +- else if(fileInfo->isMountable()) { ++ /* ++ qDebug("path: %s, type: %s, target: %s, isDir: %i, isDesktopEntry: %i", ++ fileInfo->path().toString().get(), fileInfo->mimeType()->name(), fileInfo->target().c_str(), ++ fileInfo->isDir(), fileInfo->isDesktopEntry()); ++ */ ++ ++ if(fileInfo->isMountable()) { + if(fileInfo->target().empty()) { + // the mountable is not yet mounted so we have no target URI. + GErrorPtr err{G_IO_ERROR, G_IO_ERROR_NOT_MOUNTED, +@@ -65,6 +67,9 @@ bool BasicFileLauncher::launchFiles(cons + pathsToLaunch.emplace_back(path); + } + } ++ else if(fileInfo->isDir()) { ++ folderInfos.emplace_back(fileInfo); ++ } + else { + auto& mimeType = fileInfo->mimeType(); + mimeTypeToFiles[mimeType->name()].emplace_back(fileInfo); +@@ -101,16 +106,27 @@ bool BasicFileLauncher::launchFiles(cons + bool BasicFileLauncher::launchPaths(FilePathList paths, GAppLaunchContext* ctx) { + // FIXME: blocking with an event loop is not a good design :-( + QEventLoop eventLoop; +- + auto job = new FileInfoJob{paths}; + job->setAutoDelete(false); // do not automatically delete the job since we want its results later. + + GObjectPtr ctxPtr{ctx}; ++ ++ // error handling (for example: handle path not mounted error) ++ QObject::connect(job, &FileInfoJob::error, ++ &eventLoop, [this, job, ctx](const GErrorPtr & err, Job::ErrorSeverity /* severity */ , Job::ErrorAction &act) { ++ auto path = job->currentPath(); ++ if(showError(ctx, err, path, nullptr)) { ++ // the user handled the error and ask for retry ++ act = Job::ErrorAction::RETRY; ++ } ++ }, Qt::BlockingQueuedConnection); // BlockingQueuedConnection is required here to pause the job and wait for user response ++ + QObject::connect(job, &FileInfoJob::finished, + [&eventLoop]() { + // exit the event loop when the job is done + eventLoop.exit(); + }); ++ + // run the job in another thread to not block the UI + job->runAsync(); + +@@ -143,7 +159,7 @@ BasicFileLauncher::ExecAction BasicFileL + return ExecAction::DIRECT_EXEC; + } + +-bool BasicFileLauncher::showError(GAppLaunchContext* /* ctx */, GErrorPtr& /* err */, const FilePath& /* path */, const FileInfoPtr& /* info */) { ++bool BasicFileLauncher::showError(GAppLaunchContext* /* ctx */, const GErrorPtr & /* err */, const FilePath& /* path */, const FileInfoPtr& /* info */) { + return false; + } + +@@ -247,13 +263,21 @@ bool BasicFileLauncher::launchDesktopEnt + + FilePath BasicFileLauncher::handleShortcut(const FileInfoPtr& fileInfo, GAppLaunchContext* ctx) { + auto target = fileInfo->target(); ++ ++ // if we know the target is a dir, we are not going to open it using other apps ++ // for example: `network:///smb-root' is a shortcut targeting `smb:///' and it's also a dir ++ if(fileInfo->isDir()) { ++ return FilePath::fromPathStr(target.c_str()); ++ } ++ + auto scheme = CStrPtr{g_uri_parse_scheme(target.c_str())}; + if(scheme) { + // collect the uri schemes we support + if(strcmp(scheme.get(), "file") == 0 + || strcmp(scheme.get(), "trash") == 0 + || strcmp(scheme.get(), "network") == 0 +- || strcmp(scheme.get(), "computer") == 0) { ++ || strcmp(scheme.get(), "computer") == 0 ++ || strcmp(scheme.get(), "menu") == 0) { + return FilePath::fromUri(fileInfo->target().c_str()); + } + else { +--- a/src/core/basicfilelauncher.h ++++ b/src/core/basicfilelauncher.h +@@ -53,7 +53,7 @@ protected: + + virtual bool openFolder(GAppLaunchContext* ctx, const FileInfoList& folderInfos, GErrorPtr& err); + +- virtual bool showError(GAppLaunchContext* ctx, GErrorPtr& err, const FilePath& path = FilePath{}, const FileInfoPtr& info = FileInfoPtr{}); ++ virtual bool showError(GAppLaunchContext* ctx, const GErrorPtr& err, const FilePath& path = FilePath{}, const FileInfoPtr& info = FileInfoPtr{}); + + virtual ExecAction askExecFile(const FileInfoPtr& file); + +--- a/src/core/fileinfo.cpp ++++ b/src/core/fileinfo.cpp +@@ -36,10 +36,9 @@ void FileInfo::setFromGFileInfo(const GO + size_ = g_file_info_get_size(inf.get()); + + tmp = g_file_info_get_content_type(inf.get()); +- if(!tmp) { +- tmp = "application/octet-stream"; ++ if(tmp) { ++ mimeType_ = MimeType::fromName(tmp); + } +- mimeType_ = MimeType::fromName(tmp); + + mode_ = g_file_info_get_attribute_uint32(inf.get(), G_FILE_ATTRIBUTE_UNIX_MODE); + +@@ -196,6 +195,10 @@ _file_is_symlink: + } + } + ++ if(!mimeType_) { ++ mimeType_ = MimeType::fromName("application/octet-stream"); ++ } ++ + /* if there is a custom folder icon, use it */ + if(isNative() && type == G_FILE_TYPE_DIRECTORY) { + auto local_path = path().localPath(); +--- a/src/core/fileinfojob.cpp ++++ b/src/core/fileinfojob.cpp +@@ -13,31 +13,41 @@ FileInfoJob::FileInfoJob(FilePathList pa + + void FileInfoJob::exec() { + for(const auto& path: paths_) { +- if(!isCancelled()) { ++ if(isCancelled()) { ++ break; ++ } ++ currentPath_ = path; ++ ++ bool retry; ++ do { ++ retry = false; + GErrorPtr err; + GFileInfoPtr inf{ + g_file_query_info(path.gfile().get(), defaultGFileInfoQueryAttribs, + G_FILE_QUERY_INFO_NONE, cancellable().get(), &err), + false + }; +- if(!inf) { +- continue; +- } +- +- // Reuse the same dirPath object when the path remains the same (optimize for files in the same dir) +- auto dirPath = commonDirPath_.isValid() ? commonDirPath_ : path.parent(); +- FileInfo fileInfo(inf, dirPath); ++ if(inf) { ++ // Reuse the same dirPath object when the path remains the same (optimize for files in the same dir) ++ auto dirPath = commonDirPath_.isValid() ? commonDirPath_ : path.parent(); ++ auto fileInfoPtr = std::make_shared(inf, dirPath); ++ ++ // FIXME: this is not elegant ++ if(cutFilesHashSet_ ++ && cutFilesHashSet_->count(path.hash())) { ++ fileInfoPtr->bindCutFiles(cutFilesHashSet_); ++ } + +- if(cutFilesHashSet_ +- && cutFilesHashSet_->count(fileInfo.path().hash())) { +- fileInfo.bindCutFiles(cutFilesHashSet_); ++ results_.push_back(fileInfoPtr); ++ Q_EMIT gotInfo(path, results_.back()); + } +- +- auto fileInfoPtr = std::make_shared(fileInfo); +- +- results_.push_back(fileInfoPtr); +- Q_EMIT gotInfo(path, fileInfoPtr); +- } ++ else { ++ auto act = emitError(err); ++ if(act == Job::ErrorAction::RETRY) { ++ retry = true; ++ } ++ } ++ } while(retry && !isCancelled()); + } + } + +--- a/src/core/fileinfojob.h ++++ b/src/core/fileinfojob.h +@@ -27,6 +27,10 @@ public: + return results_; + } + ++ const FilePath& currentPath() const { ++ return currentPath_; ++ } ++ + Q_SIGNALS: + void gotInfo(const FilePath& path, std::shared_ptr& info); + +@@ -39,6 +43,7 @@ private: + FileInfoList results_; + FilePath commonDirPath_; + const std::shared_ptr cutFilesHashSet_; ++ FilePath currentPath_; + }; + + } // namespace Fm +--- a/src/core/gioptrs.h ++++ b/src/core/gioptrs.h +@@ -112,6 +112,10 @@ public: + return err_; + } + ++ const GError* operator->() const { ++ return err_; ++ } ++ + bool operator == (const GErrorPtr& other) const { + return err_ == other.err_; + } +--- a/src/filelauncher.cpp ++++ b/src/filelauncher.cpp +@@ -76,7 +76,7 @@ bool FileLauncher::openFolder(GAppLaunch + return BasicFileLauncher::openFolder(ctx, folderInfos, err); + } + +-bool FileLauncher::showError(GAppLaunchContext* /*ctx*/, GErrorPtr &err, const FilePath &path, const FileInfoPtr &info) { ++bool FileLauncher::showError(GAppLaunchContext* /*ctx*/, const GErrorPtr &err, const FilePath &path, const FileInfoPtr &info) { + /* ask for mount if trying to launch unmounted path */ + if(err->domain == G_IO_ERROR) { + if(path && err->code == G_IO_ERROR_NOT_MOUNTED) { +--- a/src/filelauncher.h ++++ b/src/filelauncher.h +@@ -43,7 +43,7 @@ protected: + + bool openFolder(GAppLaunchContext* ctx, const FileInfoList& folderInfos, GErrorPtr& err) override; + +- bool showError(GAppLaunchContext* ctx, GErrorPtr& err, const FilePath& path = FilePath{}, const FileInfoPtr& info = FileInfoPtr{}) override; ++ bool showError(GAppLaunchContext* ctx, const GErrorPtr &err, const FilePath& path = FilePath{}, const FileInfoPtr& info = FileInfoPtr{}) override; + + ExecAction askExecFile(const FileInfoPtr& file) override; + diff --git a/debian/patches/fix-incorrect-file-info-handling-2.patch b/debian/patches/fix-incorrect-file-info-handling-2.patch new file mode 100644 index 0000000..27d6069 --- /dev/null +++ b/debian/patches/fix-incorrect-file-info-handling-2.patch @@ -0,0 +1,68 @@ +Description: Correctly handle mountable types +Author: "Hong Jen Yee (PCMan)" +Origin: upstream +Applied-Upstream: commit:dc7a575 +Last-Update: 2018-07-14 +--- a/src/core/basicfilelauncher.cpp ++++ b/src/core/basicfilelauncher.cpp +@@ -28,11 +28,10 @@ bool BasicFileLauncher::launchFiles(cons + // classify files according to different mimetypes + for(auto& fileInfo : fileInfos) { + /* +- qDebug("path: %s, type: %s, target: %s, isDir: %i, isDesktopEntry: %i", ++ qDebug("path: %s, type: %s, target: %s, isDir: %i, isShortcut: %i, isMountable: %i, isDesktopEntry: %i", + fileInfo->path().toString().get(), fileInfo->mimeType()->name(), fileInfo->target().c_str(), +- fileInfo->isDir(), fileInfo->isDesktopEntry()); ++ fileInfo->isDir(), fileInfo->isShortcut(), fileInfo->isMountable(), fileInfo->isDesktopEntry()); + */ +- + if(fileInfo->isMountable()) { + if(fileInfo->target().empty()) { + // the mountable is not yet mounted so we have no target URI. +@@ -267,6 +266,7 @@ FilePath BasicFileLauncher::handleShortc + // if we know the target is a dir, we are not going to open it using other apps + // for example: `network:///smb-root' is a shortcut targeting `smb:///' and it's also a dir + if(fileInfo->isDir()) { ++ qDebug("shortcut is dir: %s", target.c_str()); + return FilePath::fromPathStr(target.c_str()); + } + +--- a/src/core/fileinfo.cpp ++++ b/src/core/fileinfo.cpp +@@ -118,7 +118,8 @@ void FileInfo::setFromGFileInfo(const GO + isDeletable_ = true; + } + +- isShortcut_ = false; ++ isShortcut_ = (type == G_FILE_TYPE_SHORTCUT); ++ isMountable_ = (type == G_FILE_TYPE_MOUNTABLE); + + /* special handling for symlinks */ + if(g_file_info_get_is_symlink(inf.get())) { +@@ -129,7 +130,6 @@ void FileInfo::setFromGFileInfo(const GO + + switch(type) { + case G_FILE_TYPE_SHORTCUT: +- isShortcut_ = true; + /* Falls through. */ + case G_FILE_TYPE_MOUNTABLE: + uri = g_file_info_get_attribute_string(inf.get(), G_FILE_ATTRIBUTE_STANDARD_TARGET_URI); +--- a/src/core/fileinfo.h ++++ b/src/core/fileinfo.h +@@ -151,7 +151,7 @@ public: + } + + bool isMountable() const { +- return mimeType_->isMountable(); ++ return isMountable_; + } + + bool isShortcut() const { +@@ -239,6 +239,7 @@ private: + std::string target_; /* target of shortcut or mountable. */ + + bool isShortcut_ : 1; /* TRUE if file is shortcut type */ ++ bool isMountable_ : 1; /* TRUE if file is mountable type */ + bool isAccessible_ : 1; /* TRUE if can be read by user */ + bool isWritable_ : 1; /* TRUE if can be written to by user */ + bool isDeletable_ : 1; /* TRUE if can be deleted by user */ diff --git a/debian/patches/series b/debian/patches/series index 35164f2..afefa13 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -1 +1,3 @@ add-lxqt-archiver-integration.patch +fix-incorrect-file-info-handling-1.patch +fix-incorrect-file-info-handling-2.patch